]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pty.c
Turned the old `Telnet Command' System-submenu into a more general
[PuTTY.git] / unix / pty.c
1 /*
2  * Pseudo-tty backend for pterm.
3  * 
4  * Unlike the other backends, data for this one is not neatly
5  * encapsulated into a data structure, because it wouldn't make
6  * sense to do so - the utmp stuff has to be done before a backend
7  * is initialised, and starting a second pterm from the same
8  * process would therefore be infeasible because privileges would
9  * already have been dropped. Hence, I haven't bothered to keep the
10  * data dynamically allocated: instead, the backend handle is just
11  * a null pointer and ignored everywhere.
12  */
13
14 #define _XOPEN_SOURCE
15 #define _XOPEN_SOURCE_EXTENDED
16 #define _GNU_SOURCE
17 #include <features.h>
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <fcntl.h>
25 #include <termios.h>
26 #include <grp.h>
27 #include <utmp.h>
28 #include <pwd.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <sys/ioctl.h>
33 #include <errno.h>
34
35 #include "putty.h"
36
37 #ifndef FALSE
38 #define FALSE 0
39 #endif
40 #ifndef TRUE
41 #define TRUE 1
42 #endif
43
44 #ifndef UTMP_FILE
45 #define UTMP_FILE "/var/run/utmp"
46 #endif
47 #ifndef WTMP_FILE
48 #define WTMP_FILE "/var/log/wtmp"
49 #endif
50 #ifndef LASTLOG_FILE
51 #ifdef _PATH_LASTLOG
52 #define LASTLOG_FILE _PATH_LASTLOG
53 #else
54 #define LASTLOG_FILE "/var/log/lastlog"
55 #endif
56 #endif
57
58 /*
59  * Set up a default for vaguely sane systems. The idea is that if
60  * OMIT_UTMP is not defined, then at least one of the symbols which
61  * enable particular forms of utmp processing should be, if only so
62  * that a link error can warn you that you should have defined
63  * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is
64  * the only such symbol.
65  */
66 #ifndef OMIT_UTMP
67 #if !defined HAVE_PUTUTLINE
68 #define HAVE_PUTUTLINE
69 #endif
70 #endif
71
72 static Config pty_cfg;
73 static int pty_master_fd;
74 static void *pty_frontend;
75 static char pty_name[FILENAME_MAX];
76 static int pty_signal_pipe[2];
77 static int pty_stamped_utmp = 0;
78 static int pty_child_pid;
79 static int pty_utmp_helper_pid, pty_utmp_helper_pipe;
80 static int pty_term_width, pty_term_height;
81 static int pty_child_dead, pty_finished;
82 static int pty_exit_code;
83 #ifndef OMIT_UTMP
84 static struct utmp utmp_entry;
85 #endif
86 char **pty_argv;
87 int use_pty_argv = TRUE;
88
89 static void pty_close(void);
90
91 static void setup_utmp(char *ttyname, char *location)
92 {
93 #ifndef OMIT_UTMP
94 #ifdef HAVE_LASTLOG
95     struct lastlog lastlog_entry;
96     FILE *lastlog;
97 #endif
98     struct passwd *pw;
99     FILE *wtmp;
100
101     pw = getpwuid(getuid());
102     memset(&utmp_entry, 0, sizeof(utmp_entry));
103     utmp_entry.ut_type = USER_PROCESS;
104     utmp_entry.ut_pid = getpid();
105     strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));
106     strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));
107     strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));
108     strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));
109     time(&utmp_entry.ut_time);
110
111 #if defined HAVE_PUTUTLINE
112     utmpname(UTMP_FILE);
113     setutent();
114     pututline(&utmp_entry);
115     endutent();
116 #endif
117
118     if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {
119         fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp);
120         fclose(wtmp);
121     }
122
123 #ifdef HAVE_LASTLOG
124     memset(&lastlog_entry, 0, sizeof(lastlog_entry));
125     strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line));
126     strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host));
127     time(&lastlog_entry.ll_time);
128     if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) {
129         fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET);
130         fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog);
131         fclose(lastlog);
132     }
133 #endif
134
135     pty_stamped_utmp = 1;
136
137 #endif
138 }
139
140 static void cleanup_utmp(void)
141 {
142 #ifndef OMIT_UTMP
143     FILE *wtmp;
144
145     if (!pty_stamped_utmp)
146         return;
147
148     utmp_entry.ut_type = DEAD_PROCESS;
149     memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));
150     time(&utmp_entry.ut_time);
151
152     if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {
153         fwrite(&utmp_entry, 1, sizeof(utmp_entry), wtmp);
154         fclose(wtmp);
155     }
156
157     memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line));
158     utmp_entry.ut_time = 0;
159
160 #if defined HAVE_PUTUTLINE
161     utmpname(UTMP_FILE);
162     setutent();
163     pututline(&utmp_entry);
164     endutent();
165 #endif
166
167     pty_stamped_utmp = 0;              /* ensure we never double-cleanup */
168 #endif
169 }
170
171 static void sigchld_handler(int signum)
172 {
173     write(pty_signal_pipe[1], "x", 1);
174 }
175
176 static void fatal_sig_handler(int signum)
177 {
178     putty_signal(signum, SIG_DFL);
179     cleanup_utmp();
180     setuid(getuid());
181     raise(signum);
182 }
183
184 static void pty_open_master(void)
185 {
186 #ifdef BSD_PTYS
187     const char chars1[] = "pqrstuvwxyz";
188     const char chars2[] = "0123456789abcdef";
189     const char *p1, *p2;
190     char master_name[20];
191     struct group *gp;
192
193     for (p1 = chars1; *p1; p1++)
194         for (p2 = chars2; *p2; p2++) {
195             sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
196             pty_master_fd = open(master_name, O_RDWR);
197             if (pty_master_fd >= 0) {
198                 if (geteuid() == 0 ||
199                     access(master_name, R_OK | W_OK) == 0)
200                     goto got_one;
201                 close(pty_master_fd);
202             }
203         }
204
205     /* If we get here, we couldn't get a tty at all. */
206     fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
207     exit(1);
208
209     got_one:
210     strcpy(pty_name, master_name);
211     pty_name[5] = 't';                 /* /dev/ptyXX -> /dev/ttyXX */
212
213     /* We need to chown/chmod the /dev/ttyXX device. */
214     gp = getgrnam("tty");
215     chown(pty_name, getuid(), gp ? gp->gr_gid : -1);
216     chmod(pty_name, 0600);
217 #else
218     pty_master_fd = open("/dev/ptmx", O_RDWR);
219
220     if (pty_master_fd < 0) {
221         perror("/dev/ptmx: open");
222         exit(1);
223     }
224
225     if (grantpt(pty_master_fd) < 0) {
226         perror("grantpt");
227         exit(1);
228     }
229     
230     if (unlockpt(pty_master_fd) < 0) {
231         perror("unlockpt");
232         exit(1);
233     }
234
235     pty_name[FILENAME_MAX-1] = '\0';
236     strncpy(pty_name, ptsname(pty_master_fd), FILENAME_MAX-1);
237 #endif
238 }
239
240 /*
241  * Pre-initialisation. This is here to get around the fact that GTK
242  * doesn't like being run in setuid/setgid programs (probably
243  * sensibly). So before we initialise GTK - and therefore before we
244  * even process the command line - we check to see if we're running
245  * set[ug]id. If so, we open our pty master _now_, chown it as
246  * necessary, and drop privileges. We can always close it again
247  * later. If we're potentially going to be doing utmp as well, we
248  * also fork off a utmp helper process and communicate with it by
249  * means of a pipe; the utmp helper will keep privileges in order
250  * to clean up utmp when we exit (i.e. when its end of our pipe
251  * closes).
252  */
253 void pty_pre_init(void)
254 {
255     pid_t pid;
256     int pipefd[2];
257
258     /* set the child signal handler straight away; it needs to be set
259      * before we ever fork. */
260     putty_signal(SIGCHLD, sigchld_handler);
261     pty_master_fd = -1;
262
263     if (geteuid() != getuid() || getegid() != getgid()) {
264         pty_open_master();
265     }
266
267 #ifndef OMIT_UTMP
268     /*
269      * Fork off the utmp helper.
270      */
271     if (pipe(pipefd) < 0) {
272         perror("pterm: pipe");
273         exit(1);
274     }
275     pid = fork();
276     if (pid < 0) {
277         perror("pterm: fork");
278         exit(1);
279     } else if (pid == 0) {
280         char display[128], buffer[128];
281         int dlen, ret;
282
283         close(pipefd[1]);
284         /*
285          * Now sit here until we receive a display name from the
286          * other end of the pipe, and then stamp utmp. Unstamp utmp
287          * again, and exit, when the pipe closes.
288          */
289
290         dlen = 0;
291         while (1) {
292             
293             ret = read(pipefd[0], buffer, lenof(buffer));
294             if (ret <= 0) {
295                 cleanup_utmp();
296                 _exit(0);
297             } else if (!pty_stamped_utmp) {
298                 if (dlen < lenof(display))
299                     memcpy(display+dlen, buffer,
300                            min(ret, lenof(display)-dlen));
301                 if (buffer[ret-1] == '\0') {
302                     /*
303                      * Now we have a display name. NUL-terminate
304                      * it, and stamp utmp.
305                      */
306                     display[lenof(display)-1] = '\0';
307                     /*
308                      * Trap as many fatal signals as we can in the
309                      * hope of having the best possible chance to
310                      * clean up utmp before termination. We are
311                      * unfortunately unprotected against SIGKILL,
312                      * but that's life.
313                      */
314                     putty_signal(SIGHUP, fatal_sig_handler);
315                     putty_signal(SIGINT, fatal_sig_handler);
316                     putty_signal(SIGQUIT, fatal_sig_handler);
317                     putty_signal(SIGILL, fatal_sig_handler);
318                     putty_signal(SIGABRT, fatal_sig_handler);
319                     putty_signal(SIGFPE, fatal_sig_handler);
320                     putty_signal(SIGPIPE, fatal_sig_handler);
321                     putty_signal(SIGALRM, fatal_sig_handler);
322                     putty_signal(SIGTERM, fatal_sig_handler);
323                     putty_signal(SIGSEGV, fatal_sig_handler);
324                     putty_signal(SIGUSR1, fatal_sig_handler);
325                     putty_signal(SIGUSR2, fatal_sig_handler);
326 #ifdef SIGBUS
327                     putty_signal(SIGBUS, fatal_sig_handler);
328 #endif
329 #ifdef SIGPOLL
330                     putty_signal(SIGPOLL, fatal_sig_handler);
331 #endif
332 #ifdef SIGPROF
333                     putty_signal(SIGPROF, fatal_sig_handler);
334 #endif
335 #ifdef SIGSYS
336                     putty_signal(SIGSYS, fatal_sig_handler);
337 #endif
338 #ifdef SIGTRAP
339                     putty_signal(SIGTRAP, fatal_sig_handler);
340 #endif
341 #ifdef SIGVTALRM
342                     putty_signal(SIGVTALRM, fatal_sig_handler);
343 #endif
344 #ifdef SIGXCPU
345                     putty_signal(SIGXCPU, fatal_sig_handler);
346 #endif
347 #ifdef SIGXFSZ
348                     putty_signal(SIGXFSZ, fatal_sig_handler);
349 #endif
350 #ifdef SIGIO
351                     putty_signal(SIGIO, fatal_sig_handler);
352 #endif
353                     setup_utmp(pty_name, display);
354                 }
355             }
356         }
357     } else {
358         close(pipefd[0]);
359         pty_utmp_helper_pid = pid;
360         pty_utmp_helper_pipe = pipefd[1];
361     }
362 #endif
363
364     /* Drop privs. */
365     {
366         int gid = getgid(), uid = getuid();
367 #ifndef HAVE_NO_SETRESUID
368         int setresgid(gid_t, gid_t, gid_t);
369         int setresuid(uid_t, uid_t, uid_t);
370         setresgid(gid, gid, gid);
371         setresuid(uid, uid, uid);
372 #else
373         setgid(getgid());
374         setuid(getuid());
375 #endif
376     }
377 }
378
379 int pty_select_result(int fd, int event)
380 {
381     char buf[4096];
382     int ret;
383     int finished = FALSE;
384
385     if (fd == pty_master_fd && event == 1) {
386
387         ret = read(pty_master_fd, buf, sizeof(buf));
388
389         /*
390          * Clean termination condition is that either ret == 0, or ret
391          * < 0 and errno == EIO. Not sure why the latter, but it seems
392          * to happen. Boo.
393          */
394         if (ret == 0 || (ret < 0 && errno == EIO)) {
395             /*
396              * We assume a clean exit if the pty has closed but the
397              * actual child process hasn't. The only way I can
398              * imagine this happening is if it detaches itself from
399              * the pty and goes daemonic - in which case the
400              * expected usage model would precisely _not_ be for
401              * the pterm window to hang around!
402              */
403             finished = TRUE;
404             if (!pty_child_dead)
405                 pty_exit_code = 0;
406         } else if (ret < 0) {
407             perror("read pty master");
408             exit(1);
409         } else if (ret > 0) {
410             from_backend(pty_frontend, 0, buf, ret);
411         }
412     } else if (fd == pty_signal_pipe[0]) {
413         pid_t pid;
414         int status;
415         char c[1];
416
417         read(pty_signal_pipe[0], c, 1); /* ignore its value; it'll be `x' */
418
419         do {
420             pid = waitpid(-1, &status, WNOHANG);
421             if (pid == pty_child_pid &&
422                 (WIFEXITED(status) || WIFSIGNALED(status))) {
423                 /*
424                  * The primary child process died. We could keep
425                  * the terminal open for remaining subprocesses to
426                  * output to, but conventional wisdom seems to feel
427                  * that that's the Wrong Thing for an xterm-alike,
428                  * so we bail out now (though we don't necessarily
429                  * _close_ the window, depending on the state of
430                  * Close On Exit). This would be easy enough to
431                  * change or make configurable if necessary.
432                  */
433                 pty_exit_code = status;
434                 pty_child_dead = TRUE;
435                 finished = TRUE;
436             }
437         } while(pid > 0);
438     }
439
440     if (finished && !pty_finished) {
441         uxsel_del(pty_master_fd);
442         pty_close();
443         pty_master_fd = -1;
444
445         pty_finished = TRUE;
446
447         /*
448          * This is a slight layering-violation sort of hack: only
449          * if we're not closing on exit (COE is set to Never, or to
450          * Only On Clean and it wasn't a clean exit) do we output a
451          * `terminated' message.
452          */
453         if (pty_cfg.close_on_exit == FORCE_OFF ||
454             (pty_cfg.close_on_exit == AUTO && pty_exit_code != 0)) {
455             char message[512];
456             if (WIFEXITED(pty_exit_code))
457                 sprintf(message, "\r\n[pterm: process terminated with exit"
458                         " code %d]\r\n", WEXITSTATUS(pty_exit_code));
459             else if (WIFSIGNALED(pty_exit_code))
460 #ifdef HAVE_NO_STRSIGNAL
461                 sprintf(message, "\r\n[pterm: process terminated on signal"
462                         " %d]\r\n", WTERMSIG(pty_exit_code));
463 #else
464                 sprintf(message, "\r\n[pterm: process terminated on signal"
465                         " %d (%.400s)]\r\n", WTERMSIG(pty_exit_code),
466                         strsignal(WTERMSIG(pty_exit_code)));
467 #endif
468             from_backend(pty_frontend, 0, message, strlen(message));
469         }
470     }
471     return !finished;
472 }
473
474 static void pty_uxsel_setup(void)
475 {
476     uxsel_set(pty_master_fd, 1, pty_select_result);
477     uxsel_set(pty_signal_pipe[0], 1, pty_select_result);
478 }
479
480 /*
481  * Called to set up the pty.
482  * 
483  * Returns an error message, or NULL on success.
484  *
485  * Also places the canonical host name into `realhost'. It must be
486  * freed by the caller.
487  */
488 static char *pty_init(void *frontend, void **backend_handle, Config *cfg,
489                       char *host, int port, char **realhost, int nodelay)
490 {
491     int slavefd;
492     pid_t pid, pgrp;
493     long windowid;
494
495     pty_frontend = frontend;
496     *backend_handle = NULL;            /* we can't sensibly use this, sadly */
497
498     pty_cfg = *cfg;                    /* structure copy */
499     pty_term_width = cfg->width;
500     pty_term_height = cfg->height;
501
502     if (pty_master_fd < 0)
503         pty_open_master();
504
505     /*
506      * Set the backspace character to be whichever of ^H and ^? is
507      * specified by bksp_is_delete.
508      */
509     {
510         struct termios attrs;
511         tcgetattr(pty_master_fd, &attrs);
512         attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010';
513         tcsetattr(pty_master_fd, TCSANOW, &attrs);
514     }
515
516     /*
517      * Stamp utmp (that is, tell the utmp helper process to do so),
518      * or not.
519      */
520     if (!cfg->stamp_utmp)
521         close(pty_utmp_helper_pipe);   /* just let the child process die */
522     else {
523         char *location = get_x_display(pty_frontend);
524         int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */
525         while (pos < len) {
526             int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
527             if (ret < 0) {
528                 perror("pterm: writing to utmp helper process");
529                 close(pty_utmp_helper_pipe);   /* arrgh, just give up */
530                 break;
531             }
532             pos += ret;
533         }
534     }
535
536     windowid = get_windowid(pty_frontend);
537
538     /*
539      * Fork and execute the command.
540      */
541     pid = fork();
542     if (pid < 0) {
543         perror("fork");
544         exit(1);
545     }
546
547     if (pid == 0) {
548         int i;
549         /*
550          * We are the child.
551          */
552
553         slavefd = open(pty_name, O_RDWR);
554         if (slavefd < 0) {
555             perror("slave pty: open");
556             _exit(1);
557         }
558
559         close(pty_master_fd);
560         fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */
561         dup2(slavefd, 0);
562         dup2(slavefd, 1);
563         dup2(slavefd, 2);
564         setsid();
565         ioctl(slavefd, TIOCSCTTY, 1);
566         pgrp = getpid();
567         tcsetpgrp(slavefd, pgrp);
568         setpgrp();
569         close(open(pty_name, O_WRONLY, 0));
570         setpgrp();
571         /* Close everything _else_, for tidiness. */
572         for (i = 3; i < 1024; i++)
573             close(i);
574         {
575             char term_env_var[10 + sizeof(cfg->termtype)];
576             sprintf(term_env_var, "TERM=%s", cfg->termtype);
577             putenv(term_env_var);
578         }
579         {
580             char windowid_env_var[40];
581             sprintf(windowid_env_var, "WINDOWID=%ld", windowid);
582             putenv(windowid_env_var);
583         }
584         /*
585          * SIGINT and SIGQUIT may have been set to ignored by our
586          * parent, particularly by things like sh -c 'pterm &' and
587          * some window managers. Reverse this for our child process.
588          */
589         putty_signal(SIGINT, SIG_DFL);
590         putty_signal(SIGQUIT, SIG_DFL);
591         if (pty_argv)
592             execvp(pty_argv[0], pty_argv);
593         else {
594             char *shell = getenv("SHELL");
595             char *shellname;
596             if (cfg->login_shell) {
597                 char *p = strrchr(shell, '/');
598                 shellname = snewn(2+strlen(shell), char);
599                 p = p ? p+1 : shell;
600                 sprintf(shellname, "-%s", p);
601             } else
602                 shellname = shell;
603             execl(getenv("SHELL"), shellname, NULL);
604         }
605
606         /*
607          * If we're here, exec has gone badly foom.
608          */
609         perror("exec");
610         _exit(127);
611     } else {
612         pty_child_pid = pid;
613         pty_child_dead = FALSE;
614         pty_finished = FALSE;
615     }      
616
617     if (pipe(pty_signal_pipe) < 0) {
618         perror("pipe");
619         exit(1);
620     }
621     pty_uxsel_setup();
622
623     return NULL;
624 }
625
626 /*
627  * Stub routine (we don't have any need to reconfigure this backend).
628  */
629 static void pty_reconfig(void *handle, Config *cfg)
630 {
631 }
632
633 /*
634  * Stub routine (never called in pterm
635  */
636 static void pty_free(void *handle)
637 {
638 }
639
640
641 /*
642  * Called to send data down the pty.
643  */
644 static int pty_send(void *handle, char *buf, int len)
645 {
646     if (pty_master_fd < 0)
647         return 0;                      /* ignore all writes if fd closed */
648
649     while (len > 0) {
650         int ret = write(pty_master_fd, buf, len);
651         if (ret < 0) {
652             perror("write pty master");
653             exit(1);
654         }
655         buf += ret;
656         len -= ret;
657     }
658     return 0;
659 }
660
661 static void pty_close(void)
662 {
663     if (pty_master_fd >= 0) {
664         close(pty_master_fd);
665         pty_master_fd = -1;
666     }
667     close(pty_utmp_helper_pipe);       /* this causes utmp to be cleaned up */
668 }
669
670 /*
671  * Called to query the current socket sendability status.
672  */
673 static int pty_sendbuffer(void *handle)
674 {
675     return 0;
676 }
677
678 /*
679  * Called to set the size of the window
680  */
681 static void pty_size(void *handle, int width, int height)
682 {
683     struct winsize size;
684
685     pty_term_width = width;
686     pty_term_height = height;
687
688     size.ws_row = (unsigned short)pty_term_height;
689     size.ws_col = (unsigned short)pty_term_width;
690     size.ws_xpixel = (unsigned short) pty_term_width *
691         font_dimension(pty_frontend, 0);
692     size.ws_ypixel = (unsigned short) pty_term_height *
693         font_dimension(pty_frontend, 1);
694     ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size);
695     return;
696 }
697
698 /*
699  * Send special codes.
700  */
701 static void pty_special(void *handle, Telnet_Special code)
702 {
703     /* Do nothing! */
704     return;
705 }
706
707 /*
708  * Return a list of the special codes that make sense in this
709  * protocol.
710  */
711 static const struct telnet_special *pty_get_specials(void *handle)
712 {
713     /*
714      * Hmm. When I get round to having this actually usable, it
715      * might be quite nice to have the ability to deliver a few
716      * well chosen signals to the child process - SIGINT, SIGTERM,
717      * SIGKILL at least.
718      */
719     return NULL;
720 }
721
722 static Socket pty_socket(void *handle)
723 {
724     return NULL;                       /* shouldn't ever be needed */
725 }
726
727 static int pty_sendok(void *handle)
728 {
729     return 1;
730 }
731
732 static void pty_unthrottle(void *handle, int backlog)
733 {
734     /* do nothing */
735 }
736
737 static int pty_ldisc(void *handle, int option)
738 {
739     return 0;                          /* neither editing nor echoing */
740 }
741
742 static void pty_provide_ldisc(void *handle, void *ldisc)
743 {
744     /* This is a stub. */
745 }
746
747 static void pty_provide_logctx(void *handle, void *logctx)
748 {
749     /* This is a stub. */
750 }
751
752 static int pty_exitcode(void *handle)
753 {
754     if (!pty_finished)
755         return -1;                     /* not dead yet */
756     else
757         return pty_exit_code;
758 }
759
760 Backend pty_backend = {
761     pty_init,
762     pty_free,
763     pty_reconfig,
764     pty_send,
765     pty_sendbuffer,
766     pty_size,
767     pty_special,
768     pty_get_specials,
769     pty_socket,
770     pty_exitcode,
771     pty_sendok,
772     pty_ldisc,
773     pty_provide_ldisc,
774     pty_provide_logctx,
775     pty_unthrottle,
776     1
777 };