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