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: */