Package: policycoreutils
Version: 2.0.44-2
Severity: normal
Tags: patch
File: /usr/sbin/open_init_pty
Hi,
I catch, that se_aptitude eats too much of the CPU time. While tracking
the problem I found open_init_pty has the bug. The select() is called to
watch filedescriptor pty_master even in case with no data in buffer for
write. So loop is running without yielding anything other. The problem
is more visible in virtual machine (XEN domU) - nice() does not help
there.
I started to figure out the problem and finally rewrote a part of
open_init_pty utility. This is my small contribution against global
warming :). A patch of it is greater than utility itself, so the whole
file open_init_pty.c is attached.
The changes:
* Two directions of data transfer
pty_master -> stdout
stdin -> pty_master
are buffered separately using simple ring-buffers.
The loop of watching filedescriptors and read/write on these is
symetric. Watched (select) fds are only those, that can be
handled. E.g. pty_master fd is not watched, if there is no data to
write to it...
* Handling of the SIGCHLD is removed completely. The waitpid() in
NOHANG mode can detect child termination and receives the
exit-status.
* The exit status of child process is passed into open_init_pty's
exit(). I think the open_init_pty is just a wrapper and it should
not return success if a child failed.
* Removed a nice() call.
* Cosmetic changes in perror() calls - the colon is appended
automatically, so colons are removed and messages changed a bit
to some unitary shape.
* Removed a fflush() calles for streams stdout and stderr, because
exit() does this itself (as I checked in libc info manual).
* Changed fileno(stdin) to STDIN_FILENO,...
I suppose the original source file uses tab stop value lower than
standard 8. I added vim mode line at end with tab stop set to 4.
I hope the number of bugs in this change is not to high :). Please
review it.
Best Regards
--
Zito
-- System Information:
Debian Release: lenny/sid
APT prefers unstable
APT policy: (500, 'unstable')
Architecture: i386 (i686)
Kernel: Linux 2.6.18-6-xen-686 (SMP w/1 CPU core)
Locale: LANG=C, LC_CTYPE=cs_CZ.ISO-8859-2 (charmap=ISO-8859-2)
Shell: /bin/sh linked to /bin/bash
Versions of packages policycoreutils depends on:
ii libc6 2.7-10 GNU C Library: Shared libraries
ii libpam0g 0.99.10.0-1~icz50+1 Pluggable Authentication Modules l
ii libselinux1 2.0.59-1 SELinux shared libraries
ii libsemanage1 2.0.24-1 shared libraries used by SELinux p
ii libsepol1 2.0.25-1 Security Enhanced Linux policy lib
ii python-selinux 2.0.59-1 Python bindings to SELinux shared
ii python-semanage 2.0.24-1 Python bindings for SELinux polic
ii python2.5 2.5.2-2 An interactive high-level object-o
ii sepolgen 1.0.11-1 A Python module used in SELinux po
Versions of packages policycoreutils recommends:
pn selinux-policy-refpolicy-targ <none> (no description available)
-- no debconf information
/* -*- Mode: C -*-
* open_init_pty.c ---
* Author : Manoj Srivastava ( [EMAIL PROTECTED] )
* Created On : Fri Jan 14 10:48:28 2005
* Created On Node : glaurung.internal.golden-gryphon.com
* Last Modified By : Manoj Srivastava
* Last Modified On : Thu Sep 15 00:57:00 2005
* Last Machine Used: glaurung.internal.golden-gryphon.com
* Update Count : 92
* Status : Unknown, Use with caution!
* HISTORY :
* Description :
*
* Distributed under the terms of the GNU General Public License v2
*
* open_init_pty
*
* SYNOPSIS:
*
* This program allows a systems administrator to execute daemons
* which need to work in the initrc domain, and which need to have
* pty's as system_u:system_r:initrc_t
*
* USAGE:
*
* * arch-tag: a5583d39-72b9-4cdf-ba1b-5678ea4cbe20
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sysexits.h>
#include <pty.h> /* for openpty and forkpty */
#include <utmp.h> /* for login_tty */
#include <termios.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/wait.h>
#define MAXRETR 3 /* The max number of IO retries on a fd */
#define BUFSIZE 16384 /* The ring buffer size */
static struct termios saved_termios;
static int saved_fd = -1;
static enum { RESET, RAW, CBREAK } tty_state = RESET;
static int tty_semi_raw(int fd)
{
struct termios buf;
if (tty_state == RESET) {
if (tcgetattr(fd, &saved_termios) < 0) {
return -1;
}
}
buf = saved_termios;
/*
* echo off, canonical mode off, extended input processing off,
* signal chars off
*/
buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/*
* no SIGINT on break, CR-to-NL off, input parity check off, do not
* strip 8th bit on input,output flow control off
*/
buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* Clear size bits, parity checking off */
buf.c_cflag &= ~(CSIZE | PARENB);
/* set 8 bits/char */
buf.c_cflag |= CS8;
/* Output processing off
buf.c_oflag &= ~(OPOST); */
buf.c_cc[VMIN] = 1; /* one byte at a time, no timer */
buf.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSANOW, &buf) < 0) {
return -1;
}
tty_state = RAW;
saved_fd = fd;
return 0;
}
void tty_atexit(void)
{
if (tty_state != CBREAK && tty_state != RAW) {
return;
}
if (tcsetattr(saved_fd, TCSANOW, &saved_termios) < 0) {
return;
}
tty_state = RESET;
return;
}
/* The simple ring buffer */
struct ring_buffer
{
char *buf;
char *wptr;
char *rptr;
size_t size;
size_t count;
};
void ringbuf_init(struct ring_buffer *b, char *buf, size_t size)
{
b->buf = b->wptr = b->rptr = buf;
b->size = size;
b->count = 0;
}
int ringbuf_isempty(struct ring_buffer *b)
{
return b->count == 0;
}
size_t ringbuf_space(struct ring_buffer *b)
{
if ( b->rptr > b->wptr )
return b->rptr - b->wptr;
if ( b->rptr < b->wptr || b->count == 0 )
return b->buf + b->size - b->wptr;
return 0;
}
size_t ringbuf_chunk_size(struct ring_buffer *b)
{
if ( b->rptr < b->wptr )
return b->wptr - b->rptr;
if ( b->rptr > b->wptr || b->count > 0 )
return b->buf + b->size - b->rptr;
return 0;
}
ssize_t ringbuf_read(struct ring_buffer *b, int fd)
{
ssize_t n = read(fd, b->wptr, ringbuf_space(b));
if ( n <= 0 )
return n;
b->wptr += n;
b->count += n;
if ( b->buf + b->size <= b->wptr )
b->wptr = b->buf;
return n;
}
ssize_t ringbuf_write(struct ring_buffer *b, int fd)
{
ssize_t n = write(fd, b->rptr, ringbuf_chunk_size(b));
if ( n <= 0 )
return n;
b->rptr += n;
b->count -= n;
if ( b->buf + b->size <= b->rptr )
b->rptr = b->buf;
return n;
}
#define FD_SET_VALUE(fd, fdset, value) \
if (value) \
FD_SET(fd, fdset); \
else \
FD_CLR(fd, fdset);
int main(int argc, char *argv[])
{
pid_t child_pid;
int child_exit_status;
struct termios tty_attr;
struct winsize window_size;
int pty_master;
int retval = 0;
char inbuf_mem[BUFSIZE];
char outbuf_mem[BUFSIZE];
struct ring_buffer inbuf;
struct ring_buffer outbuf;
ringbuf_init(&inbuf, inbuf_mem, sizeof inbuf_mem);
ringbuf_init(&outbuf, outbuf_mem, sizeof outbuf_mem);
if (argc == 1) {
printf("usage: %s PROGRAM [ARGS]...\n", argv[0]);
exit(1);
}
if (isatty(STDIN_FILENO)) {
/* get terminal parameters associated with stdout */
if (tcgetattr(STDOUT_FILENO, &tty_attr) < 0) {
perror("tcgetattr(stdout,...)");
exit(EX_OSERR);
}
/* end of if(tcsetattr(&tty_attr)) */
/* get window size */
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window_size) < 0) {
perror("ioctl(stdout,...)");
exit(1);
}
child_pid = forkpty(&pty_master, NULL, &tty_attr, &window_size);
} /* end of if(isatty(STDIN_FILENO)) */
else { /* not interactive */
child_pid = forkpty(&pty_master, NULL, NULL, NULL);
}
if (child_pid < 0) {
perror("fork()");
exit(EX_OSERR);
} /* end of if(child_pid < 0) */
if (child_pid == 0) {
/* in the child */
struct termios s_tty_attr;
if (tcgetattr(STDIN_FILENO, &s_tty_attr)) {
perror("tcgetattr(stdin,...)");
exit(EXIT_FAILURE);
}
/* Turn off echo */
s_tty_attr.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
/* Also turn of NL to CR?LF on output */
s_tty_attr.c_oflag &= ~(ONLCR);
if (tcsetattr(STDIN_FILENO, TCSANOW, &s_tty_attr)) {
perror("tcsetattr(stdin,...)");
exit(EXIT_FAILURE);
}
if (execvp(argv[1], argv + 1)) {
perror("execvp()");
exit(EXIT_FAILURE);
}
}
/*
* Read current file descriptor flags, preparing to do non blocking reads
*/
retval = fcntl(pty_master, F_GETFL);
if (retval < 0) {
perror("fcntl(pty_master, F_GETFL)");
exit(EX_IOERR);
}
/* Set the connection to be non-blocking */
if (fcntl(pty_master, F_SETFL, retval | O_NONBLOCK) < 0) {
perror("fcntl(pty_master, F_SETFL, ... | O_NONBLOCK)");
exit(1);
}
if (isatty(STDIN_FILENO)) {
if (tty_semi_raw(STDIN_FILENO) < 0) {
perror("tty_semi_raw(stdin)");
}
if (atexit(tty_atexit) < 0) {
perror("atexit()");
}
}
/* for select()... */
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
unsigned err_n_rpty = 0;
unsigned err_n_wpty = 0;
unsigned err_n_stdin = 0;
unsigned err_n_stdout = 0;
int done = 0;
do {
/* Accept events only on fds, that we can handle now. */
FD_SET_VALUE(pty_master, &readfds,
ringbuf_space(&outbuf) > 0 && err_n_rpty < MAXRETR);
FD_SET_VALUE(pty_master, &writefds,
! ringbuf_isempty(&inbuf) && err_n_wpty < MAXRETR);
FD_SET_VALUE(STDIN_FILENO, &readfds,
ringbuf_space(&inbuf) > 0 && err_n_stdin < MAXRETR);
FD_SET_VALUE(STDOUT_FILENO, &writefds,
! ringbuf_isempty(&outbuf) && err_n_stdout < MAXRETR);
int select_rc = select(pty_master + 1,
&readfds, &writefds, &exceptfds, NULL);
if ( select_rc < 0 ) {
perror("select()");
exit(EX_IOERR);
}
#ifdef DEBUG
fprintf(stderr, "select() returned %d\n", select_rc);
#endif
if (FD_ISSET(STDOUT_FILENO, &writefds)) {
#ifdef DEBUG
fprintf(stderr, "stdout can be written\n");
#endif
ssize_t n = ringbuf_write(&outbuf, STDOUT_FILENO);
if ( n <= 0 && n != EINTR && n != EAGAIN )
err_n_stdout++;
#ifdef DEBUG
if ( n >= 0 )
fprintf(stderr, "%d bytes written into stdout\n", n);
else
perror("write(stdout,...)");
#endif
}
if (FD_ISSET(pty_master, &writefds)) {
#ifdef DEBUG
fprintf(stderr, "pty_master can be written\n");
#endif
ssize_t n = ringbuf_write(&inbuf, pty_master);
if ( n <= 0 && n != EINTR && n != EAGAIN )
err_n_wpty++;
#ifdef DEBUG
if ( n >= 0 )
fprintf(stderr, "%d bytes written into pty_master\n", n);
else
perror("write(pty_master,...)");
#endif
}
if (FD_ISSET(STDIN_FILENO, &readfds)) {
#ifdef DEBUG
fprintf(stderr, "stdin can be read\n");
#endif
ssize_t n = ringbuf_read(&inbuf, STDIN_FILENO);
if ( n <= 0 && n != EINTR && n != EAGAIN )
err_n_stdin++;
#ifdef DEBUG
if ( n >= 0 )
fprintf(stderr, "%d bytes read from stdin\n", n);
else
perror("read(stdin,...)");
#endif
}
if (FD_ISSET(pty_master, &readfds)) {
#ifdef DEBUG
fprintf(stderr, "pty_master can be read\n");
#endif
ssize_t n = ringbuf_read(&outbuf, pty_master);
if ( n <= 0 && n != EINTR && n != EAGAIN )
err_n_rpty++;
#ifdef DEBUG
if ( n >= 0 )
fprintf(stderr, "%d bytes read from pty_master\n", n);
else
perror("read(pty_master,...)");
#endif
}
if ( ! done )
if ( waitpid(child_pid, &child_exit_status, WNOHANG) > 0 )
done = 1;
} while ( !done
|| !(ringbuf_isempty(&inbuf) || err_n_wpty >= MAXRETR)
|| !(ringbuf_isempty(&outbuf) || err_n_stdout >= MAXRETR) );
#ifdef DEBUG
fprintf(stderr, "inbuf: %u bytes left, outbuf: %u bytes left\n",
inbuf.count, outbuf.count);
fprintf(stderr, "err_n_rpty=%u, err_n_wpty=%u, "
"err_n_stdin=%u, err_n_stdout=%u\n",
err_n_rpty, err_n_wpty, err_n_stdin, err_n_stdout);
#endif
if ( WIFEXITED(child_exit_status) )
exit(WEXITSTATUS(child_exit_status));
exit(EXIT_FAILURE);
} /* end of main() */
/*
* vim:ts=4:
*/