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