X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/d37c2d8198de28cc4076c560dff9a94f8452fe7c..39baeaa47a957eefe6eb00296ff8b753e3bf4868:/unix/pty.c diff --git a/unix/pty.c b/unix/pty.c index 8a3d06be..2a7abb68 100644 --- a/unix/pty.c +++ b/unix/pty.c @@ -6,8 +6,15 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include "putty.h" @@ -19,11 +26,152 @@ #define TRUE 1 #endif +#ifndef UTMP_FILE +#define UTMP_FILE "/var/run/utmp" +#endif +#ifndef WTMP_FILE +#define WTMP_FILE "/var/log/wtmp" +#endif +#ifndef LASTLOG_FILE +#ifdef _PATH_LASTLOG +#define LASTLOG_FILE _PATH_LASTLOG +#else +#define LASTLOG_FILE "/var/log/lastlog" +#endif +#endif + +/* + * Set up a default for vaguely sane systems. The idea is that if + * OMIT_UTMP is not defined, then at least one of the symbols which + * enable particular forms of utmp processing should be, if only so + * that a link error can warn you that you should have defined + * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is + * the only such symbol. + */ +#ifndef OMIT_UTMP +#if !defined HAVE_PUTUTLINE +#define HAVE_PUTUTLINE +#endif +#endif + int pty_master_fd; +static int pty_stamped_utmp = 0; +static int pty_child_pid; +static sig_atomic_t pty_child_dead; +#ifndef OMIT_UTMP +static struct utmp utmp_entry; +#endif char **pty_argv; +int pty_child_is_dead(void) +{ + return pty_child_dead; +} + static void pty_size(void); +static void setup_utmp(char *ttyname) +{ +#ifndef OMIT_UTMP +#ifdef HAVE_LASTLOG + struct lastlog lastlog_entry; + FILE *lastlog; +#endif + struct passwd *pw; + char *location; + FILE *wtmp; + + if (!cfg.stamp_utmp) + return; + + pw = getpwuid(getuid()); + location = get_x_display(); + memset(&utmp_entry, 0, sizeof(utmp_entry)); + utmp_entry.ut_type = USER_PROCESS; + utmp_entry.ut_pid = getpid(); + strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line)); + strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id)); + strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user)); + strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host)); + time(&utmp_entry.ut_time); + +#if defined HAVE_PUTUTLINE + utmpname(UTMP_FILE); + setutent(); + pututline(&utmp_entry); + endutent(); +#endif + + if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) { + fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp); + fclose(wtmp); + } + +#ifdef HAVE_LASTLOG + memset(&lastlog_entry, 0, sizeof(lastlog_entry)); + strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line)); + strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host)); + time(&lastlog_entry.ll_time); + if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) { + fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET); + fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog); + fclose(lastlog); + } +#endif + + pty_stamped_utmp = 1; + +#endif +} + +static void cleanup_utmp(void) +{ +#ifndef OMIT_UTMP + FILE *wtmp; + + if (!cfg.stamp_utmp || !pty_stamped_utmp) + return; + + utmp_entry.ut_type = DEAD_PROCESS; + memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user)); + time(&utmp_entry.ut_time); + + if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) { + fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp); + fclose(wtmp); + } + + memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line)); + utmp_entry.ut_time = 0; + +#if defined HAVE_PUTUTLINE + utmpname(UTMP_FILE); + setutent(); + pututline(&utmp_entry); + endutent(); +#endif + + pty_stamped_utmp = 0; /* ensure we never double-cleanup */ +#endif +} + +static void sigchld_handler(int signum) +{ + pid_t pid; + int status; + pid = waitpid(-1, &status, WNOHANG); + if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status))) + pty_child_dead = TRUE; +} + +static void fatal_sig_handler(int signum) +{ + signal(signum, SIG_DFL); + cleanup_utmp(); + setuid(getuid()); + raise(signum); +} + /* * Called to set up the pty. * @@ -38,6 +186,34 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) char name[FILENAME_MAX]; pid_t pid, pgrp; +#ifdef BSD_PTYS + { + const char chars1[] = "pqrstuvwxyz"; + const char chars2[] = "0123456789abcdef"; + const char *p1, *p2; + char master_name[20]; + + for (p1 = chars1; *p1; p1++) + for (p2 = chars2; *p2; p2++) { + sprintf(master_name, "/dev/pty%c%c", *p1, *p2); + pty_master_fd = open(master_name, O_RDWR); + if (pty_master_fd >= 0) { + if (geteuid() == 0 || + access(master_name, R_OK | W_OK) == 0) + goto got_one; + close(pty_master_fd); + } + } + + /* If we get here, we couldn't get a tty at all. */ + fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n"); + exit(1); + + got_one: + strcpy(name, master_name); + name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */ + } +#else pty_master_fd = open("/dev/ptmx", O_RDWR); if (pty_master_fd < 0) { @@ -57,6 +233,65 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) name[FILENAME_MAX-1] = '\0'; strncpy(name, ptsname(pty_master_fd), FILENAME_MAX-1); +#endif + + /* + * 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] = cfg.bksp_is_delete ? '\177' : '\010'; + tcsetattr(pty_master_fd, TCSANOW, &attrs); + } + + /* + * Trap as many fatal signals as we can in the hope of having + * the best chance to clean up utmp before termination. + */ + signal(SIGHUP, fatal_sig_handler); + signal(SIGINT, fatal_sig_handler); + signal(SIGQUIT, fatal_sig_handler); + signal(SIGILL, fatal_sig_handler); + signal(SIGABRT, fatal_sig_handler); + signal(SIGFPE, fatal_sig_handler); + signal(SIGPIPE, fatal_sig_handler); + signal(SIGALRM, fatal_sig_handler); + signal(SIGTERM, fatal_sig_handler); + signal(SIGSEGV, fatal_sig_handler); + signal(SIGUSR1, fatal_sig_handler); + signal(SIGUSR2, fatal_sig_handler); +#ifdef SIGBUS + signal(SIGBUS, fatal_sig_handler); +#endif +#ifdef SIGPOLL + signal(SIGPOLL, fatal_sig_handler); +#endif +#ifdef SIGPROF + signal(SIGPROF, fatal_sig_handler); +#endif +#ifdef SIGSYS + signal(SIGSYS, fatal_sig_handler); +#endif +#ifdef SIGTRAP + signal(SIGTRAP, fatal_sig_handler); +#endif +#ifdef SIGVTALRM + signal(SIGVTALRM, fatal_sig_handler); +#endif +#ifdef SIGXCPU + signal(SIGXCPU, fatal_sig_handler); +#endif +#ifdef SIGXFSZ + signal(SIGXFSZ, fatal_sig_handler); +#endif +#ifdef SIGIO + signal(SIGIO, fatal_sig_handler); +#endif + /* Also clean up utmp on normal exit. */ + atexit(cleanup_utmp); + setup_utmp(name); /* * Fork and execute the command. @@ -79,6 +314,15 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) exit(1); } +#ifdef BSD_PTYS + /* We need to chown/chmod the /dev/ttyXX device. */ + { + struct group *gp = getgrnam("tty"); + fchown(slavefd, getuid(), gp ? gp->gr_gid : -1); + fchmod(slavefd, 0600); + } +#endif + close(pty_master_fd); close(0); close(1); @@ -94,6 +338,9 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) setpgrp(); close(open(name, O_WRONLY, 0)); setpgrp(); + /* In case we were setgid-utmp or setuid-root, drop privs. */ + setgid(getgid()); + setuid(getuid()); /* Close everything _else_, for tidiness. */ for (i = 3; i < 1024; i++) close(i); @@ -104,8 +351,19 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) } if (pty_argv) execvp(pty_argv[0], pty_argv); - else - execl(getenv("SHELL"), getenv("SHELL"), NULL); + else { + char *shell = getenv("SHELL"); + char *shellname; + if (cfg.login_shell) { + char *p = strrchr(shell, '/'); + shellname = smalloc(2+strlen(shell)); + p = p ? p+1 : shell; + sprintf(shellname, "-%s", p); + } else + shellname = shell; + execl(getenv("SHELL"), shellname, NULL); + } + /* * If we're here, exec has gone badly foom. */ @@ -113,6 +371,9 @@ static char *pty_init(char *host, int port, char **realhost, int nodelay) exit(127); } else { close(slavefd); + pty_child_pid = pid; + pty_child_dead = FALSE; + signal(SIGCHLD, sigchld_handler); } return NULL; @@ -152,6 +413,8 @@ static void pty_size(void) size.ws_row = (unsigned short)rows; size.ws_col = (unsigned short)cols; + size.ws_xpixel = (unsigned short) cols * font_dimension(0); + size.ws_ypixel = (unsigned short) rows * font_dimension(1); ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size); return; }