1 /*
2  * Pseudo-tty backend for pterm.
3  */
4 
5 #define _GNU_SOURCE
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <signal.h>
12 #include <assert.h>
13 #include <fcntl.h>
14 #include <termios.h>
15 #include <grp.h>
16 #include <pwd.h>
17 #include <time.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/wait.h>
21 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <termios.h>
24 
25 #include "putty.h"
26 #include "ssh.h"
27 #include "sshserver.h" /* to check the prototypes of server-needed things */
28 #include "tree234.h"
29 
30 #ifndef OMIT_UTMP
31 #include <utmpx.h>
32 #endif
33 
34 /* updwtmpx() needs the name of the wtmp file.  Try to find it. */
35 #ifndef WTMPX_FILE
36 #ifdef _PATH_WTMPX
37 #define WTMPX_FILE _PATH_WTMPX
38 #else
39 #define WTMPX_FILE "/var/log/wtmpx"
40 #endif
41 #endif
42 
43 #ifndef LASTLOG_FILE
44 #ifdef _PATH_LASTLOG
45 #define LASTLOG_FILE _PATH_LASTLOG
46 #else
47 #define LASTLOG_FILE "/var/log/lastlog"
48 #endif
49 #endif
50 
51 /*
52  * Set up a default for vaguely sane systems. The idea is that if
53  * OMIT_UTMP is not defined, then at least one of the symbols which
54  * enable particular forms of utmp processing should be, if only so
55  * that a link error can warn you that you should have defined
56  * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is
57  * the only such symbol.
58  */
59 #ifndef OMIT_UTMP
60 #if !defined HAVE_PUTUTLINE
61 #define HAVE_PUTUTLINE
62 #endif
63 #endif
64 
65 typedef struct Pty Pty;
66 
67 /*
68  * The pty_signal_pipe, along with the SIGCHLD handler, must be
69  * process-global rather than session-specific.
70  */
71 static int pty_signal_pipe[2] = { -1, -1 };   /* obviously bogus initial val */
72 
73 typedef struct PtyFd {
74     int fd;
75     Pty *pty;
76 } PtyFd;
77 
78 struct Pty {
79     Conf *conf;
80 
81     int master_fd, slave_fd;
82     int pipefds[6];
83     PtyFd fds[3];
84     int master_i, master_o, master_e;
85 
86     Seat *seat;
87     char name[FILENAME_MAX];
88     pid_t child_pid;
89     int term_width, term_height;
90     bool child_dead, finished;
91     int exit_code;
92     bufchain output_data;
93     bool pending_eof;
94     Backend backend;
95 };
96 
97 /*
98  * We store all the (active) PtyFd structures in a tree sorted by fd,
99  * so that when we get an uxsel notification we know which backend
100  * instance is the owner of the pty that caused it, and then we can
101  * find out which fd is the relevant one too.
102  */
ptyfd_compare(void * av,void * bv)103 static int ptyfd_compare(void *av, void *bv)
104 {
105     PtyFd *a = (PtyFd *)av;
106     PtyFd *b = (PtyFd *)bv;
107 
108     if (a->fd < b->fd)
109         return -1;
110     else if (a->fd > b->fd)
111         return +1;
112     return 0;
113 }
114 
ptyfd_find(void * av,void * bv)115 static int ptyfd_find(void *av, void *bv)
116 {
117     int a = *(int *)av;
118     PtyFd *b = (PtyFd *)bv;
119 
120     if (a < b->fd)
121         return -1;
122     else if (a > b->fd)
123         return +1;
124     return 0;
125 }
126 
127 static tree234 *ptyfds = NULL;
128 
129 /*
130  * We also have a tree of Pty structures themselves, sorted by child
131  * pid, so that when we wait() in response to the signal we know which
132  * backend instance is the owner of the process that caused the
133  * signal.
134  */
pty_compare_by_pid(void * av,void * bv)135 static int pty_compare_by_pid(void *av, void *bv)
136 {
137     Pty *a = (Pty *)av;
138     Pty *b = (Pty *)bv;
139 
140     if (a->child_pid < b->child_pid)
141         return -1;
142     else if (a->child_pid > b->child_pid)
143         return +1;
144     return 0;
145 }
146 
pty_find_by_pid(void * av,void * bv)147 static int pty_find_by_pid(void *av, void *bv)
148 {
149     pid_t a = *(pid_t *)av;
150     Pty *b = (Pty *)bv;
151 
152     if (a < b->child_pid)
153         return -1;
154     else if (a > b->child_pid)
155         return +1;
156     return 0;
157 }
158 
159 static tree234 *ptys_by_pid = NULL;
160 
161 /*
162  * If we are using pty_pre_init(), it will need to have already
163  * allocated a pty structure, which we must then return from
164  * pty_init() rather than allocating a new one. Here we store that
165  * structure between allocation and use.
166  *
167  * Note that although most of this module is entirely capable of
168  * handling multiple ptys in a single process, pty_pre_init() is
169  * fundamentally _dependent_ on there being at most one pty per
170  * process, so the normal static-data constraints don't apply.
171  *
172  * Likewise, since utmp is only used via pty_pre_init, it too must
173  * be single-instance, so we can declare utmp-related variables
174  * here.
175  */
176 static Pty *single_pty = NULL;
177 
178 #ifndef OMIT_UTMP
179 static pid_t pty_utmp_helper_pid = -1;
180 static int pty_utmp_helper_pipe = -1;
181 static bool pty_stamped_utmp;
182 static struct utmpx utmp_entry;
183 #endif
184 
185 /*
186  * pty_argv is a grievous hack to allow a proper argv to be passed
187  * through from the Unix command line. Again, it doesn't really
188  * make sense outside a one-pty-per-process setup.
189  */
190 char **pty_argv;
191 
192 char *pty_osx_envrestore_prefix;
193 
194 static void pty_close(Pty *pty);
195 static void pty_try_write(Pty *pty);
196 
197 #ifndef OMIT_UTMP
setup_utmp(char * ttyname,char * location)198 static void setup_utmp(char *ttyname, char *location)
199 {
200 #ifdef HAVE_LASTLOG
201     struct lastlog lastlog_entry;
202     FILE *lastlog;
203 #endif
204     struct passwd *pw;
205     struct timeval tv;
206 
207     pw = getpwuid(getuid());
208     if (!pw)
209         return; /* can't stamp utmp if we don't have a username */
210     memset(&utmp_entry, 0, sizeof(utmp_entry));
211     utmp_entry.ut_type = USER_PROCESS;
212     utmp_entry.ut_pid = getpid();
213 #if __GNUC__ >= 8
214 #   pragma GCC diagnostic push
215 #   pragma GCC diagnostic ignored "-Wstringop-truncation"
216 #endif // __GNUC__ >= 8
217     strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));
218     strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));
219     strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));
220     strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));
221 #if __GNUC__ >= 8
222 #   pragma GCC diagnostic pop
223 #endif // __GNUC__ >= 8
224     /*
225      * Apparently there are some architectures where (struct
226      * utmpx).ut_tv is not essentially struct timeval (e.g. Linux
227      * amd64). Hence the temporary.
228      */
229     gettimeofday(&tv, NULL);
230     utmp_entry.ut_tv.tv_sec = tv.tv_sec;
231     utmp_entry.ut_tv.tv_usec = tv.tv_usec;
232 
233     setutxent();
234     pututxline(&utmp_entry);
235     endutxent();
236 
237     updwtmpx(WTMPX_FILE, &utmp_entry);
238 
239 #ifdef HAVE_LASTLOG
240     memset(&lastlog_entry, 0, sizeof(lastlog_entry));
241     strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line));
242     strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host));
243     time(&lastlog_entry.ll_time);
244     if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) {
245         fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET);
246         fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog);
247         fclose(lastlog);
248     }
249 #endif
250 
251     pty_stamped_utmp = true;
252 
253 }
254 
cleanup_utmp(void)255 static void cleanup_utmp(void)
256 {
257     struct timeval tv;
258 
259     if (!pty_stamped_utmp)
260         return;
261 
262     utmp_entry.ut_type = DEAD_PROCESS;
263     memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));
264     gettimeofday(&tv, NULL);
265     utmp_entry.ut_tv.tv_sec = tv.tv_sec;
266     utmp_entry.ut_tv.tv_usec = tv.tv_usec;
267 
268     updwtmpx(WTMPX_FILE, &utmp_entry);
269 
270     memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line));
271     utmp_entry.ut_tv.tv_sec = 0;
272     utmp_entry.ut_tv.tv_usec = 0;
273 
274     setutxent();
275     pututxline(&utmp_entry);
276     endutxent();
277 
278     pty_stamped_utmp = false;     /* ensure we never double-cleanup */
279 }
280 #endif
281 
sigchld_handler(int signum)282 static void sigchld_handler(int signum)
283 {
284     if (write(pty_signal_pipe[1], "x", 1) <= 0)
285         /* not much we can do about it */;
286 }
287 
pty_setup_sigchld_handler(void)288 static void pty_setup_sigchld_handler(void)
289 {
290     static bool setup = false;
291     if (!setup) {
292         putty_signal(SIGCHLD, sigchld_handler);
293         setup = true;
294     }
295 }
296 
297 #ifndef OMIT_UTMP
fatal_sig_handler(int signum)298 static void fatal_sig_handler(int signum)
299 {
300     putty_signal(signum, SIG_DFL);
301     cleanup_utmp();
302     raise(signum);
303 }
304 #endif
305 
pty_open_slave(Pty * pty)306 static int pty_open_slave(Pty *pty)
307 {
308     if (pty->slave_fd < 0) {
309         pty->slave_fd = open(pty->name, O_RDWR);
310         cloexec(pty->slave_fd);
311     }
312 
313     return pty->slave_fd;
314 }
315 
pty_open_master(Pty * pty)316 static void pty_open_master(Pty *pty)
317 {
318 #ifdef BSD_PTYS
319     const char chars1[] = "pqrstuvwxyz";
320     const char chars2[] = "0123456789abcdef";
321     const char *p1, *p2;
322     char master_name[20];
323     struct group *gp;
324 
325     for (p1 = chars1; *p1; p1++)
326         for (p2 = chars2; *p2; p2++) {
327             sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
328             pty->master_fd = open(master_name, O_RDWR);
329             if (pty->master_fd >= 0) {
330                 if (geteuid() == 0 ||
331                     access(master_name, R_OK | W_OK) == 0) {
332                     /*
333                      * We must also check at this point that we are
334                      * able to open the slave side of the pty. We
335                      * wouldn't want to allocate the wrong master,
336                      * get all the way down to forking, and _then_
337                      * find we're unable to open the slave.
338                      */
339                     strcpy(pty->name, master_name);
340                     pty->name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */
341 
342                     cloexec(pty->master_fd);
343 
344                     if (pty_open_slave(pty) >= 0 &&
345                         access(pty->name, R_OK | W_OK) == 0)
346                         goto got_one;
347                     if (pty->slave_fd > 0)
348                         close(pty->slave_fd);
349                     pty->slave_fd = -1;
350                 }
351                 close(pty->master_fd);
352             }
353         }
354 
355     /* If we get here, we couldn't get a tty at all. */
356     fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
357     exit(1);
358 
359     got_one:
360 
361     /* We need to chown/chmod the /dev/ttyXX device. */
362     gp = getgrnam("tty");
363     chown(pty->name, getuid(), gp ? gp->gr_gid : -1);
364     chmod(pty->name, 0600);
365 #else
366 
367     const int flags = O_RDWR
368 #ifdef O_NOCTTY
369         | O_NOCTTY
370 #endif
371         ;
372 
373 #ifdef HAVE_POSIX_OPENPT
374 #ifdef SET_NONBLOCK_VIA_OPENPT
375     /*
376      * OS X, as of 10.10 at least, doesn't permit me to set O_NONBLOCK
377      * on pty master fds via the usual fcntl mechanism. Fortunately,
378      * it does let me work around this by adding O_NONBLOCK to the
379      * posix_openpt flags parameter, which isn't a documented use of
380      * the API but seems to work. So we'll do that for now.
381      */
382     pty->master_fd = posix_openpt(flags | O_NONBLOCK);
383 #else
384     pty->master_fd = posix_openpt(flags);
385 #endif
386 
387     if (pty->master_fd < 0) {
388         perror("posix_openpt");
389         exit(1);
390     }
391 #else
392     pty->master_fd = open("/dev/ptmx", flags);
393 
394     if (pty->master_fd < 0) {
395         perror("/dev/ptmx: open");
396         exit(1);
397     }
398 #endif
399 
400     if (grantpt(pty->master_fd) < 0) {
401         perror("grantpt");
402         exit(1);
403     }
404 
405     if (unlockpt(pty->master_fd) < 0) {
406         perror("unlockpt");
407         exit(1);
408     }
409 
410     cloexec(pty->master_fd);
411 
412     pty->name[FILENAME_MAX-1] = '\0';
413     strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1);
414 #endif
415 
416 #ifndef SET_NONBLOCK_VIA_OPENPT
417     nonblock(pty->master_fd);
418 #endif
419 }
420 
new_pty_struct(void)421 static Pty *new_pty_struct(void)
422 {
423     Pty *pty = snew(Pty);
424     pty->conf = NULL;
425     pty->pending_eof = false;
426     bufchain_init(&pty->output_data);
427     return pty;
428 }
429 
430 /*
431  * Pre-initialisation. This is here to get around the fact that GTK
432  * doesn't like being run in setuid/setgid programs (probably
433  * sensibly). So before we initialise GTK - and therefore before we
434  * even process the command line - we check to see if we're running
435  * set[ug]id. If so, we open our pty master _now_, chown it as
436  * necessary, and drop privileges. We can always close it again
437  * later. If we're potentially going to be doing utmp as well, we
438  * also fork off a utmp helper process and communicate with it by
439  * means of a pipe; the utmp helper will keep privileges in order
440  * to clean up utmp when we exit (i.e. when its end of our pipe
441  * closes).
442  */
pty_pre_init(void)443 void pty_pre_init(void)
444 {
445 #ifndef NO_PTY_PRE_INIT
446 
447     Pty *pty;
448 
449 #ifndef OMIT_UTMP
450     pid_t pid;
451     int pipefd[2];
452 #endif
453 
454     pty = single_pty = new_pty_struct();
455 
456     /* set the child signal handler straight away; it needs to be set
457      * before we ever fork. */
458     pty_setup_sigchld_handler();
459     pty->master_fd = pty->slave_fd = -1;
460 #ifndef OMIT_UTMP
461     pty_stamped_utmp = false;
462 #endif
463 
464     if (geteuid() != getuid() || getegid() != getgid()) {
465         pty_open_master(pty);
466 
467 #ifndef OMIT_UTMP
468         /*
469          * Fork off the utmp helper.
470          */
471         if (pipe(pipefd) < 0) {
472             perror("pterm: pipe");
473             exit(1);
474         }
475         cloexec(pipefd[0]);
476         cloexec(pipefd[1]);
477         pid = fork();
478         if (pid < 0) {
479             perror("pterm: fork");
480             exit(1);
481         } else if (pid == 0) {
482             char display[128], buffer[128];
483             int dlen, ret;
484 
485             close(pipefd[1]);
486             /*
487              * Now sit here until we receive a display name from the
488              * other end of the pipe, and then stamp utmp. Unstamp utmp
489              * again, and exit, when the pipe closes.
490              */
491 
492             dlen = 0;
493             while (1) {
494 
495                 ret = read(pipefd[0], buffer, lenof(buffer));
496                 if (ret <= 0) {
497                     cleanup_utmp();
498                     _exit(0);
499                 } else if (!pty_stamped_utmp) {
500                     if (dlen < lenof(display))
501                         memcpy(display+dlen, buffer,
502                                min(ret, lenof(display)-dlen));
503                     if (buffer[ret-1] == '\0') {
504                         /*
505                          * Now we have a display name. NUL-terminate
506                          * it, and stamp utmp.
507                          */
508                         display[lenof(display)-1] = '\0';
509                         /*
510                          * Trap as many fatal signals as we can in the
511                          * hope of having the best possible chance to
512                          * clean up utmp before termination. We are
513                          * unfortunately unprotected against SIGKILL,
514                          * but that's life.
515                          */
516                         putty_signal(SIGHUP, fatal_sig_handler);
517                         putty_signal(SIGINT, fatal_sig_handler);
518                         putty_signal(SIGQUIT, fatal_sig_handler);
519                         putty_signal(SIGILL, fatal_sig_handler);
520                         putty_signal(SIGABRT, fatal_sig_handler);
521                         putty_signal(SIGFPE, fatal_sig_handler);
522                         putty_signal(SIGPIPE, fatal_sig_handler);
523                         putty_signal(SIGALRM, fatal_sig_handler);
524                         putty_signal(SIGTERM, fatal_sig_handler);
525                         putty_signal(SIGSEGV, fatal_sig_handler);
526                         putty_signal(SIGUSR1, fatal_sig_handler);
527                         putty_signal(SIGUSR2, fatal_sig_handler);
528 #ifdef SIGBUS
529                         putty_signal(SIGBUS, fatal_sig_handler);
530 #endif
531 #ifdef SIGPOLL
532                         putty_signal(SIGPOLL, fatal_sig_handler);
533 #endif
534 #ifdef SIGPROF
535                         putty_signal(SIGPROF, fatal_sig_handler);
536 #endif
537 #ifdef SIGSYS
538                         putty_signal(SIGSYS, fatal_sig_handler);
539 #endif
540 #ifdef SIGTRAP
541                         putty_signal(SIGTRAP, fatal_sig_handler);
542 #endif
543 #ifdef SIGVTALRM
544                         putty_signal(SIGVTALRM, fatal_sig_handler);
545 #endif
546 #ifdef SIGXCPU
547                         putty_signal(SIGXCPU, fatal_sig_handler);
548 #endif
549 #ifdef SIGXFSZ
550                         putty_signal(SIGXFSZ, fatal_sig_handler);
551 #endif
552 #ifdef SIGIO
553                         putty_signal(SIGIO, fatal_sig_handler);
554 #endif
555                         setup_utmp(pty->name, display);
556                     }
557                 }
558             }
559         } else {
560             close(pipefd[0]);
561             pty_utmp_helper_pid = pid;
562             pty_utmp_helper_pipe = pipefd[1];
563         }
564 #endif
565     }
566 
567     /* Drop privs. */
568     {
569 #ifndef HAVE_NO_SETRESUID
570         int gid = getgid(), uid = getuid();
571         int setresgid(gid_t, gid_t, gid_t);
572         int setresuid(uid_t, uid_t, uid_t);
573         if (setresgid(gid, gid, gid) < 0) {
574             perror("setresgid");
575             exit(1);
576         }
577         if (setresuid(uid, uid, uid) < 0) {
578             perror("setresuid");
579             exit(1);
580         }
581 #else
582         if (setgid(getgid()) < 0) {
583             perror("setgid");
584             exit(1);
585         }
586         if (setuid(getuid()) < 0) {
587             perror("setuid");
588             exit(1);
589         }
590 #endif
591     }
592 
593 #endif /* NO_PTY_PRE_INIT */
594 
595 }
596 
597 static void pty_try_wait(void);
598 
pty_real_select_result(Pty * pty,int fd,int event,int status)599 static void pty_real_select_result(Pty *pty, int fd, int event, int status)
600 {
601     char buf[4096];
602     int ret;
603     bool finished = false;
604 
605     if (event < 0) {
606         /*
607          * We've been called because our child process did
608          * something. `status' tells us what.
609          */
610         if ((WIFEXITED(status) || WIFSIGNALED(status))) {
611             /*
612              * The primary child process died.
613              */
614             pty->child_dead = true;
615             del234(ptys_by_pid, pty);
616             pty->exit_code = status;
617 
618             /*
619              * If this is an ordinary pty session, this is also the
620              * moment to terminate the whole backend.
621              *
622              * We _could_ instead keep the terminal open for remaining
623              * subprocesses to output to, but conventional wisdom
624              * seems to feel that that's the Wrong Thing for an
625              * xterm-alike, so we bail out now (though we don't
626              * necessarily _close_ the window, depending on the state
627              * of Close On Exit). This would be easy enough to change
628              * or make configurable if necessary.
629              */
630             if (pty->master_fd >= 0)
631                 finished = true;
632         }
633     } else {
634         if (event == SELECT_R) {
635             bool is_stdout = (fd == pty->master_o);
636 
637             ret = read(fd, buf, sizeof(buf));
638 
639             /*
640              * Treat EIO on a pty master as equivalent to EOF (because
641              * that's how the kernel seems to report the event where
642              * the last process connected to the other end of the pty
643              * went away).
644              */
645             if (fd == pty->master_fd && ret < 0 && errno == EIO)
646                 ret = 0;
647 
648             if (ret == 0) {
649                 /*
650                  * EOF on this input fd, so to begin with, we may as
651                  * well close it, and remove all references to it in
652                  * the pty's fd fields.
653                  */
654                 uxsel_del(fd);
655                 close(fd);
656                 if (pty->master_fd == fd)
657                     pty->master_fd = -1;
658                 if (pty->master_o == fd)
659                     pty->master_o = -1;
660                 if (pty->master_e == fd)
661                     pty->master_e = -1;
662 
663                 if (is_stdout) {
664                     /*
665                      * We assume a clean exit if the pty (or stdout
666                      * pipe) has closed, but the actual child process
667                      * hasn't. The only way I can imagine this
668                      * happening is if it detaches itself from the pty
669                      * and goes daemonic - in which case the expected
670                      * usage model would precisely _not_ be for the
671                      * pterm window to hang around!
672                      */
673                     finished = true;
674                     pty_try_wait(); /* one last effort to collect exit code */
675                     if (!pty->child_dead)
676                         pty->exit_code = 0;
677                 }
678             } else if (ret < 0) {
679                 perror("read pty master");
680                 exit(1);
681             } else if (ret > 0) {
682                 seat_output(pty->seat, !is_stdout, buf, ret);
683             }
684         } else if (event == SELECT_W) {
685             /*
686              * Attempt to send data down the pty.
687              */
688             pty_try_write(pty);
689         }
690     }
691 
692     if (finished && !pty->finished) {
693         int close_on_exit;
694         int i;
695 
696         for (i = 0; i < 3; i++)
697             if (pty->fds[i].fd >= 0)
698                 uxsel_del(pty->fds[i].fd);
699 
700         pty_close(pty);
701 
702         pty->finished = true;
703 
704         /*
705          * This is a slight layering-violation sort of hack: only
706          * if we're not closing on exit (COE is set to Never, or to
707          * Only On Clean and it wasn't a clean exit) do we output a
708          * `terminated' message.
709          */
710         close_on_exit = conf_get_int(pty->conf, CONF_close_on_exit);
711         if (close_on_exit == FORCE_OFF ||
712             (close_on_exit == AUTO && pty->exit_code != 0)) {
713             char *message;
714             if (WIFEXITED(pty->exit_code)) {
715                 message = dupprintf(
716                     "\r\n[pterm: process terminated with exit code %d]\r\n",
717                     WEXITSTATUS(pty->exit_code));
718             } else if (WIFSIGNALED(pty->exit_code)) {
719 #ifdef HAVE_NO_STRSIGNAL
720                 message = dupprintf(
721                     "\r\n[pterm: process terminated on signal %d]\r\n",
722                     WTERMSIG(pty->exit_code));
723 #else
724                 message = dupprintf(
725                     "\r\n[pterm: process terminated on signal %d (%s)]\r\n",
726                     WTERMSIG(pty->exit_code),
727                     strsignal(WTERMSIG(pty->exit_code)));
728 #endif
729             } else {
730                 /* _Shouldn't_ happen, but if it does, a vague message
731                  * is better than no message at all */
732                 message = dupprintf("\r\n[pterm: process terminated]\r\n");
733             }
734             seat_stdout_pl(pty->seat, ptrlen_from_asciz(message));
735             sfree(message);
736         }
737 
738         seat_eof(pty->seat);
739         seat_notify_remote_exit(pty->seat);
740     }
741 }
742 
pty_try_wait(void)743 static void pty_try_wait(void)
744 {
745     Pty *pty;
746     pid_t pid;
747     int status;
748 
749     do {
750         pid = waitpid(-1, &status, WNOHANG);
751 
752         pty = find234(ptys_by_pid, &pid, pty_find_by_pid);
753 
754         if (pty)
755             pty_real_select_result(pty, -1, -1, status);
756     } while (pid > 0);
757 }
758 
pty_select_result(int fd,int event)759 void pty_select_result(int fd, int event)
760 {
761     if (fd == pty_signal_pipe[0]) {
762         char c[1];
763 
764         if (read(pty_signal_pipe[0], c, 1) <= 0)
765             /* ignore error */;
766         /* ignore its value; it'll be `x' */
767 
768         pty_try_wait();
769     } else {
770         PtyFd *ptyfd = find234(ptyfds, &fd, ptyfd_find);
771 
772         if (ptyfd)
773             pty_real_select_result(ptyfd->pty, fd, event, 0);
774     }
775 }
776 
pty_uxsel_setup_fd(Pty * pty,int fd)777 static void pty_uxsel_setup_fd(Pty *pty, int fd)
778 {
779     int rwx = 0;
780 
781     if (fd < 0)
782         return;
783 
784     /* read from standard output and standard error pipes */
785     if (pty->master_o == fd || pty->master_e == fd)
786         rwx |= SELECT_R;
787     /* write to standard input pipe if we have any data */
788     if (pty->master_i == fd && bufchain_size(&pty->output_data))
789         rwx |= SELECT_W;
790 
791     uxsel_set(fd, rwx, pty_select_result);
792 }
793 
pty_uxsel_setup(Pty * pty)794 static void pty_uxsel_setup(Pty *pty)
795 {
796     /*
797      * We potentially have three separate fds here, but on the other
798      * hand, some of them might be the same (if they're a pty master).
799      * So we can't just call uxsel_set(master_o, SELECT_R) and then
800      * uxsel_set(master_i, SELECT_W), without the latter potentially
801      * undoing the work of the former if master_o == master_i.
802      *
803      * Instead, here we call a single uxsel on each one of these fds
804      * (if it exists at all), and for each one, check it against all
805      * three to see which bits to set.
806      */
807     pty_uxsel_setup_fd(pty, pty->master_o);
808     pty_uxsel_setup_fd(pty, pty->master_e);
809     pty_uxsel_setup_fd(pty, pty->master_i);
810 
811     /*
812      * In principle this only needs calling once for all pty
813      * backend instances, but it's simplest just to call it every
814      * time; uxsel won't mind.
815      */
816     uxsel_set(pty_signal_pipe[0], SELECT_R, pty_select_result);
817 }
818 
copy_ttymodes_into_termios(struct termios * attrs,struct ssh_ttymodes modes)819 static void copy_ttymodes_into_termios(
820     struct termios *attrs, struct ssh_ttymodes modes)
821 {
822 #define TTYMODE_CHAR(name, ssh_opcode, cc_index) {                      \
823         if (modes.have_mode[ssh_opcode]) {                              \
824             unsigned value = modes.mode_val[ssh_opcode];                \
825             /* normalise wire value of 255 to local _POSIX_VDISABLE */  \
826             attrs->c_cc[cc_index] = (value == 255 ?                     \
827                                      _POSIX_VDISABLE : value);          \
828         }                                                               \
829     }
830 
831 #define TTYMODE_FLAG(flagval, ssh_opcode, field, flagmask) {    \
832         if (modes.have_mode[ssh_opcode]) {                      \
833             attrs->c_##field##flag &= ~flagmask;                \
834             if (modes.mode_val[ssh_opcode])                     \
835                 attrs->c_##field##flag |= flagval;              \
836         }                                                       \
837     }
838 
839 #define TTYMODES_LOCAL_ONLY   /* omit any that this platform doesn't know */
840 #include "sshttymodes.h"
841 
842 #undef TTYMODES_LOCAL_ONLY
843 #undef TTYMODE_CHAR
844 #undef TTYMODE_FLAG
845 
846     if (modes.have_mode[TTYMODE_ISPEED])
847         cfsetispeed(attrs, modes.mode_val[TTYMODE_ISPEED]);
848     if (modes.have_mode[TTYMODE_OSPEED])
849         cfsetospeed(attrs, modes.mode_val[TTYMODE_OSPEED]);
850 }
851 
852 /*
853  * The main setup function for the pty back end. This doesn't match
854  * the signature of backend_init(), partly because it has to be able
855  * to take extra arguments such as an argv array, and also because
856  * once we're changing the type signature _anyway_ we can discard the
857  * stuff that's not really applicable to this backend like host names
858  * and port numbers.
859  */
pty_backend_create(Seat * seat,LogContext * logctx,Conf * conf,char ** argv,const char * cmd,struct ssh_ttymodes ttymodes,bool pipes_instead,const char * dir,const char * const * env_vars_to_unset)860 Backend *pty_backend_create(
861     Seat *seat, LogContext *logctx, Conf *conf, char **argv, const char *cmd,
862     struct ssh_ttymodes ttymodes, bool pipes_instead, const char *dir,
863     const char *const *env_vars_to_unset)
864 {
865     int slavefd;
866     pid_t pid, pgrp;
867 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */
868     bool got_windowid;
869     long windowid;
870 #endif
871     Pty *pty;
872     int i;
873 
874     /* No local authentication phase in this protocol */
875     seat_set_trust_status(seat, false);
876 
877     if (single_pty) {
878         pty = single_pty;
879         assert(pty->conf == NULL);
880     } else {
881         pty = new_pty_struct();
882         pty->master_fd = pty->slave_fd = -1;
883 #ifndef OMIT_UTMP
884         pty_stamped_utmp = false;
885 #endif
886     }
887     for (i = 0; i < 6; i++)
888         pty->pipefds[i] = -1;
889     for (i = 0; i < 3; i++) {
890         pty->fds[i].fd = -1;
891         pty->fds[i].pty = pty;
892     }
893 
894     if (pty_signal_pipe[0] < 0) {
895         if (pipe(pty_signal_pipe) < 0) {
896             perror("pipe");
897             exit(1);
898         }
899         cloexec(pty_signal_pipe[0]);
900         cloexec(pty_signal_pipe[1]);
901     }
902 
903     pty->seat = seat;
904     pty->backend.vt = &pty_backend;
905 
906     pty->conf = conf_copy(conf);
907     pty->term_width = conf_get_int(conf, CONF_width);
908     pty->term_height = conf_get_int(conf, CONF_height);
909 
910     if (!ptyfds)
911         ptyfds = newtree234(ptyfd_compare);
912 
913     if (pipes_instead) {
914         if (pty->master_fd >= 0) {
915             /* If somehow we've got a pty master already and don't
916              * need it, throw it away! */
917             close(pty->master_fd);
918 #ifndef OMIT_UTMP
919             if (pty_utmp_helper_pipe >= 0) {
920                 close(pty_utmp_helper_pipe); /* don't need this either */
921                 pty_utmp_helper_pipe = -1;
922             }
923 #endif
924         }
925 
926 
927         for (i = 0; i < 6; i += 2) {
928             if (pipe(pty->pipefds + i) < 0) {
929                 backend_free(&pty->backend);
930                 return NULL;
931             }
932         }
933 
934         pty->fds[0].fd = pty->master_i = pty->pipefds[1];
935         pty->fds[1].fd = pty->master_o = pty->pipefds[2];
936         pty->fds[2].fd = pty->master_e = pty->pipefds[4];
937 
938         add234(ptyfds, &pty->fds[0]);
939         add234(ptyfds, &pty->fds[1]);
940         add234(ptyfds, &pty->fds[2]);
941     } else {
942         if (pty->master_fd < 0)
943             pty_open_master(pty);
944 
945 #ifndef OMIT_UTMP
946         /*
947          * Stamp utmp (that is, tell the utmp helper process to do so),
948          * or not.
949          */
950         if (pty_utmp_helper_pipe >= 0) {   /* if it's < 0, we can't anyway */
951             if (!conf_get_bool(conf, CONF_stamp_utmp)) {
952                 /* We're not stamping utmp, so just let the child
953                  * process die that was waiting to unstamp it later. */
954                 close(pty_utmp_helper_pipe);
955                 pty_utmp_helper_pipe = -1;
956             } else {
957                 const char *location = seat_get_x_display(pty->seat);
958                 int len = strlen(location)+1, pos = 0; /* +1 to include NUL */
959                 while (pos < len) {
960                     int ret = write(pty_utmp_helper_pipe,
961                                     location + pos, len - pos);
962                     if (ret < 0) {
963                         perror("pterm: writing to utmp helper process");
964                         close(pty_utmp_helper_pipe); /* arrgh, just give up */
965                         pty_utmp_helper_pipe = -1;
966                         break;
967                     }
968                     pos += ret;
969                 }
970             }
971         }
972 #endif
973 
974         pty->master_i = pty->master_fd;
975         pty->master_o = pty->master_fd;
976         pty->master_e = -1;
977 
978         pty->fds[0].fd = pty->master_fd;
979         add234(ptyfds, &pty->fds[0]);
980     }
981 
982 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */
983     got_windowid = seat_get_windowid(pty->seat, &windowid);
984 #endif
985 
986     /*
987      * Set up the signal handler to catch SIGCHLD, if pty_pre_init
988      * didn't already do it.
989      */
990     pty_setup_sigchld_handler();
991 
992     /*
993      * Fork and execute the command.
994      */
995     pid = fork();
996     if (pid < 0) {
997         perror("fork");
998         exit(1);
999     }
1000 
1001     if (pid == 0) {
1002         struct termios attrs;
1003 
1004         /*
1005          * We are the child.
1006          */
1007 
1008         if (pty_osx_envrestore_prefix) {
1009             int plen = strlen(pty_osx_envrestore_prefix);
1010             extern char **environ;
1011             char **ep;
1012 
1013           restart_osx_env_restore:
1014             for (ep = environ; *ep; ep++) {
1015                 char *e = *ep;
1016 
1017                 if (!strncmp(e, pty_osx_envrestore_prefix, plen)) {
1018                     bool unset = (e[plen] == 'u');
1019                     char *pname = dupprintf("%.*s", (int)strcspn(e, "="), e);
1020                     char *name = pname + plen + 1;
1021                     char *value = e + strcspn(e, "=");
1022                     if (*value) value++;
1023                     value = dupstr(value);
1024                     if (unset)
1025                         unsetenv(name);
1026                     else
1027                         setenv(name, value, 1);
1028                     unsetenv(pname);
1029                     sfree(pname);
1030                     sfree(value);
1031                     goto restart_osx_env_restore;
1032                 }
1033             }
1034         }
1035 
1036         pgrp = getpid();
1037 
1038         if (pipes_instead) {
1039             int i;
1040             dup2(pty->pipefds[0], 0);
1041             dup2(pty->pipefds[3], 1);
1042             dup2(pty->pipefds[5], 2);
1043             for (i = 0; i < 6; i++)
1044                 close(pty->pipefds[i]);
1045 
1046             setsid();
1047         } else {
1048             slavefd = pty_open_slave(pty);
1049             if (slavefd < 0) {
1050                 perror("slave pty: open");
1051                 _exit(1);
1052             }
1053 
1054             close(pty->master_fd);
1055             noncloexec(slavefd);
1056             dup2(slavefd, 0);
1057             dup2(slavefd, 1);
1058             dup2(slavefd, 2);
1059             close(slavefd);
1060             setsid();
1061 #ifdef TIOCSCTTY
1062             ioctl(0, TIOCSCTTY, 1);
1063 #endif
1064             tcsetpgrp(0, pgrp);
1065 
1066             /*
1067              * Set up configuration-dependent termios settings on the new
1068              * pty. Linux would have let us do this on the pty master
1069              * before we forked, but that fails on OS X, so we do it here
1070              * instead.
1071              */
1072             if (tcgetattr(0, &attrs) == 0) {
1073                 /*
1074                  * Set the backspace character to be whichever of ^H and
1075                  * ^? is specified by bksp_is_delete.
1076                  */
1077                 attrs.c_cc[VERASE] = conf_get_bool(conf, CONF_bksp_is_delete)
1078                     ? '\177' : '\010';
1079 
1080                 /*
1081                  * Set the IUTF8 bit iff the character set is UTF-8.
1082                  */
1083 #ifdef IUTF8
1084                 if (seat_is_utf8(seat))
1085                     attrs.c_iflag |= IUTF8;
1086                 else
1087                     attrs.c_iflag &= ~IUTF8;
1088 #endif
1089 
1090                 copy_ttymodes_into_termios(&attrs, ttymodes);
1091 
1092                 tcsetattr(0, TCSANOW, &attrs);
1093             }
1094         }
1095 
1096         setpgid(pgrp, pgrp);
1097         if (!pipes_instead) {
1098             int ptyfd = open(pty->name, O_WRONLY, 0);
1099             if (ptyfd >= 0)
1100                 close(ptyfd);
1101         }
1102         setpgid(pgrp, pgrp);
1103 
1104         if (env_vars_to_unset)
1105             for (const char *const *p = env_vars_to_unset; *p; p++)
1106                 unsetenv(*p);
1107 
1108         if (!pipes_instead) {
1109             char *term_env_var = dupprintf("TERM=%s",
1110                                            conf_get_str(conf, CONF_termtype));
1111             putenv(term_env_var);
1112             /* We mustn't free term_env_var, as putenv links it into the
1113              * environment in place.
1114              */
1115         }
1116 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */
1117         if (got_windowid) {
1118             char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid);
1119             putenv(windowid_env_var);
1120             /* We mustn't free windowid_env_var, as putenv links it into the
1121              * environment in place.
1122              */
1123         }
1124         {
1125             /*
1126              * In case we were invoked with a --display argument that
1127              * doesn't match DISPLAY in our actual environment, we
1128              * should set DISPLAY for processes running inside the
1129              * terminal to match the display the terminal itself is
1130              * on.
1131              */
1132             const char *x_display = seat_get_x_display(pty->seat);
1133             if (x_display) {
1134                 char *x_display_env_var = dupprintf("DISPLAY=%s", x_display);
1135                 putenv(x_display_env_var);
1136                 /* As above, we don't free this. */
1137             } else {
1138                 unsetenv("DISPLAY");
1139             }
1140         }
1141 #endif
1142         {
1143             char *key, *val;
1144 
1145             for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1146                  val != NULL;
1147                  val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1148                 char *varval = dupcat(key, "=", val);
1149                 putenv(varval);
1150                 /*
1151                  * We must not free varval, since putenv links it
1152                  * into the environment _in place_. Weird, but
1153                  * there we go. Memory usage will be rationalised
1154                  * as soon as we exec anyway.
1155                  */
1156             }
1157         }
1158 
1159         if (dir) {
1160             if (chdir(dir) < 0) {
1161                 /* Ignore the error - nothing we can sensibly do about it,
1162                  * and our existing cwd is as good a fallback as any. */
1163             }
1164         }
1165 
1166         /*
1167          * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by
1168          * our parent, particularly by things like sh -c 'pterm &' and
1169          * some window or session managers. SIGPIPE was also
1170          * (potentially) blocked by us during startup. Reverse all
1171          * this for our child process.
1172          */
1173         putty_signal(SIGINT, SIG_DFL);
1174         putty_signal(SIGQUIT, SIG_DFL);
1175         putty_signal(SIGPIPE, SIG_DFL);
1176         block_signal(SIGPIPE, false);
1177         if (argv || cmd) {
1178             /*
1179              * If we were given a separated argument list, try to exec
1180              * it.
1181              */
1182             if (argv) {
1183                 execvp(argv[0], argv);
1184             }
1185             /*
1186              * Otherwise, if we were given a single command string,
1187              * try passing that to $SHELL -c.
1188              *
1189              * In the case of pterm, this system of fallbacks arranges
1190              * that we can _either_ follow 'pterm -e' with a list of
1191              * argv elements to be fed directly to exec, _or_ with a
1192              * single argument containing a command to be parsed by a
1193              * shell (but, in cases of doubt, the former is more
1194              * reliable). We arrange this by setting argv to the full
1195              * argument list, and also setting cmd to the single
1196              * element of argv if it's a length-1 list.
1197              *
1198              * A quick survey of other terminal emulators' -e options
1199              * (as of Debian squeeze) suggests that:
1200              *
1201              *  - xterm supports both modes, more or less like this
1202              *  - gnome-terminal will only accept a one-string shell command
1203              *  - Eterm, kterm and rxvt will only accept a list of
1204              *    argv elements (as did older versions of pterm).
1205              *
1206              * It therefore seems important to support both usage
1207              * modes in order to be a drop-in replacement for either
1208              * xterm or gnome-terminal, and hence for anyone's
1209              * plausible uses of the Debian-style alias
1210              * 'x-terminal-emulator'.
1211              *
1212              * In other use cases, a caller can set only one of argv
1213              * and cmd to get a fixed handling of the input.
1214              */
1215             if (cmd) {
1216                 char *shell = getenv("SHELL");
1217                 if (shell)
1218                     execl(shell, shell, "-c", cmd, (void *)NULL);
1219             }
1220         } else {
1221             const char *shell = getenv("SHELL");
1222             if (!shell)
1223                 shell = "/bin/sh";
1224             char *shellname;
1225             if (conf_get_bool(conf, CONF_login_shell)) {
1226                 const char *p = strrchr(shell, '/');
1227                 shellname = snewn(2+strlen(shell), char);
1228                 p = p ? p+1 : shell;
1229                 sprintf(shellname, "-%s", p);
1230             } else
1231                 shellname = (char *)shell;
1232             execl(shell, shellname, (void *)NULL);
1233         }
1234 
1235         /*
1236          * If we're here, exec has gone badly foom.
1237          */
1238         perror("exec");
1239         _exit(127);
1240     } else {
1241         pty->child_pid = pid;
1242         pty->child_dead = false;
1243         pty->finished = false;
1244         if (pty->slave_fd > 0)
1245             close(pty->slave_fd);
1246         if (!ptys_by_pid)
1247             ptys_by_pid = newtree234(pty_compare_by_pid);
1248         if (pty->pipefds[0] >= 0) {
1249             close(pty->pipefds[0]);
1250             pty->pipefds[0] = -1;
1251         }
1252         if (pty->pipefds[3] >= 0) {
1253             close(pty->pipefds[3]);
1254             pty->pipefds[3] = -1;
1255         }
1256         if (pty->pipefds[5] >= 0) {
1257             close(pty->pipefds[5]);
1258             pty->pipefds[5] = -1;
1259         }
1260         add234(ptys_by_pid, pty);
1261     }
1262 
1263     pty_uxsel_setup(pty);
1264 
1265     return &pty->backend;
1266 }
1267 
1268 /*
1269  * This is the pty backend's _official_ init method, for BackendVtable
1270  * purposes. Its job is just to be an API converter, ignoring the
1271  * irrelevant input parameters and making up auxiliary outputs. Also
1272  * it gets the argv array from the global variable pty_argv, expecting
1273  * that it will have been invoked by pterm.
1274  */
pty_init(const BackendVtable * vt,Seat * seat,Backend ** backend_handle,LogContext * logctx,Conf * conf,const char * host,int port,char ** realhost,bool nodelay,bool keepalive)1275 static char *pty_init(const BackendVtable *vt, Seat *seat,
1276                       Backend **backend_handle, LogContext *logctx,
1277                       Conf *conf, const char *host, int port,
1278                       char **realhost, bool nodelay, bool keepalive)
1279 {
1280     const char *cmd = NULL;
1281     struct ssh_ttymodes modes;
1282 
1283     memset(&modes, 0, sizeof(modes));
1284 
1285     if (pty_argv && pty_argv[0] && !pty_argv[1])
1286         cmd = pty_argv[0];
1287 
1288     assert(vt == &pty_backend);
1289     *backend_handle = pty_backend_create(
1290         seat, logctx, conf, pty_argv, cmd, modes, false, NULL, NULL);
1291     *realhost = dupstr("");
1292     return NULL;
1293 }
1294 
pty_reconfig(Backend * be,Conf * conf)1295 static void pty_reconfig(Backend *be, Conf *conf)
1296 {
1297     Pty *pty = container_of(be, Pty, backend);
1298     /*
1299      * We don't have much need to reconfigure this backend, but
1300      * unfortunately we do need to pick up the setting of Close On
1301      * Exit so we know whether to give a `terminated' message.
1302      */
1303     conf_copy_into(pty->conf, conf);
1304 }
1305 
1306 /*
1307  * Stub routine (never called in pterm).
1308  */
pty_free(Backend * be)1309 static void pty_free(Backend *be)
1310 {
1311     Pty *pty = container_of(be, Pty, backend);
1312     int i;
1313 
1314     pty_close(pty);
1315 
1316     /* Either of these may fail `not found'. That's fine with us. */
1317     del234(ptys_by_pid, pty);
1318     for (i = 0; i < 3; i++)
1319         if (pty->fds[i].fd >= 0)
1320             del234(ptyfds, &pty->fds[i]);
1321 
1322     bufchain_clear(&pty->output_data);
1323 
1324     conf_free(pty->conf);
1325     pty->conf = NULL;
1326 
1327     if (pty == single_pty) {
1328         /*
1329          * Leave this structure around in case we need to Restart
1330          * Session.
1331          */
1332     } else {
1333         sfree(pty);
1334     }
1335 }
1336 
pty_try_write(Pty * pty)1337 static void pty_try_write(Pty *pty)
1338 {
1339     ssize_t ret;
1340 
1341     assert(pty->master_i >= 0);
1342 
1343     while (bufchain_size(&pty->output_data) > 0) {
1344         ptrlen data = bufchain_prefix(&pty->output_data);
1345         ret = write(pty->master_i, data.ptr, data.len);
1346 
1347         if (ret < 0 && (errno == EWOULDBLOCK)) {
1348             /*
1349              * We've sent all we can for the moment.
1350              */
1351             break;
1352         }
1353         if (ret < 0) {
1354             perror("write pty master");
1355             exit(1);
1356         }
1357         bufchain_consume(&pty->output_data, ret);
1358     }
1359 
1360     if (pty->pending_eof && bufchain_size(&pty->output_data) == 0) {
1361         /* This should only happen if pty->master_i is a pipe that
1362          * doesn't alias either output fd */
1363         assert(pty->master_i != pty->master_o);
1364         assert(pty->master_i != pty->master_e);
1365         uxsel_del(pty->master_i);
1366         close(pty->master_i);
1367         pty->master_i = -1;
1368         pty->pending_eof = false;
1369     }
1370 
1371     pty_uxsel_setup(pty);
1372 }
1373 
1374 /*
1375  * Called to send data down the pty.
1376  */
pty_send(Backend * be,const char * buf,size_t len)1377 static size_t pty_send(Backend *be, const char *buf, size_t len)
1378 {
1379     Pty *pty = container_of(be, Pty, backend);
1380 
1381     if (pty->master_i < 0 || pty->pending_eof)
1382         return 0;                      /* ignore all writes if fd closed */
1383 
1384     bufchain_add(&pty->output_data, buf, len);
1385     pty_try_write(pty);
1386 
1387     return bufchain_size(&pty->output_data);
1388 }
1389 
pty_close(Pty * pty)1390 static void pty_close(Pty *pty)
1391 {
1392     int i;
1393 
1394     if (pty->master_o >= 0)
1395         uxsel_del(pty->master_o);
1396     if (pty->master_e >= 0)
1397         uxsel_del(pty->master_e);
1398     if (pty->master_i >= 0)
1399         uxsel_del(pty->master_i);
1400 
1401     if (pty->master_fd >= 0) {
1402         close(pty->master_fd);
1403         pty->master_fd = -1;
1404     }
1405     for (i = 0; i < 6; i++) {
1406         if (pty->pipefds[i] >= 0)
1407             close(pty->pipefds[i]);
1408         pty->pipefds[i] = -1;
1409     }
1410     pty->master_i = pty->master_o = pty->master_e = -1;
1411 #ifndef OMIT_UTMP
1412     if (pty_utmp_helper_pipe >= 0) {
1413         close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */
1414         pty_utmp_helper_pipe = -1;
1415     }
1416 #endif
1417 }
1418 
1419 /*
1420  * Called to query the current socket sendability status.
1421  */
pty_sendbuffer(Backend * be)1422 static size_t pty_sendbuffer(Backend *be)
1423 {
1424     /* Pty *pty = container_of(be, Pty, backend); */
1425     return 0;
1426 }
1427 
1428 /*
1429  * Called to set the size of the window
1430  */
pty_size(Backend * be,int width,int height)1431 static void pty_size(Backend *be, int width, int height)
1432 {
1433     Pty *pty = container_of(be, Pty, backend);
1434     struct winsize size;
1435     int xpixel = 0, ypixel = 0;
1436 
1437     pty->term_width = width;
1438     pty->term_height = height;
1439 
1440     if (pty->master_fd < 0)
1441         return;
1442 
1443     seat_get_window_pixel_size(pty->seat, &xpixel, &ypixel);
1444 
1445     size.ws_row = (unsigned short)pty->term_height;
1446     size.ws_col = (unsigned short)pty->term_width;
1447     size.ws_xpixel = (unsigned short)xpixel;
1448     size.ws_ypixel = (unsigned short)ypixel;
1449     ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size);
1450     return;
1451 }
1452 
1453 /*
1454  * Send special codes.
1455  */
pty_special(Backend * be,SessionSpecialCode code,int arg)1456 static void pty_special(Backend *be, SessionSpecialCode code, int arg)
1457 {
1458     Pty *pty = container_of(be, Pty, backend);
1459 
1460     if (code == SS_BRK) {
1461         if (pty->master_fd >= 0)
1462             tcsendbreak(pty->master_fd, 0);
1463         return;
1464     }
1465 
1466     if (code == SS_EOF) {
1467         if (pty->master_i >= 0 && pty->master_i != pty->master_fd) {
1468             pty->pending_eof = true;
1469             pty_try_write(pty);
1470         }
1471         return;
1472     }
1473 
1474     {
1475         int sig = -1;
1476 
1477         #define SIGNAL_SUB(name) if (code == SS_SIG ## name) sig = SIG ## name;
1478         #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name)
1479         #define SIGNALS_LOCAL_ONLY
1480         #include "sshsignals.h"
1481         #undef SIGNAL_SUB
1482         #undef SIGNAL_MAIN
1483         #undef SIGNALS_LOCAL_ONLY
1484 
1485         if (sig != -1) {
1486             if (!pty->child_dead)
1487                 kill(pty->child_pid, sig);
1488             return;
1489         }
1490     }
1491 
1492     return;
1493 }
1494 
1495 /*
1496  * Return a list of the special codes that make sense in this
1497  * protocol.
1498  */
pty_get_specials(Backend * be)1499 static const SessionSpecial *pty_get_specials(Backend *be)
1500 {
1501     /* Pty *pty = container_of(be, Pty, backend); */
1502     /*
1503      * Hmm. When I get round to having this actually usable, it
1504      * might be quite nice to have the ability to deliver a few
1505      * well chosen signals to the child process - SIGINT, SIGTERM,
1506      * SIGKILL at least.
1507      */
1508     return NULL;
1509 }
1510 
pty_connected(Backend * be)1511 static bool pty_connected(Backend *be)
1512 {
1513     /* Pty *pty = container_of(be, Pty, backend); */
1514     return true;
1515 }
1516 
pty_sendok(Backend * be)1517 static bool pty_sendok(Backend *be)
1518 {
1519     /* Pty *pty = container_of(be, Pty, backend); */
1520     return true;
1521 }
1522 
pty_unthrottle(Backend * be,size_t backlog)1523 static void pty_unthrottle(Backend *be, size_t backlog)
1524 {
1525     /* Pty *pty = container_of(be, Pty, backend); */
1526     /* do nothing */
1527 }
1528 
pty_ldisc(Backend * be,int option)1529 static bool pty_ldisc(Backend *be, int option)
1530 {
1531     /* Pty *pty = container_of(be, Pty, backend); */
1532     return false;                    /* neither editing nor echoing */
1533 }
1534 
pty_provide_ldisc(Backend * be,Ldisc * ldisc)1535 static void pty_provide_ldisc(Backend *be, Ldisc *ldisc)
1536 {
1537     /* Pty *pty = container_of(be, Pty, backend); */
1538     /* This is a stub. */
1539 }
1540 
pty_exitcode(Backend * be)1541 static int pty_exitcode(Backend *be)
1542 {
1543     Pty *pty = container_of(be, Pty, backend);
1544     if (!pty->finished)
1545         return -1;                     /* not dead yet */
1546     else if (WIFSIGNALED(pty->exit_code))
1547         return 128 + WTERMSIG(pty->exit_code);
1548     else
1549         return WEXITSTATUS(pty->exit_code);
1550 }
1551 
pty_backend_exit_signum(Backend * be)1552 int pty_backend_exit_signum(Backend *be)
1553 {
1554     Pty *pty = container_of(be, Pty, backend);
1555 
1556     if (!pty->finished || !WIFSIGNALED(pty->exit_code))
1557         return -1;
1558 
1559     return WTERMSIG(pty->exit_code);
1560 }
1561 
pty_backend_exit_signame(Backend * be,char ** aux_msg)1562 ptrlen pty_backend_exit_signame(Backend *be, char **aux_msg)
1563 {
1564     *aux_msg = NULL;
1565 
1566     int sig = pty_backend_exit_signum(be);
1567     if (sig < 0)
1568         return PTRLEN_LITERAL("");
1569 
1570     #define SIGNAL_SUB(s) {                             \
1571         if (sig == SIG ## s)                            \
1572             return PTRLEN_LITERAL(#s);                  \
1573     }
1574     #define SIGNAL_MAIN(s, desc) SIGNAL_SUB(s)
1575     #define SIGNALS_LOCAL_ONLY
1576     #include "sshsignals.h"
1577     #undef SIGNAL_MAIN
1578     #undef SIGNAL_SUB
1579     #undef SIGNALS_LOCAL_ONLY
1580 
1581     *aux_msg = dupprintf("untranslatable signal number %d: %s",
1582                          sig, strsignal(sig));
1583     return PTRLEN_LITERAL("HUP");      /* need some kind of default */
1584 }
1585 
pty_cfg_info(Backend * be)1586 static int pty_cfg_info(Backend *be)
1587 {
1588     /* Pty *pty = container_of(be, Pty, backend); */
1589     return 0;
1590 }
1591 
1592 const BackendVtable pty_backend = {
1593     .init = pty_init,
1594     .free = pty_free,
1595     .reconfig = pty_reconfig,
1596     .send = pty_send,
1597     .sendbuffer = pty_sendbuffer,
1598     .size = pty_size,
1599     .special = pty_special,
1600     .get_specials = pty_get_specials,
1601     .connected = pty_connected,
1602     .exitcode = pty_exitcode,
1603     .sendok = pty_sendok,
1604     .ldisc_option_state = pty_ldisc,
1605     .provide_ldisc = pty_provide_ldisc,
1606     .unthrottle = pty_unthrottle,
1607     .cfg_info = pty_cfg_info,
1608     .id = "pty",
1609     .displayname = "pty",
1610     .protocol = -1,
1611 };
1612