[PATCH 2/3] Add new open_init_pty that doesn't waste CPU time

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Russell Coker <russell@xxxxxxxxxxxx>

---
 policycoreutils/run_init/Makefile          |   11 +-
 policycoreutils/run_init/open_init_pty.cpp |  423 ++++++++++++++++++++++++++++
 2 files changed, 429 insertions(+), 5 deletions(-)
 create mode 100644 policycoreutils/run_init/open_init_pty.cpp

diff --git a/policycoreutils/run_init/Makefile b/policycoreutils/run_init/Makefile
index 12b39b4..f3c5e0a 100644
--- a/policycoreutils/run_init/Makefile
+++ b/policycoreutils/run_init/Makefile
@@ -8,8 +8,9 @@ LOCALEDIR ?= /usr/share/locale
 PAMH = $(shell ls /usr/include/security/pam_appl.h 2>/dev/null)
 AUDITH = $(shell ls /usr/include/libaudit.h 2>/dev/null)
 
-CFLAGS ?= -Werror -Wall -W
+CFLAGS ?= -Werror -Wall -W -g -O2
 override CFLAGS += -I$(PREFIX)/include -DUSE_NLS -DLOCALEDIR="\"$(LOCALEDIR)\"" -DPACKAGE="\"policycoreutils\""
+CPPFLAGS ?= $(CFLAGS)
 LDLIBS += -lselinux -L$(PREFIX)/lib
 ifeq ($(PAMH), /usr/include/security/pam_appl.h)
 	override CFLAGS += -DUSE_PAM
@@ -23,17 +24,17 @@ ifeq ($(AUDITH), /usr/include/libaudit.h)
 	LDLIBS += -laudit
 endif
 
-TARGETS=$(patsubst %.c,%,$(wildcard *.c))
+TARGETS=open_init_pty run_init
 
 all: $(TARGETS)
 
-open_init_pty: open_init_pty.c
-	$(LINK.c) $^ -ldl -lutil -o $@
+open_init_pty: open_init_pty.cpp
+	$(LINK.cpp) $^ -ldl -lutil -o $@
 
 
 install: all
 	test -d $(SBINDIR)      || install -m 755 -d $(SBINDIR)
-	test -d $(MANDIR)/man1 || install -m 755 -d $(MANDIR)/man1
+	test -d $(MANDIR)/man8 || install -m 755 -d $(MANDIR)/man8
 	install -m 755 run_init $(SBINDIR)
 	install -m 755 open_init_pty $(SBINDIR)
 	install -m 644 run_init.8 $(MANDIR)/man8/
