diff options
Diffstat (limited to 'daemon/pty_open.c')
-rw-r--r-- | daemon/pty_open.c | 843 |
1 files changed, 0 insertions, 843 deletions
diff --git a/daemon/pty_open.c b/daemon/pty_open.c deleted file mode 100644 index 6f6d83e8..00000000 --- a/daemon/pty_open.c +++ /dev/null @@ -1,843 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Alexander Larsson <alexl@redhat.com> - */ - -/* - * Copyright (C) 2001,2002,2004 Red Hat, Inc. - * - * This is free software; you can redistribute it and/or modify it under - * the terms of the GNU Library General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Originally from vte */ - -#include "config.h" -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <sys/wait.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#ifdef HAVE_SYS_UN_H -#include <sys/un.h> -#endif -#ifdef HAVE_STROPTS_H -#include <stropts.h> -#endif -#ifdef HAVE_TERMIOS_H -#include <termios.h> -#endif -#ifdef HAVE_UTMP_H -#include <utmp.h> -#endif -#ifdef HAVE_UTIL_H -#include <util.h> -#endif -#include <glib.h> -#include "pty_open.h" - -int _pty_set_size(int master, int columns, int rows); - -/* Solaris does not have the login_tty() function so implement locally. */ -#ifndef HAVE_LOGIN_TTY -static int login_tty(int pts) -{ -#if defined(HAVE_STROPTS_H) - /* push a terminal onto stream head */ - if (ioctl(pts, I_PUSH, "ptem") == -1) return -1; - if (ioctl(pts, I_PUSH, "ldterm") == -1) return -1; -#endif - setsid(); -#if defined(TIOCSCTTY) - ioctl(pts, TIOCSCTTY, 0); -#endif - dup2(pts, 0); - dup2(pts, 1); - dup2(pts, 2); - if (pts > 2) close(pts); - return 0; -} -#endif - -/* Reset the handlers for all known signals to their defaults. The parent - * (or one of the libraries it links to) may have changed one to be ignored. */ -static void -_pty_reset_signal_handlers(void) -{ - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGILL, SIG_DFL); - signal(SIGABRT, SIG_DFL); - signal(SIGFPE, SIG_DFL); - signal(SIGKILL, SIG_DFL); - signal(SIGSEGV, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - signal(SIGALRM, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGCONT, SIG_DFL); - signal(SIGSTOP, SIG_DFL); - signal(SIGTSTP, SIG_DFL); - signal(SIGTTIN, SIG_DFL); - signal(SIGTTOU, SIG_DFL); -#ifdef SIGBUS - signal(SIGBUS, SIG_DFL); -#endif -#ifdef SIGPOLL - signal(SIGPOLL, SIG_DFL); -#endif -#ifdef SIGPROF - signal(SIGPROF, SIG_DFL); -#endif -#ifdef SIGSYS - signal(SIGSYS, SIG_DFL); -#endif -#ifdef SIGTRAP - signal(SIGTRAP, SIG_DFL); -#endif -#ifdef SIGURG - signal(SIGURG, SIG_DFL); -#endif -#ifdef SIGVTALARM - signal(SIGVTALARM, SIG_DFL); -#endif -#ifdef SIGXCPU - signal(SIGXCPU, SIG_DFL); -#endif -#ifdef SIGXFSZ - signal(SIGXFSZ, SIG_DFL); -#endif -#ifdef SIGIOT - signal(SIGIOT, SIG_DFL); -#endif -#ifdef SIGEMT - signal(SIGEMT, SIG_DFL); -#endif -#ifdef SIGSTKFLT - signal(SIGSTKFLT, SIG_DFL); -#endif -#ifdef SIGIO - signal(SIGIO, SIG_DFL); -#endif -#ifdef SIGCLD - signal(SIGCLD, SIG_DFL); -#endif -#ifdef SIGPWR - signal(SIGPWR, SIG_DFL); -#endif -#ifdef SIGINFO - signal(SIGINFO, SIG_DFL); -#endif -#ifdef SIGLOST - signal(SIGLOST, SIG_DFL); -#endif -#ifdef SIGWINCH - signal(SIGWINCH, SIG_DFL); -#endif -#ifdef SIGUNUSED - signal(SIGUNUSED, SIG_DFL); -#endif -} - -#ifdef HAVE_SOCKETPAIR -static int -_pty_pipe_open(int *a, int *b) -{ - int p[2], ret = -1; -#ifdef PF_UNIX -#ifdef SOCK_STREAM - ret = socketpair(PF_UNIX, SOCK_STREAM, 0, p); -#else -#ifdef SOCK_DGRAM - ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, p); -#endif -#endif - if (ret == 0) { - *a = p[0]; - *b = p[1]; - return 0; - } -#endif - return ret; -} -#else -static int -_pty_pipe_open(int *a, int *b) -{ - int p[2], ret = -1; - - ret = pipe(p); - - if (ret == 0) { - *a = p[0]; - *b = p[1]; - } - return ret; -} -#endif - -static int -_pty_pipe_open_bi(int *a, int *b, int *c, int *d) -{ - int ret; - ret = _pty_pipe_open(a, b); - if (ret != 0) { - return ret; - } - ret = _pty_pipe_open(c, d); - if (ret != 0) { - close(*a); - close(*b); - } - return ret; -} - -/* Like read, but hide EINTR and EAGAIN. */ -static ssize_t -n_read(int fd, void *buffer, size_t count) -{ - size_t n = 0; - char *buf = buffer; - int i; - while (n < count) { - i = read(fd, buf + n, count - n); - switch (i) { - case 0: - return n; - break; - case -1: - switch (errno) { - case EINTR: - case EAGAIN: -#ifdef ERESTART - case ERESTART: -#endif - break; - default: - return -1; - } - break; - default: - n += i; - break; - } - } - return n; -} - -/* Like write, but hide EINTR and EAGAIN. */ -static ssize_t -n_write(int fd, const void *buffer, size_t count) -{ - size_t n = 0; - const char *buf = buffer; - int i; - while (n < count) { - i = write(fd, buf + n, count - n); - switch (i) { - case 0: - return n; - break; - case -1: - switch (errno) { - case EINTR: - case EAGAIN: -#ifdef ERESTART - case ERESTART: -#endif - break; - default: - return -1; - } - break; - default: - n += i; - break; - } - } - return n; -} - -/* Run the given command (if specified), using the given descriptor as the - * controlling terminal. */ -static int -_pty_run_on_pty(int fd, gboolean login, - int stdin_fd, int stdout_fd, int stderr_fd, - int ready_reader, int ready_writer, - char **env_add, const char *command, char **argv, - const char *directory) -{ - int i; - char c; - char **args, *arg; - -#ifdef HAVE_STROPTS_H - if (!ioctl (fd, I_FIND, "ptem") && ioctl (fd, I_PUSH, "ptem") == -1) { - close (fd); - _exit (0); - return -1; - } - - if (!ioctl (fd, I_FIND, "ldterm") && ioctl (fd, I_PUSH, "ldterm") == -1) { - close (fd); - _exit (0); - return -1; - } - - if (!ioctl (fd, I_FIND, "ttcompat") && ioctl (fd, I_PUSH, "ttcompat") == -1) { - perror ("ioctl (fd, I_PUSH, \"ttcompat\")"); - close (fd); - _exit (0); - return -1; - } -#endif /* HAVE_STROPTS_H */ - - /* Set any environment variables. */ - for (i = 0; (env_add != NULL) && (env_add[i] != NULL); i++) { - if (putenv(g_strdup(env_add[i])) != 0) { - g_warning("Error adding `%s' to environment, " - "continuing.", env_add[i]); - } - } - - /* Reset our signals -- our parent may have done any number of - * weird things to them. */ - _pty_reset_signal_handlers(); - - /* Change to the requested directory. */ - if (directory != NULL) { - chdir(directory); - } - -#ifdef HAVE_UTMP_H - /* This sets stdin, stdout, stderr to the socket */ - if (login && login_tty (fd) == -1) { - g_printerr ("mount child process login_tty failed: %s\n", g_strerror (errno)); - return -1; - } -#endif - - /* Signal to the parent that we've finished setting things up by - * sending an arbitrary byte over the status pipe and waiting for - * a response. This synchronization step ensures that the pty is - * fully initialized before the parent process attempts to do anything - * with it, and is required on systems where additional setup, beyond - * merely opening the device, is required. This is at least the case - * on Solaris. */ - /* Initialize so valgrind doesn't complain */ - c = 0; - n_write(ready_writer, &c, 1); - fsync(ready_writer); - n_read(ready_reader, &c, 1); - close(ready_writer); - if (ready_writer != ready_reader) { - close(ready_reader); - } - - /* If the caller provided a command, we can't go back, ever. */ - if (command != NULL) { - /* Outta here. */ - if (argv != NULL) { - for (i = 0; (argv[i] != NULL); i++) ; - args = g_malloc0(sizeof(char*) * (i + 1)); - for (i = 0; (argv[i] != NULL); i++) { - args[i] = g_strdup(argv[i]); - } - execvp(command, args); - } else { - arg = g_strdup(command); - execlp(command, arg, NULL); - } - - /* Avoid calling any atexit() code. */ - _exit(0); - g_assert_not_reached(); - } - - return 0; -} - -/* Open the named PTY slave, fork off a child (storing its PID in child), - * and exec the named command in its own session as a process group leader */ -static int -_pty_fork_on_pty_name(const char *path, int parent_fd, char **env_add, - const char *command, char **argv, - const char *directory, - int columns, int rows, - int *stdin_fd, int *stdout_fd, int *stderr_fd, - pid_t *child, gboolean reapchild, gboolean login) -{ - int fd, i; - char c; - int ready_a[2] = { 0, 0 }; - int ready_b[2] = { 0, 0 }; - pid_t pid, grandchild_pid; - int pid_pipe[2]; - int stdin_pipe[2]; - int stdout_pipe[2]; - int stderr_pipe[2]; - - /* Open pipes for synchronizing between parent and child. */ - if (_pty_pipe_open_bi(&ready_a[0], &ready_a[1], - &ready_b[0], &ready_b[1]) == -1) { - /* Error setting up pipes. Bail. */ - goto bail_ready; - } - - if (reapchild && pipe(pid_pipe)) { - /* Error setting up pipes. Bail. */ - goto bail_pid; - } - - if (pipe(stdin_pipe)) { - /* Error setting up pipes. Bail. */ - goto bail_stdin; - } - if (pipe(stdout_pipe)) { - /* Error setting up pipes. Bail. */ - goto bail_stdout; - } - if (pipe(stderr_pipe)) { - /* Error setting up pipes. Bail. */ - goto bail_stderr; - } - - /* Start up a child. */ - pid = fork(); - switch (pid) { - case -1: - /* Error fork()ing. Bail. */ - *child = -1; - return -1; - break; - case 0: - /* Child. Close the parent's ends of the pipes. */ - close(ready_a[0]); - close(ready_b[1]); - - close(stdin_pipe[1]); - close(stdout_pipe[0]); - close(stderr_pipe[0]); - - if(reapchild) { - close(pid_pipe[0]); - - /* Fork a intermediate child. This is needed to not - * produce zombies! */ - grandchild_pid = fork(); - - if (grandchild_pid < 0) { - /* Error during fork! */ - n_write (pid_pipe[1], &grandchild_pid, - sizeof (grandchild_pid)); - _exit (1); - } else if (grandchild_pid > 0) { - /* Parent! (This is the actual intermediate child; - * so write the pid to the parent and then exit */ - n_write (pid_pipe[1], &grandchild_pid, - sizeof (grandchild_pid)); - close (pid_pipe[1]); - _exit (0); - } - - /* Start a new session and become process-group leader. */ - setsid(); - setpgid(0, 0); - } - - /* Close most descriptors. */ - for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) { - if ((i != ready_b[0]) && - (i != ready_a[1]) && - (i != stdin_pipe[0]) && - (i != stdout_pipe[1]) && - (i != stderr_pipe[1])) { - close(i); - } - } - - /* Set up stdin/out/err */ - dup2(stdin_pipe[0], STDIN_FILENO); - close (stdin_pipe[0]); - dup2(stdout_pipe[1], STDOUT_FILENO); - close (stdout_pipe[1]); - dup2(stderr_pipe[1], STDERR_FILENO); - close (stderr_pipe[1]); - - /* Open the slave PTY, acquiring it as the controlling terminal - * for this process and its children. */ - fd = open(path, O_RDWR); - if (fd == -1) { - return -1; - } -#ifdef TIOCSCTTY - /* TIOCSCTTY is defined? Let's try that, too. */ - ioctl(fd, TIOCSCTTY, fd); -#endif - /* Store 0 as the "child"'s ID to indicate to the caller that - * it is now the child. */ - *child = 0; - return _pty_run_on_pty(fd, login, - stdin_pipe[1], stdout_pipe[1], stderr_pipe[1], - ready_b[0], ready_a[1], - env_add, command, argv, directory); - break; - default: - /* Parent. Close the child's ends of the pipes, do the ready - * handshake, and return the child's PID. */ - close(ready_b[0]); - close(ready_a[1]); - - close(stdin_pipe[0]); - close(stdout_pipe[1]); - close(stderr_pipe[1]); - - if (reapchild) { - close(pid_pipe[1]); - - /* Reap the intermediate child */ - wait_again: - if (waitpid (pid, NULL, 0) < 0) { - if (errno == EINTR) { - goto wait_again; - } else if (errno == ECHILD) { - ; /* NOOP! Child already reaped. */ - } else { - g_warning ("waitpid() should not fail in pty-open.c"); - } - } - - /* - * Read the child pid from the pid_pipe - * */ - if (n_read (pid_pipe[0], child, sizeof (pid_t)) - != sizeof (pid_t) || *child == -1) { - g_warning ("Error while spanning child!"); - goto bail_fork; - } - - close(pid_pipe[0]); - - } else { - /* No intermediate child, simple */ - *child = pid; - } - - /* Wait for the child to be ready, set the window size, then - * signal that we're ready. We need to synchronize here to - * avoid possible races when the child has to do more setup - * of the terminal than just opening it. */ - n_read(ready_a[0], &c, 1); - _pty_set_size(parent_fd, columns, rows); - n_write(ready_b[1], &c, 1); - close(ready_a[0]); - close(ready_b[1]); - - *stdin_fd = stdin_pipe[1]; - *stdout_fd = stdout_pipe[0]; - *stderr_fd = stderr_pipe[0]; - - return 0; - break; - } - g_assert_not_reached(); - return -1; - - bail_fork: - close(stderr_pipe[0]); - close(stderr_pipe[1]); - bail_stderr: - close(stdout_pipe[0]); - close(stdout_pipe[1]); - bail_stdout: - close(stdin_pipe[0]); - close(stdin_pipe[1]); - bail_stdin: - if(reapchild) { - close(pid_pipe[0]); - close(pid_pipe[1]); - } - bail_pid: - close(ready_a[0]); - close(ready_a[1]); - close(ready_b[0]); - close(ready_b[1]); - bail_ready: - *child = -1; - return -1; -} - -/** - * pty_set_size: - * @master: the file descriptor of the pty master - * @columns: the desired number of columns - * @rows: the desired number of rows - * - * Attempts to resize the pseudo terminal's window size. If successful, the - * OS kernel will send #SIGWINCH to the child process group. - * - * Returns: 0 on success, -1 on failure. - */ -int -_pty_set_size(int master, int columns, int rows) -{ - struct winsize size; - int ret; - memset(&size, 0, sizeof(size)); - size.ws_row = rows ? rows : 24; - size.ws_col = columns ? columns : 80; - ret = ioctl(master, TIOCSWINSZ, &size); - return ret; -} - - -static char * -_pty_ptsname(int master) -{ -#if defined(HAVE_PTSNAME_R) - gsize len = 1024; - char *buf = NULL; - int i; - do { - buf = g_malloc0(len); - i = ptsname_r(master, buf, len - 1); - switch (i) { - case 0: - /* Return the allocated buffer with the name in it. */ - return buf; - break; - default: - g_free(buf); - buf = NULL; - break; - } - len *= 2; - } while ((i != 0) && (errno == ERANGE)); -#elif defined(HAVE_PTSNAME) - char *p; - if ((p = ptsname(master)) != NULL) { - return g_strdup(p); - } -#elif defined(TIOCGPTN) - int pty = 0; - if (ioctl(master, TIOCGPTN, &pty) == 0) { - return g_strdup_printf("/dev/pts/%d", pty); - } -#endif - return NULL; -} - -static int -_pty_getpt(void) -{ - int fd, flags; -#ifdef HAVE_GETPT - /* Call the system's function for allocating a pty. */ - fd = getpt(); -#elif defined(HAVE_POSIX_OPENPT) - fd = posix_openpt(O_RDWR | O_NOCTTY); -#else - /* Try to allocate a pty by accessing the pty master multiplex. */ - fd = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if ((fd == -1) && (errno == ENOENT)) { - fd = open("/dev/ptc", O_RDWR | O_NOCTTY); /* AIX */ - } -#endif - /* Set it to blocking. */ - flags = fcntl(fd, F_GETFL); - flags &= ~(O_NONBLOCK); - fcntl(fd, F_SETFL, flags); - return fd; -} - -static int -_pty_grantpt(int master) -{ -#ifdef HAVE_GRANTPT - return grantpt(master); -#else - return 0; -#endif -} - -static int -_pty_unlockpt(int fd) -{ -#ifdef HAVE_UNLOCKPT - return unlockpt(fd); -#elif defined(TIOCSPTLCK) - int zero = 0; - return ioctl(fd, TIOCSPTLCK, &zero); -#else - return -1; -#endif -} - -static int -_pty_open_unix98(pid_t *child, guint flags, char **env_add, - const char *command, char **argv, - const char *directory, int columns, int rows, - int *stdin_fd, int *stdout_fd, int *stderr_fd) -{ - int fd; - char *buf; - - /* Attempt to open the master. */ - fd = _pty_getpt(); - if (fd != -1) { - /* Read the slave number and unlock it. */ - if (((buf = _pty_ptsname(fd)) == NULL) || - (_pty_grantpt(fd) != 0) || - (_pty_unlockpt(fd) != 0)) { - close(fd); - fd = -1; - } else { - /* Start up a child process with the given command. */ - if (_pty_fork_on_pty_name(buf, fd, env_add, command, - argv, directory, - columns, rows, - stdin_fd, stdout_fd, stderr_fd, - child, - flags & PTY_REAP_CHILD, - flags & PTY_LOGIN_TTY) != 0) { - close(fd); - fd = -1; - } - g_free(buf); - } - } - return fd; -} - -/** - * pty_open: - * @child: location to store the new process's ID - * @env_add: a list of environment variables to add to the child's environment - * @command: name of the binary to run - * @argv: arguments to pass to @command - * @directory: directory to start the new command in, or NULL - * @columns: desired window columns - * @rows: desired window rows - * @lastlog: TRUE if the lastlog should be updated - * @utmp: TRUE if the utmp or utmpx log should be updated - * @wtmp: TRUE if the wtmp or wtmpx log should be updated - * - * Starts a new copy of @command running under a psuedo-terminal, optionally in - * the supplied @directory, with window size set to @rows x @columns - * and variables in @env_add added to its environment. If any combination of - * @lastlog, @utmp, and @wtmp is set, then the session is logged in the - * corresponding system files. - * - * Returns: an open file descriptor for the pty master, -1 on failure - */ -int -pty_open(pid_t *child, guint flags, char **env_add, - const char *command, char **argv, const char *directory, - int columns, int rows, - int *stdin_fd, int *stdout_fd, int *stderr_fd) -{ - int ret = -1; - if (ret == -1) { - ret = _pty_open_unix98(child, flags, env_add, command, - argv, directory, columns, rows, - stdin_fd, stdout_fd, stderr_fd); - } - return ret; -} - - -#ifdef PTY_MAIN -int fd; - -static void -sigchld_handler(int signum) -{ -} - -int -main(int argc, char **argv) -{ - pid_t child = 0; - char c; - int ret; - signal(SIGCHLD, sigchld_handler); - fd = pty_open(&child, 0, NULL, - (argc > 1) ? argv[1] : NULL, - (argc > 1) ? argv + 1 : NULL, - NULL, - 0, 0, - NULL, NULL, NULL); - if (child == 0) { - int i; - for (i = 0; ; i++) { - switch (i % 3) { - case 0: - case 1: - fprintf(stdout, "%d\n", i); - break; - case 2: - fprintf(stderr, "%d\n", i); - break; - default: - g_assert_not_reached(); - break; - } - sleep(1); - } - } - g_print("Child pid is %d.\n", (int)child); - do { - ret = n_read(fd, &c, 1); - if (ret == 0) { - break; - } - if ((ret == -1) && (errno != EAGAIN) && (errno != EINTR)) { - break; - } - if (argc < 2) { - n_write(STDOUT_FILENO, "[", 1); - } - n_write(STDOUT_FILENO, &c, 1); - if (argc < 2) { - n_write(STDOUT_FILENO, "]", 1); - } - } while (TRUE); - return 0; -} -#endif |