X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxpty.c;h=39f96f126afd9a347f3c46caf2bd7c8fed914015;hb=095072fa46b2d7b8beafaddb2f873d2f500a1e10;hp=afd64afb8385b49bf56215dab084cbc0b52ee0ea;hpb=f69591412c6d6efe06e32d46ab6f4d9608197d91;p=PuTTY.git diff --git a/unix/uxpty.c b/unix/uxpty.c index afd64afb..39f96f12 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -180,6 +180,8 @@ static struct utmpx utmp_entry; */ char **pty_argv; +char *pty_osx_envrestore_prefix; + static void pty_close(Pty pty); static void pty_try_write(Pty pty); @@ -259,18 +261,19 @@ static void cleanup_utmp(void) } #endif +#ifndef NO_PTY_PRE_INIT static void sigchld_handler(int signum) { if (write(pty_signal_pipe[1], "x", 1) <= 0) /* not much we can do about it */; } +#endif #ifndef OMIT_UTMP static void fatal_sig_handler(int signum) { putty_signal(signum, SIG_DFL); cleanup_utmp(); - setuid(getuid()); raise(signum); } #endif @@ -335,12 +338,39 @@ static void pty_open_master(Pty pty) chown(pty->name, getuid(), gp ? gp->gr_gid : -1); chmod(pty->name, 0600); #else - pty->master_fd = open("/dev/ptmx", O_RDWR); + + const int flags = O_RDWR +#ifdef O_NOCTTY + | O_NOCTTY +#endif + ; + +#ifdef HAVE_POSIX_OPENPT +#ifdef SET_NONBLOCK_VIA_OPENPT + /* + * OS X, as of 10.10 at least, doesn't permit me to set O_NONBLOCK + * on pty master fds via the usual fcntl mechanism. Fortunately, + * it does let me work around this by adding O_NONBLOCK to the + * posix_openpt flags parameter, which isn't a documented use of + * the API but seems to work. So we'll do that for now. + */ + pty->master_fd = posix_openpt(flags | O_NONBLOCK); +#else + pty->master_fd = posix_openpt(flags); +#endif + + if (pty->master_fd < 0) { + perror("posix_openpt"); + exit(1); + } +#else + pty->master_fd = open("/dev/ptmx", flags); if (pty->master_fd < 0) { perror("/dev/ptmx: open"); exit(1); } +#endif if (grantpt(pty->master_fd) < 0) { perror("grantpt"); @@ -358,21 +388,23 @@ static void pty_open_master(Pty pty) strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1); #endif - { - /* - * Set the pty master into non-blocking mode. - */ - int fl; - fl = fcntl(pty->master_fd, F_GETFL); - if (fl != -1 && !(fl & O_NONBLOCK)) - fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK); - } +#ifndef SET_NONBLOCK_VIA_OPENPT + nonblock(pty->master_fd); +#endif if (!ptys_by_fd) ptys_by_fd = newtree234(pty_compare_by_fd); add234(ptys_by_fd, pty); } +static Pty new_pty_struct(void) +{ + Pty pty = snew(struct pty_tag); + pty->conf = NULL; + bufchain_init(&pty->output_data); + return pty; +} + /* * Pre-initialisation. This is here to get around the fact that GTK * doesn't like being run in setuid/setgid programs (probably @@ -388,6 +420,8 @@ static void pty_open_master(Pty pty) */ void pty_pre_init(void) { +#ifndef NO_PTY_PRE_INIT + Pty pty; #ifndef OMIT_UTMP @@ -395,8 +429,7 @@ void pty_pre_init(void) int pipefd[2]; #endif - pty = single_pty = snew(struct pty_tag); - bufchain_init(&pty->output_data); + pty = single_pty = new_pty_struct(); /* set the child signal handler straight away; it needs to be set * before we ever fork. */ @@ -515,13 +548,28 @@ void pty_pre_init(void) int gid = getgid(), uid = getuid(); int setresgid(gid_t, gid_t, gid_t); int setresuid(uid_t, uid_t, uid_t); - setresgid(gid, gid, gid); - setresuid(uid, uid, uid); + if (setresgid(gid, gid, gid) < 0) { + perror("setresgid"); + exit(1); + } + if (setresuid(uid, uid, uid) < 0) { + perror("setresuid"); + exit(1); + } #else - setgid(getgid()); - setuid(getuid()); + if (setgid(getgid()) < 0) { + perror("setgid"); + exit(1); + } + if (setuid(getuid()) < 0) { + perror("setuid"); + exit(1); + } #endif } + +#endif /* NO_PTY_PRE_INIT */ + } int pty_real_select_result(Pty pty, int event, int status) @@ -606,6 +654,7 @@ int pty_real_select_result(Pty pty, int event, int status) if (close_on_exit == FORCE_OFF || (close_on_exit == AUTO && pty->exit_code != 0)) { char message[512]; + message[0] = '\0'; if (WIFEXITED(pty->exit_code)) sprintf(message, "\r\n[pterm: process terminated with exit" " code %d]\r\n", WEXITSTATUS(pty->exit_code)); @@ -685,8 +734,8 @@ static void pty_uxsel_setup(Pty pty) * freed by the caller. */ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, - char *host, int port, char **realhost, int nodelay, - int keepalive) + const char *host, int port, char **realhost, + int nodelay, int keepalive) { int slavefd; pid_t pid, pgrp; @@ -697,8 +746,9 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, if (single_pty) { pty = single_pty; + assert(pty->conf == NULL); } else { - pty = snew(struct pty_tag); + pty = new_pty_struct(); pty->master_fd = pty->slave_fd = -1; #ifndef OMIT_UTMP pty_stamped_utmp = FALSE; @@ -715,38 +765,28 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, if (pty->master_fd < 0) pty_open_master(pty); - /* - * Set the backspace character to be whichever of ^H and ^? is - * specified by bksp_is_delete. - */ - { - struct termios attrs; - tcgetattr(pty->master_fd, &attrs); - attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete) - ? '\177' : '\010'; - tcsetattr(pty->master_fd, TCSANOW, &attrs); - } - #ifndef OMIT_UTMP /* * Stamp utmp (that is, tell the utmp helper process to do so), * or not. */ - if (!conf_get_int(conf, CONF_stamp_utmp)) { - close(pty_utmp_helper_pipe); /* just let the child process die */ - pty_utmp_helper_pipe = -1; - } else if (pty_utmp_helper_pipe >= 0) { - char *location = get_x_display(pty->frontend); - int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ - while (pos < len) { - int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); - if (ret < 0) { - perror("pterm: writing to utmp helper process"); - close(pty_utmp_helper_pipe); /* arrgh, just give up */ - pty_utmp_helper_pipe = -1; - break; - } - pos += ret; + if (pty_utmp_helper_pipe >= 0) { /* if it's < 0, we can't anyway */ + if (!conf_get_int(conf, CONF_stamp_utmp)) { + close(pty_utmp_helper_pipe); /* just let the child process die */ + pty_utmp_helper_pipe = -1; + } else { + const char *location = get_x_display(pty->frontend); + int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ + while (pos < len) { + int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); + if (ret < 0) { + perror("pterm: writing to utmp helper process"); + close(pty_utmp_helper_pipe); /* arrgh, just give up */ + pty_utmp_helper_pipe = -1; + break; + } + pos += ret; + } } } #endif @@ -765,10 +805,40 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, } if (pid == 0) { + struct termios attrs; + /* * We are the child. */ + if (pty_osx_envrestore_prefix) { + int plen = strlen(pty_osx_envrestore_prefix); + extern char **environ; + char **ep; + + restart_osx_env_restore: + for (ep = environ; *ep; ep++) { + char *e = *ep; + + if (!strncmp(e, pty_osx_envrestore_prefix, plen)) { + int unset = (e[plen] == 'u'); + char *pname = dupprintf("%.*s", (int)strcspn(e, "="), e); + char *name = pname + plen + 1; + char *value = e + strcspn(e, "="); + if (*value) value++; + value = dupstr(value); + if (unset) + unsetenv(name); + else + setenv(name, value, 1); + unsetenv(pname); + sfree(pname); + sfree(value); + goto restart_osx_env_restore; + } + } + } + slavefd = pty_open_slave(pty); if (slavefd < 0) { perror("slave pty: open"); @@ -776,7 +846,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, } close(pty->master_fd); - fcntl(slavefd, F_SETFD, 0); /* don't close on exec */ + noncloexec(slavefd); dup2(slavefd, 0); dup2(slavefd, 1); dup2(slavefd, 2); @@ -787,8 +857,40 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, #endif pgrp = getpid(); tcsetpgrp(0, pgrp); + + /* + * Set up configuration-dependent termios settings on the new + * pty. Linux would have let us do this on the pty master + * before we forked, but that fails on OS X, so we do it here + * instead. + */ + if (tcgetattr(0, &attrs) == 0) { + /* + * Set the backspace character to be whichever of ^H and + * ^? is specified by bksp_is_delete. + */ + attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete) + ? '\177' : '\010'; + + /* + * Set the IUTF8 bit iff the character set is UTF-8. + */ +#ifdef IUTF8 + if (frontend_is_utf8(frontend)) + attrs.c_iflag |= IUTF8; + else + attrs.c_iflag &= ~IUTF8; +#endif + + tcsetattr(0, TCSANOW, &attrs); + } + setpgid(pgrp, pgrp); - close(open(pty->name, O_WRONLY, 0)); + { + int ptyfd = open(pty->name, O_WRONLY, 0); + if (ptyfd >= 0) + close(ptyfd); + } setpgid(pgrp, pgrp); { char *term_env_var = dupprintf("TERM=%s", @@ -806,6 +908,19 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, * environment in place. */ } + { + /* + * In case we were invoked with a --display argument that + * doesn't match DISPLAY in our actual environment, we + * should set DISPLAY for processes running inside the + * terminal to match the display the terminal itself is + * on. + */ + const char *x_display = get_x_display(pty->frontend); + char *x_display_env_var = dupprintf("DISPLAY=%s", x_display); + putenv(x_display_env_var); + /* As above, we don't free this. */ + } #endif { char *key, *val; @@ -827,17 +942,49 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, /* * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by * our parent, particularly by things like sh -c 'pterm &' and - * some window or session managers. SIGCHLD, meanwhile, was - * blocked during pt_main() startup. Reverse all this for our - * child process. + * some window or session managers. SIGPIPE was also + * (potentially) blocked by us during startup. Reverse all + * this for our child process. */ putty_signal(SIGINT, SIG_DFL); putty_signal(SIGQUIT, SIG_DFL); putty_signal(SIGPIPE, SIG_DFL); - block_signal(SIGCHLD, 0); - if (pty_argv) + block_signal(SIGPIPE, 0); + if (pty_argv) { + /* + * Exec the exact argument list we were given. + */ execvp(pty_argv[0], pty_argv); - else { + /* + * If that fails, and if we had exactly one argument, pass + * that argument to $SHELL -c. + * + * This arranges that we can _either_ follow 'pterm -e' + * with a list of argv elements to be fed directly to + * exec, _or_ with a single argument containing a command + * to be parsed by a shell (but, in cases of doubt, the + * former is more reliable). + * + * A quick survey of other terminal emulators' -e options + * (as of Debian squeeze) suggests that: + * + * - xterm supports both modes, more or less like this + * - gnome-terminal will only accept a one-string shell command + * - Eterm, kterm and rxvt will only accept a list of + * argv elements (as did older versions of pterm). + * + * It therefore seems important to support both usage + * modes in order to be a drop-in replacement for either + * xterm or gnome-terminal, and hence for anyone's + * plausible uses of the Debian-style alias + * 'x-terminal-emulator'... + */ + if (pty_argv[1] == NULL) { + char *shell = getenv("SHELL"); + if (shell) + execl(shell, shell, "-c", pty_argv[0], (void *)NULL); + } + } else { char *shell = getenv("SHELL"); char *shellname; if (conf_get_int(conf, CONF_login_shell)) { @@ -878,7 +1025,7 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf, *backend_handle = pty; - *realhost = dupprintf("\0"); + *realhost = dupstr(""); return NULL; } @@ -905,7 +1052,19 @@ static void pty_free(void *handle) del234(ptys_by_pid, pty); del234(ptys_by_fd, pty); - sfree(pty); + bufchain_clear(&pty->output_data); + + conf_free(pty->conf); + pty->conf = NULL; + + if (pty == single_pty) { + /* + * Leave this structure around in case we need to Restart + * Session. + */ + } else { + sfree(pty); + } } static void pty_try_write(Pty pty) @@ -938,7 +1097,7 @@ static void pty_try_write(Pty pty) /* * Called to send data down the pty. */ -static int pty_send(void *handle, char *buf, int len) +static int pty_send(void *handle, const char *buf, int len) { Pty pty = (Pty)handle; @@ -1089,6 +1248,7 @@ Backend pty_backend = { pty_provide_logctx, pty_unthrottle, pty_cfg_info, + NULL /* test_for_upstream */, "pty", -1, 0