]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/pty.c
Moved the environment variables config block out of the Telnet panel
[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             char *e = cfg->environmt;
595             char *var, *varend, *val, *varval;
596             while (*e) {
597                 var = e;
598                 while (*e && *e != '\t') e++;
599                 varend = e;
600                 if (*e == '\t') e++;
601                 val = e;
602                 while (*e) e++;
603                 e++;
604
605                 varval = dupprintf("%.*s=%s", varend-var, var, val);
606                 putenv(varval);
607                 /*
608                  * We must not free varval, since putenv links it
609                  * into the environment _in place_. Weird, but
610                  * there we go. Memory usage will be rationalised
611                  * as soon as we exec anyway.
612                  */
613             }
614         }
615
616         /*
617          * SIGINT and SIGQUIT may have been set to ignored by our
618          * parent, particularly by things like sh -c 'pterm &' and
619          * some window managers. Reverse this for our child process.
620          */
621         putty_signal(SIGINT, SIG_DFL);
622         putty_signal(SIGQUIT, SIG_DFL);
623         if (pty_argv)
624             execvp(pty_argv[0], pty_argv);
625         else {
626             char *shell = getenv("SHELL");
627             char *shellname;
628             if (cfg->login_shell) {
629                 char *p = strrchr(shell, '/');
630                 shellname = snewn(2+strlen(shell), char);
631                 p = p ? p+1 : shell;
632                 sprintf(shellname, "-%s", p);
633             } else
634                 shellname = shell;
635             execl(getenv("SHELL"), shellname, NULL);
636         }
637
638         /*
639          * If we're here, exec has gone badly foom.
640          */
641         perror("exec");
642         _exit(127);
643     } else {
644         pty_child_pid = pid;
645         pty_child_dead = FALSE;
646         pty_finished = FALSE;
647     }      
648
649     if (pipe(pty_signal_pipe) < 0) {
650         perror("pipe");
651         exit(1);
652     }
653     pty_uxsel_setup();
654
655     return NULL;
656 }
657
658 static void pty_reconfig(void *handle, Config *cfg)
659 {
660     /*
661      * We don't have much need to reconfigure this backend, but
662      * unfortunately we do need to pick up the setting of Close On
663      * Exit so we know whether to give a `terminated' message.
664      */
665     pty_cfg = *cfg;                    /* structure copy */
666 }
667
668 /*
669  * Stub routine (never called in pterm).
670  */
671 static void pty_free(void *handle)
672 {
673 }
674
675 /*
676  * Called to send data down the pty.
677  */
678 static int pty_send(void *handle, char *buf, int len)
679 {
680     if (pty_master_fd < 0)
681         return 0;                      /* ignore all writes if fd closed */
682
683     while (len > 0) {
684         int ret = write(pty_master_fd, buf, len);
685         if (ret < 0) {
686             perror("write pty master");
687             exit(1);
688         }
689         buf += ret;
690         len -= ret;
691     }
692     return 0;
693 }
694
695 static void pty_close(void)
696 {
697     if (pty_master_fd >= 0) {
698         close(pty_master_fd);
699         pty_master_fd = -1;
700     }
701     if (pty_utmp_helper_pipe >= 0) {
702         close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */
703         pty_utmp_helper_pipe = -1;
704     }
705 }
706
707 /*
708  * Called to query the current socket sendability status.
709  */
710 static int pty_sendbuffer(void *handle)
711 {
712     return 0;
713 }
714
715 /*
716  * Called to set the size of the window
717  */
718 static void pty_size(void *handle, int width, int height)
719 {
720     struct winsize size;
721
722     pty_term_width = width;
723     pty_term_height = height;
724
725     size.ws_row = (unsigned short)pty_term_height;
726     size.ws_col = (unsigned short)pty_term_width;
727     size.ws_xpixel = (unsigned short) pty_term_width *
728         font_dimension(pty_frontend, 0);
729     size.ws_ypixel = (unsigned short) pty_term_height *
730         font_dimension(pty_frontend, 1);
731     ioctl(pty_master_fd, TIOCSWINSZ, (void *)&size);
732     return;
733 }
734
735 /*
736  * Send special codes.
737  */
738 static void pty_special(void *handle, Telnet_Special code)
739 {
740     /* Do nothing! */
741     return;
742 }
743
744 /*
745  * Return a list of the special codes that make sense in this
746  * protocol.
747  */
748 static const struct telnet_special *pty_get_specials(void *handle)
749 {
750     /*
751      * Hmm. When I get round to having this actually usable, it
752      * might be quite nice to have the ability to deliver a few
753      * well chosen signals to the child process - SIGINT, SIGTERM,
754      * SIGKILL at least.
755      */
756     return NULL;
757 }
758
759 static Socket pty_socket(void *handle)
760 {
761     return NULL;                       /* shouldn't ever be needed */
762 }
763
764 static int pty_sendok(void *handle)
765 {
766     return 1;
767 }
768
769 static void pty_unthrottle(void *handle, int backlog)
770 {
771     /* do nothing */
772 }
773
774 static int pty_ldisc(void *handle, int option)
775 {
776     return 0;                          /* neither editing nor echoing */
777 }
778
779 static void pty_provide_ldisc(void *handle, void *ldisc)
780 {
781     /* This is a stub. */
782 }
783
784 static void pty_provide_logctx(void *handle, void *logctx)
785 {
786     /* This is a stub. */
787 }
788
789 static int pty_exitcode(void *handle)
790 {
791     if (!pty_finished)
792         return -1;                     /* not dead yet */
793     else
794         return pty_exit_code;
795 }
796
797 Backend pty_backend = {
798     pty_init,
799     pty_free,
800     pty_reconfig,
801     pty_send,
802     pty_sendbuffer,
803     pty_size,
804     pty_special,
805     pty_get_specials,
806     pty_socket,
807     pty_exitcode,
808     pty_sendok,
809     pty_ldisc,
810     pty_provide_ldisc,
811     pty_provide_logctx,
812     pty_unthrottle,
813     1
814 };