diff --git a/policycoreutils/run_init/open_init_pty.cpp b/policycoreutils/run_init/open_init_pty.cpp
new file mode 100644
index 0000000..c892b20
--- /dev/null
+++ b/policycoreutils/run_init/open_init_pty.cpp
@@ -0,0 +1,423 @@
+/*                               -*- Mode: C -*-
+ * open_init_pty.c ---
+ * Author           : Manoj Srivastava ( srivasta@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx )
+ * 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 <string.h>
+#include <unistd.h>
+#include <signal.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 2048  /* 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;
+}
+
+static 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 */
+class ring_buffer
+{
+public:
+  ring_buffer(char *buf, size_t size)
+  {
+    m_buf = m_wptr = m_rptr = buf;
+    m_size = size;
+    m_count = 0;
+  }
+
+  size_t get_count() { return m_count; }
+  int isempty() { return m_count == 0; }
+
+  // return the unused space size in the buffer
+  size_t space()
+  {
+    if(m_rptr > m_wptr)
+      return m_rptr - m_wptr;
+    if(m_rptr < m_wptr || m_count == 0)
+      return m_buf + m_size - m_wptr;
+    return 0; // should not hit this
+  }
+
+  // return the used space in the buffer
+  size_t chunk_size()
+  {
+    if(m_rptr < m_wptr)
+      return m_wptr - m_rptr;
+    if(m_rptr > m_wptr || m_count > 0)
+      return m_buf + m_size - m_rptr;
+    return 0; // should not hit this
+  }
+
+  // read from fd and write to buffer memory
+  ssize_t rb_read(int fd)
+  {
+    ssize_t n = read(fd, m_wptr, space());
+    if(n <= 0)
+      return n;
+    m_wptr += n;
+    m_count += n;
+    if(m_buf + m_size <= m_wptr)
+      m_wptr = m_buf;
+    return n;
+  }
+
+  ssize_t rb_write(int fd)
+  {
+    ssize_t n = write(fd, m_rptr, chunk_size());
+    if(n <= 0)
+      return n;
+    m_rptr += n;
+    m_count -= n;
+    if(m_buf + m_size <= m_rptr)
+      m_rptr = m_buf;
+    return n;
+  }
+
+private:
+    char *m_buf; /* pointer to buffer memory */
+    char *m_wptr;
+    char *m_rptr;
+    size_t m_size; /* the number of bytes allocated for buf */
+    size_t m_count;
+};
+
+static void setfd_nonblock(int fd)
+{
+  int fsflags = fcntl(fd, F_GETFL);
+
+  if (fsflags < 0) {
+    fprintf(stderr, "fcntl(%d, F_GETFL): %s\n",
+        fd, strerror(errno));
+    exit(EX_IOERR);
+  }
+
+  if (fcntl(STDIN_FILENO, F_SETFL, fsflags | O_NONBLOCK) < 0) {
+    fprintf(stderr, "fcntl(%d, F_SETFL, ... | O_NONBLOCK): %s\n",
+        fd, strerror(errno));
+    exit(EX_IOERR);
+  }
+}
+
+static void sigchld_handler(int asig __attribute__ ((unused)))
+{
+}
+
+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;
+  char inbuf_mem[BUFSIZE];
+  char outbuf_mem[BUFSIZE];
+  ring_buffer inbuf(inbuf_mem, sizeof(inbuf_mem));
+  ring_buffer outbuf(outbuf_mem, sizeof(outbuf_mem));
+
+  if (argc == 1) {
+    printf("usage: %s PROGRAM [ARGS]...\n", argv[0]);
+    exit(1);
+  }
+
+  /* Wee need I/O calls to fail with EINTR on SIGCHLD... */
+  if ( signal(SIGCHLD, sigchld_handler) == SIG_ERR ) {
+    perror("signal(SIGCHLD,...)");
+    exit(EX_OSERR);
+  }
+
+  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("forkpty()");
+    fflush(stdout);
+    fflush(stderr);
+    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);
+    }
+  }
+
+  /*
+   * Non blocking mode for all file descriptors.
+   */
+  setfd_nonblock(pty_master);
+  setfd_nonblock(STDIN_FILENO);
+  setfd_nonblock(STDOUT_FILENO);
+
+  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. */
+    int do_select = 0;
+
+    if ( outbuf.space() > 0  &&  err_n_rpty < MAXRETR ) {
+      FD_SET(pty_master, &readfds);
+      do_select = 1;
+    } else
+      FD_CLR(pty_master, &readfds);
+
+    if ( ! inbuf.isempty()  &&  err_n_wpty < MAXRETR ) {
+      FD_SET(pty_master, &writefds);
+      do_select = 1;
+    } else
+      FD_CLR(pty_master, &writefds);
+ 
+    if ( inbuf.space() > 0  &&  err_n_stdin < MAXRETR ) {
+      FD_SET(STDIN_FILENO, &readfds);
+      do_select = 1;
+    } else
+      FD_CLR(STDIN_FILENO, &readfds);
+
+    if ( ! outbuf.isempty()  &&  err_n_stdout < MAXRETR ) {
+      FD_SET(STDOUT_FILENO, &writefds);
+      do_select = 1;
+    } else
+      FD_CLR(STDOUT_FILENO, &writefds);
+
+    if ( ! do_select )
+    {
+#ifdef DEBUG
+      fprintf(stderr, "No I/O job for us, calling waitpid()...\n");
+#endif
+        while ( waitpid(child_pid, &child_exit_status, 0) < 0 )
+        ;
+      break;
+    }
+
+    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 = outbuf.rb_write(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 = inbuf.rb_write(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 = inbuf.rb_read(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 = outbuf.rb_read(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
+      || !(inbuf.isempty() || err_n_wpty >= MAXRETR)
+        || !(outbuf.isempty() || err_n_stdout >= MAXRETR) );
+
+#ifdef DEBUG
+  fprintf(stderr, "inbuf: %u bytes left, outbuf: %u bytes left\n",
+      inbuf.get_count(), outbuf.get_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:
+ */
-- 
1.7.10


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.


[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux