OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / UNIX / UXPTY.C
1 /*\r
2  * Pseudo-tty backend for pterm.\r
3  */\r
4 \r
5 #define _GNU_SOURCE\r
6 \r
7 #include <stdio.h>\r
8 #include <stdlib.h>\r
9 #include <string.h>\r
10 #include <unistd.h>\r
11 #include <signal.h>\r
12 #include <assert.h>\r
13 #include <fcntl.h>\r
14 #include <termios.h>\r
15 #include <grp.h>\r
16 #include <utmp.h>\r
17 #include <pwd.h>\r
18 #include <time.h>\r
19 #include <sys/types.h>\r
20 #include <sys/stat.h>\r
21 #include <sys/wait.h>\r
22 #include <sys/ioctl.h>\r
23 #include <errno.h>\r
24 \r
25 #include "putty.h"\r
26 #include "tree234.h"\r
27 \r
28 #ifndef OMIT_UTMP\r
29 #include <utmpx.h>\r
30 #endif\r
31 \r
32 #ifndef FALSE\r
33 #define FALSE 0\r
34 #endif\r
35 #ifndef TRUE\r
36 #define TRUE 1\r
37 #endif\r
38 \r
39 /* updwtmpx() needs the name of the wtmp file.  Try to find it. */\r
40 #ifndef WTMPX_FILE\r
41 #ifdef _PATH_WTMPX\r
42 #define WTMPX_FILE _PATH_WTMPX\r
43 #else\r
44 #define WTMPX_FILE "/var/log/wtmpx"\r
45 #endif\r
46 #endif\r
47 \r
48 #ifndef LASTLOG_FILE\r
49 #ifdef _PATH_LASTLOG\r
50 #define LASTLOG_FILE _PATH_LASTLOG\r
51 #else\r
52 #define LASTLOG_FILE "/var/log/lastlog"\r
53 #endif\r
54 #endif\r
55 \r
56 /*\r
57  * Set up a default for vaguely sane systems. The idea is that if\r
58  * OMIT_UTMP is not defined, then at least one of the symbols which\r
59  * enable particular forms of utmp processing should be, if only so\r
60  * that a link error can warn you that you should have defined\r
61  * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is\r
62  * the only such symbol.\r
63  */\r
64 #ifndef OMIT_UTMP\r
65 #if !defined HAVE_PUTUTLINE\r
66 #define HAVE_PUTUTLINE\r
67 #endif\r
68 #endif\r
69 \r
70 typedef struct pty_tag *Pty;\r
71 \r
72 /*\r
73  * The pty_signal_pipe, along with the SIGCHLD handler, must be\r
74  * process-global rather than session-specific.\r
75  */\r
76 static int pty_signal_pipe[2] = { -1, -1 };   /* obviously bogus initial val */\r
77 \r
78 struct pty_tag {\r
79     Config cfg;\r
80     int master_fd, slave_fd;\r
81     void *frontend;\r
82     char name[FILENAME_MAX];\r
83     pid_t child_pid;\r
84     int term_width, term_height;\r
85     int child_dead, finished;\r
86     int exit_code;\r
87     bufchain output_data;\r
88 };\r
89 \r
90 /*\r
91  * We store our pty backends in a tree sorted by master fd, so that\r
92  * when we get an uxsel notification we know which backend instance\r
93  * is the owner of the pty that caused it.\r
94  */\r
95 static int pty_compare_by_fd(void *av, void *bv)\r
96 {\r
97     Pty a = (Pty)av;\r
98     Pty b = (Pty)bv;\r
99 \r
100     if (a->master_fd < b->master_fd)\r
101         return -1;\r
102     else if (a->master_fd > b->master_fd)\r
103         return +1;\r
104     return 0;\r
105 }\r
106 \r
107 static int pty_find_by_fd(void *av, void *bv)\r
108 {\r
109     int a = *(int *)av;\r
110     Pty b = (Pty)bv;\r
111 \r
112     if (a < b->master_fd)\r
113         return -1;\r
114     else if (a > b->master_fd)\r
115         return +1;\r
116     return 0;\r
117 }\r
118 \r
119 static tree234 *ptys_by_fd = NULL;\r
120 \r
121 /*\r
122  * We also have a tree sorted by child pid, so that when we wait()\r
123  * in response to the signal we know which backend instance is the\r
124  * owner of the process that caused the signal.\r
125  */\r
126 static int pty_compare_by_pid(void *av, void *bv)\r
127 {\r
128     Pty a = (Pty)av;\r
129     Pty b = (Pty)bv;\r
130 \r
131     if (a->child_pid < b->child_pid)\r
132         return -1;\r
133     else if (a->child_pid > b->child_pid)\r
134         return +1;\r
135     return 0;\r
136 }\r
137 \r
138 static int pty_find_by_pid(void *av, void *bv)\r
139 {\r
140     pid_t a = *(pid_t *)av;\r
141     Pty b = (Pty)bv;\r
142 \r
143     if (a < b->child_pid)\r
144         return -1;\r
145     else if (a > b->child_pid)\r
146         return +1;\r
147     return 0;\r
148 }\r
149 \r
150 static tree234 *ptys_by_pid = NULL;\r
151 \r
152 /*\r
153  * If we are using pty_pre_init(), it will need to have already\r
154  * allocated a pty structure, which we must then return from\r
155  * pty_init() rather than allocating a new one. Here we store that\r
156  * structure between allocation and use.\r
157  * \r
158  * Note that although most of this module is entirely capable of\r
159  * handling multiple ptys in a single process, pty_pre_init() is\r
160  * fundamentally _dependent_ on there being at most one pty per\r
161  * process, so the normal static-data constraints don't apply.\r
162  * \r
163  * Likewise, since utmp is only used via pty_pre_init, it too must\r
164  * be single-instance, so we can declare utmp-related variables\r
165  * here.\r
166  */\r
167 static Pty single_pty = NULL;\r
168 \r
169 #ifndef OMIT_UTMP\r
170 static pid_t pty_utmp_helper_pid;\r
171 static int pty_utmp_helper_pipe;\r
172 static int pty_stamped_utmp;\r
173 static struct utmpx utmp_entry;\r
174 #endif\r
175 \r
176 /*\r
177  * pty_argv is a grievous hack to allow a proper argv to be passed\r
178  * through from the Unix command line. Again, it doesn't really\r
179  * make sense outside a one-pty-per-process setup.\r
180  */\r
181 char **pty_argv;\r
182 \r
183 static void pty_close(Pty pty);\r
184 static void pty_try_write(Pty pty);\r
185 \r
186 #ifndef OMIT_UTMP\r
187 static void setup_utmp(char *ttyname, char *location)\r
188 {\r
189 #ifdef HAVE_LASTLOG\r
190     struct lastlog lastlog_entry;\r
191     FILE *lastlog;\r
192 #endif\r
193     struct passwd *pw;\r
194     struct timeval tv;\r
195 \r
196     pw = getpwuid(getuid());\r
197     memset(&utmp_entry, 0, sizeof(utmp_entry));\r
198     utmp_entry.ut_type = USER_PROCESS;\r
199     utmp_entry.ut_pid = getpid();\r
200     strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));\r
201     strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));\r
202     strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));\r
203     strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));\r
204     /*\r
205      * Apparently there are some architectures where (struct\r
206      * utmpx).ut_tv is not essentially struct timeval (e.g. Linux\r
207      * amd64). Hence the temporary.\r
208      */\r
209     gettimeofday(&tv, NULL);\r
210     utmp_entry.ut_tv.tv_sec = tv.tv_sec;\r
211     utmp_entry.ut_tv.tv_usec = tv.tv_usec;\r
212 \r
213     setutxent();\r
214     pututxline(&utmp_entry);\r
215     endutxent();\r
216 \r
217     updwtmpx(WTMPX_FILE, &utmp_entry);\r
218 \r
219 #ifdef HAVE_LASTLOG\r
220     memset(&lastlog_entry, 0, sizeof(lastlog_entry));\r
221     strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line));\r
222     strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host));\r
223     time(&lastlog_entry.ll_time);\r
224     if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) {\r
225         fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET);\r
226         fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog);\r
227         fclose(lastlog);\r
228     }\r
229 #endif\r
230 \r
231     pty_stamped_utmp = 1;\r
232 \r
233 }\r
234 \r
235 static void cleanup_utmp(void)\r
236 {\r
237     struct timeval tv;\r
238 \r
239     if (!pty_stamped_utmp)\r
240         return;\r
241 \r
242     utmp_entry.ut_type = DEAD_PROCESS;\r
243     memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));\r
244     gettimeofday(&tv, NULL);\r
245     utmp_entry.ut_tv.tv_sec = tv.tv_sec;\r
246     utmp_entry.ut_tv.tv_usec = tv.tv_usec;\r
247 \r
248     updwtmpx(WTMPX_FILE, &utmp_entry);\r
249 \r
250     memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line));\r
251     utmp_entry.ut_tv.tv_sec = 0;\r
252     utmp_entry.ut_tv.tv_usec = 0;\r
253 \r
254     setutxent();\r
255     pututxline(&utmp_entry);\r
256     endutxent();\r
257 \r
258     pty_stamped_utmp = 0;              /* ensure we never double-cleanup */\r
259 }\r
260 #endif\r
261 \r
262 static void sigchld_handler(int signum)\r
263 {\r
264     if (write(pty_signal_pipe[1], "x", 1) <= 0)\r
265         /* not much we can do about it */;\r
266 }\r
267 \r
268 #ifndef OMIT_UTMP\r
269 static void fatal_sig_handler(int signum)\r
270 {\r
271     putty_signal(signum, SIG_DFL);\r
272     cleanup_utmp();\r
273     setuid(getuid());\r
274     raise(signum);\r
275 }\r
276 #endif\r
277 \r
278 static int pty_open_slave(Pty pty)\r
279 {\r
280     if (pty->slave_fd < 0) {\r
281         pty->slave_fd = open(pty->name, O_RDWR);\r
282         cloexec(pty->slave_fd);\r
283     }\r
284 \r
285     return pty->slave_fd;\r
286 }\r
287 \r
288 static void pty_open_master(Pty pty)\r
289 {\r
290 #ifdef BSD_PTYS\r
291     const char chars1[] = "pqrstuvwxyz";\r
292     const char chars2[] = "0123456789abcdef";\r
293     const char *p1, *p2;\r
294     char master_name[20];\r
295     struct group *gp;\r
296 \r
297     for (p1 = chars1; *p1; p1++)\r
298         for (p2 = chars2; *p2; p2++) {\r
299             sprintf(master_name, "/dev/pty%c%c", *p1, *p2);\r
300             pty->master_fd = open(master_name, O_RDWR);\r
301             if (pty->master_fd >= 0) {\r
302                 if (geteuid() == 0 ||\r
303                     access(master_name, R_OK | W_OK) == 0) {\r
304                     /*\r
305                      * We must also check at this point that we are\r
306                      * able to open the slave side of the pty. We\r
307                      * wouldn't want to allocate the wrong master,\r
308                      * get all the way down to forking, and _then_\r
309                      * find we're unable to open the slave.\r
310                      */\r
311                     strcpy(pty->name, master_name);\r
312                     pty->name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */\r
313 \r
314                     cloexec(pty->master_fd);\r
315 \r
316                     if (pty_open_slave(pty) >= 0 &&\r
317                         access(pty->name, R_OK | W_OK) == 0)\r
318                         goto got_one;\r
319                     if (pty->slave_fd > 0)\r
320                         close(pty->slave_fd);\r
321                     pty->slave_fd = -1;\r
322                 }\r
323                 close(pty->master_fd);\r
324             }\r
325         }\r
326 \r
327     /* If we get here, we couldn't get a tty at all. */\r
328     fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");\r
329     exit(1);\r
330 \r
331     got_one:\r
332 \r
333     /* We need to chown/chmod the /dev/ttyXX device. */\r
334     gp = getgrnam("tty");\r
335     chown(pty->name, getuid(), gp ? gp->gr_gid : -1);\r
336     chmod(pty->name, 0600);\r
337 #else\r
338     pty->master_fd = open("/dev/ptmx", O_RDWR);\r
339 \r
340     if (pty->master_fd < 0) {\r
341         perror("/dev/ptmx: open");\r
342         exit(1);\r
343     }\r
344 \r
345     if (grantpt(pty->master_fd) < 0) {\r
346         perror("grantpt");\r
347         exit(1);\r
348     }\r
349     \r
350     if (unlockpt(pty->master_fd) < 0) {\r
351         perror("unlockpt");\r
352         exit(1);\r
353     }\r
354 \r
355     cloexec(pty->master_fd);\r
356 \r
357     pty->name[FILENAME_MAX-1] = '\0';\r
358     strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1);\r
359 #endif\r
360 \r
361     {\r
362         /*\r
363          * Set the pty master into non-blocking mode.\r
364          */\r
365         int fl;\r
366         fl = fcntl(pty->master_fd, F_GETFL);\r
367         if (fl != -1 && !(fl & O_NONBLOCK))\r
368             fcntl(pty->master_fd, F_SETFL, fl | O_NONBLOCK);\r
369     }\r
370 \r
371     if (!ptys_by_fd)\r
372         ptys_by_fd = newtree234(pty_compare_by_fd);\r
373     add234(ptys_by_fd, pty);\r
374 }\r
375 \r
376 /*\r
377  * Pre-initialisation. This is here to get around the fact that GTK\r
378  * doesn't like being run in setuid/setgid programs (probably\r
379  * sensibly). So before we initialise GTK - and therefore before we\r
380  * even process the command line - we check to see if we're running\r
381  * set[ug]id. If so, we open our pty master _now_, chown it as\r
382  * necessary, and drop privileges. We can always close it again\r
383  * later. If we're potentially going to be doing utmp as well, we\r
384  * also fork off a utmp helper process and communicate with it by\r
385  * means of a pipe; the utmp helper will keep privileges in order\r
386  * to clean up utmp when we exit (i.e. when its end of our pipe\r
387  * closes).\r
388  */\r
389 void pty_pre_init(void)\r
390 {\r
391     Pty pty;\r
392 \r
393 #ifndef OMIT_UTMP\r
394     pid_t pid;\r
395     int pipefd[2];\r
396 #endif\r
397 \r
398     pty = single_pty = snew(struct pty_tag);\r
399     bufchain_init(&pty->output_data);\r
400 \r
401     /* set the child signal handler straight away; it needs to be set\r
402      * before we ever fork. */\r
403     putty_signal(SIGCHLD, sigchld_handler);\r
404     pty->master_fd = pty->slave_fd = -1;\r
405 #ifndef OMIT_UTMP\r
406     pty_stamped_utmp = FALSE;\r
407 #endif\r
408 \r
409     if (geteuid() != getuid() || getegid() != getgid()) {\r
410         pty_open_master(pty);\r
411     }\r
412 \r
413 #ifndef OMIT_UTMP\r
414     /*\r
415      * Fork off the utmp helper.\r
416      */\r
417     if (pipe(pipefd) < 0) {\r
418         perror("pterm: pipe");\r
419         exit(1);\r
420     }\r
421     cloexec(pipefd[0]);\r
422     cloexec(pipefd[1]);\r
423     pid = fork();\r
424     if (pid < 0) {\r
425         perror("pterm: fork");\r
426         exit(1);\r
427     } else if (pid == 0) {\r
428         char display[128], buffer[128];\r
429         int dlen, ret;\r
430 \r
431         close(pipefd[1]);\r
432         /*\r
433          * Now sit here until we receive a display name from the\r
434          * other end of the pipe, and then stamp utmp. Unstamp utmp\r
435          * again, and exit, when the pipe closes.\r
436          */\r
437 \r
438         dlen = 0;\r
439         while (1) {\r
440             \r
441             ret = read(pipefd[0], buffer, lenof(buffer));\r
442             if (ret <= 0) {\r
443                 cleanup_utmp();\r
444                 _exit(0);\r
445             } else if (!pty_stamped_utmp) {\r
446                 if (dlen < lenof(display))\r
447                     memcpy(display+dlen, buffer,\r
448                            min(ret, lenof(display)-dlen));\r
449                 if (buffer[ret-1] == '\0') {\r
450                     /*\r
451                      * Now we have a display name. NUL-terminate\r
452                      * it, and stamp utmp.\r
453                      */\r
454                     display[lenof(display)-1] = '\0';\r
455                     /*\r
456                      * Trap as many fatal signals as we can in the\r
457                      * hope of having the best possible chance to\r
458                      * clean up utmp before termination. We are\r
459                      * unfortunately unprotected against SIGKILL,\r
460                      * but that's life.\r
461                      */\r
462                     putty_signal(SIGHUP, fatal_sig_handler);\r
463                     putty_signal(SIGINT, fatal_sig_handler);\r
464                     putty_signal(SIGQUIT, fatal_sig_handler);\r
465                     putty_signal(SIGILL, fatal_sig_handler);\r
466                     putty_signal(SIGABRT, fatal_sig_handler);\r
467                     putty_signal(SIGFPE, fatal_sig_handler);\r
468                     putty_signal(SIGPIPE, fatal_sig_handler);\r
469                     putty_signal(SIGALRM, fatal_sig_handler);\r
470                     putty_signal(SIGTERM, fatal_sig_handler);\r
471                     putty_signal(SIGSEGV, fatal_sig_handler);\r
472                     putty_signal(SIGUSR1, fatal_sig_handler);\r
473                     putty_signal(SIGUSR2, fatal_sig_handler);\r
474 #ifdef SIGBUS\r
475                     putty_signal(SIGBUS, fatal_sig_handler);\r
476 #endif\r
477 #ifdef SIGPOLL\r
478                     putty_signal(SIGPOLL, fatal_sig_handler);\r
479 #endif\r
480 #ifdef SIGPROF\r
481                     putty_signal(SIGPROF, fatal_sig_handler);\r
482 #endif\r
483 #ifdef SIGSYS\r
484                     putty_signal(SIGSYS, fatal_sig_handler);\r
485 #endif\r
486 #ifdef SIGTRAP\r
487                     putty_signal(SIGTRAP, fatal_sig_handler);\r
488 #endif\r
489 #ifdef SIGVTALRM\r
490                     putty_signal(SIGVTALRM, fatal_sig_handler);\r
491 #endif\r
492 #ifdef SIGXCPU\r
493                     putty_signal(SIGXCPU, fatal_sig_handler);\r
494 #endif\r
495 #ifdef SIGXFSZ\r
496                     putty_signal(SIGXFSZ, fatal_sig_handler);\r
497 #endif\r
498 #ifdef SIGIO\r
499                     putty_signal(SIGIO, fatal_sig_handler);\r
500 #endif\r
501                     setup_utmp(pty->name, display);\r
502                 }\r
503             }\r
504         }\r
505     } else {\r
506         close(pipefd[0]);\r
507         pty_utmp_helper_pid = pid;\r
508         pty_utmp_helper_pipe = pipefd[1];\r
509     }\r
510 #endif\r
511 \r
512     /* Drop privs. */\r
513     {\r
514 #ifndef HAVE_NO_SETRESUID\r
515         int gid = getgid(), uid = getuid();\r
516         int setresgid(gid_t, gid_t, gid_t);\r
517         int setresuid(uid_t, uid_t, uid_t);\r
518         setresgid(gid, gid, gid);\r
519         setresuid(uid, uid, uid);\r
520 #else\r
521         setgid(getgid());\r
522         setuid(getuid());\r
523 #endif\r
524     }\r
525 }\r
526 \r
527 int pty_real_select_result(Pty pty, int event, int status)\r
528 {\r
529     char buf[4096];\r
530     int ret;\r
531     int finished = FALSE;\r
532 \r
533     if (event < 0) {\r
534         /*\r
535          * We've been called because our child process did\r
536          * something. `status' tells us what.\r
537          */\r
538         if ((WIFEXITED(status) || WIFSIGNALED(status))) {\r
539             /*\r
540              * The primary child process died. We could keep\r
541              * the terminal open for remaining subprocesses to\r
542              * output to, but conventional wisdom seems to feel\r
543              * that that's the Wrong Thing for an xterm-alike,\r
544              * so we bail out now (though we don't necessarily\r
545              * _close_ the window, depending on the state of\r
546              * Close On Exit). This would be easy enough to\r
547              * change or make configurable if necessary.\r
548              */\r
549             pty->exit_code = status;\r
550             pty->child_dead = TRUE;\r
551             del234(ptys_by_pid, pty);\r
552             finished = TRUE;\r
553         }\r
554     } else {\r
555         if (event == 1) {\r
556 \r
557             ret = read(pty->master_fd, buf, sizeof(buf));\r
558 \r
559             /*\r
560              * Clean termination condition is that either ret == 0, or ret\r
561              * < 0 and errno == EIO. Not sure why the latter, but it seems\r
562              * to happen. Boo.\r
563              */\r
564             if (ret == 0 || (ret < 0 && errno == EIO)) {\r
565                 /*\r
566                  * We assume a clean exit if the pty has closed but the\r
567                  * actual child process hasn't. The only way I can\r
568                  * imagine this happening is if it detaches itself from\r
569                  * the pty and goes daemonic - in which case the\r
570                  * expected usage model would precisely _not_ be for\r
571                  * the pterm window to hang around!\r
572                  */\r
573                 finished = TRUE;\r
574                 if (!pty->child_dead)\r
575                     pty->exit_code = 0;\r
576             } else if (ret < 0) {\r
577                 perror("read pty master");\r
578                 exit(1);\r
579             } else if (ret > 0) {\r
580                 from_backend(pty->frontend, 0, buf, ret);\r
581             }\r
582         } else if (event == 2) {\r
583             /*\r
584              * Attempt to send data down the pty.\r
585              */\r
586             pty_try_write(pty);\r
587         }\r
588     }\r
589 \r
590     if (finished && !pty->finished) {\r
591         uxsel_del(pty->master_fd);\r
592         pty_close(pty);\r
593         pty->master_fd = -1;\r
594 \r
595         pty->finished = TRUE;\r
596 \r
597         /*\r
598          * This is a slight layering-violation sort of hack: only\r
599          * if we're not closing on exit (COE is set to Never, or to\r
600          * Only On Clean and it wasn't a clean exit) do we output a\r
601          * `terminated' message.\r
602          */\r
603         if (pty->cfg.close_on_exit == FORCE_OFF ||\r
604             (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) {\r
605             char message[512];\r
606             if (WIFEXITED(pty->exit_code))\r
607                 sprintf(message, "\r\n[pterm: process terminated with exit"\r
608                         " code %d]\r\n", WEXITSTATUS(pty->exit_code));\r
609             else if (WIFSIGNALED(pty->exit_code))\r
610 #ifdef HAVE_NO_STRSIGNAL\r
611                 sprintf(message, "\r\n[pterm: process terminated on signal"\r
612                         " %d]\r\n", WTERMSIG(pty->exit_code));\r
613 #else\r
614                 sprintf(message, "\r\n[pterm: process terminated on signal"\r
615                         " %d (%.400s)]\r\n", WTERMSIG(pty->exit_code),\r
616                         strsignal(WTERMSIG(pty->exit_code)));\r
617 #endif\r
618             from_backend(pty->frontend, 0, message, strlen(message));\r
619         }\r
620 \r
621         notify_remote_exit(pty->frontend);\r
622     }\r
623 \r
624     return !finished;\r
625 }\r
626 \r
627 int pty_select_result(int fd, int event)\r
628 {\r
629     int ret = TRUE;\r
630     Pty pty;\r
631 \r
632     if (fd == pty_signal_pipe[0]) {\r
633         pid_t pid;\r
634         int status;\r
635         char c[1];\r
636 \r
637         if (read(pty_signal_pipe[0], c, 1) <= 0)\r
638             /* ignore error */;\r
639         /* ignore its value; it'll be `x' */\r
640 \r
641         do {\r
642             pid = waitpid(-1, &status, WNOHANG);\r
643 \r
644             pty = find234(ptys_by_pid, &pid, pty_find_by_pid);\r
645 \r
646             if (pty)\r
647                 ret = ret && pty_real_select_result(pty, -1, status);\r
648         } while (pid > 0);\r
649     } else {\r
650         pty = find234(ptys_by_fd, &fd, pty_find_by_fd);\r
651 \r
652         if (pty)\r
653             ret = ret && pty_real_select_result(pty, event, 0);\r
654     }\r
655 \r
656     return ret;\r
657 }\r
658 \r
659 static void pty_uxsel_setup(Pty pty)\r
660 {\r
661     int rwx;\r
662 \r
663     rwx = 1;                           /* always want to read from pty */\r
664     if (bufchain_size(&pty->output_data))\r
665         rwx |= 2;                      /* might also want to write to it */\r
666     uxsel_set(pty->master_fd, rwx, pty_select_result);\r
667 \r
668     /*\r
669      * In principle this only needs calling once for all pty\r
670      * backend instances, but it's simplest just to call it every\r
671      * time; uxsel won't mind.\r
672      */\r
673     uxsel_set(pty_signal_pipe[0], 1, pty_select_result);\r
674 }\r
675 \r
676 /*\r
677  * Called to set up the pty.\r
678  * \r
679  * Returns an error message, or NULL on success.\r
680  *\r
681  * Also places the canonical host name into `realhost'. It must be\r
682  * freed by the caller.\r
683  */\r
684 static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,\r
685                             char *host, int port, char **realhost, int nodelay,\r
686                             int keepalive)\r
687 {\r
688     int slavefd;\r
689     pid_t pid, pgrp;\r
690 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */\r
691     long windowid;\r
692 #endif\r
693     Pty pty;\r
694 \r
695     if (single_pty) {\r
696         pty = single_pty;\r
697     } else {\r
698         pty = snew(struct pty_tag);\r
699         pty->master_fd = pty->slave_fd = -1;\r
700 #ifndef OMIT_UTMP\r
701         pty_stamped_utmp = FALSE;\r
702 #endif\r
703     }\r
704 \r
705     pty->frontend = frontend;\r
706     *backend_handle = NULL;            /* we can't sensibly use this, sadly */\r
707 \r
708     pty->cfg = *cfg;                   /* structure copy */\r
709     pty->term_width = cfg->width;\r
710     pty->term_height = cfg->height;\r
711 \r
712     if (pty->master_fd < 0)\r
713         pty_open_master(pty);\r
714 \r
715     /*\r
716      * Set the backspace character to be whichever of ^H and ^? is\r
717      * specified by bksp_is_delete.\r
718      */\r
719     {\r
720         struct termios attrs;\r
721         tcgetattr(pty->master_fd, &attrs);\r
722         attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010';\r
723         tcsetattr(pty->master_fd, TCSANOW, &attrs);\r
724     }\r
725 \r
726 #ifndef OMIT_UTMP\r
727     /*\r
728      * Stamp utmp (that is, tell the utmp helper process to do so),\r
729      * or not.\r
730      */\r
731     if (!cfg->stamp_utmp) {\r
732         close(pty_utmp_helper_pipe);   /* just let the child process die */\r
733         pty_utmp_helper_pipe = -1;\r
734     } else {\r
735         char *location = get_x_display(pty->frontend);\r
736         int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */\r
737         while (pos < len) {\r
738             int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);\r
739             if (ret < 0) {\r
740                 perror("pterm: writing to utmp helper process");\r
741                 close(pty_utmp_helper_pipe);   /* arrgh, just give up */\r
742                 pty_utmp_helper_pipe = -1;\r
743                 break;\r
744             }\r
745             pos += ret;\r
746         }\r
747     }\r
748 #endif\r
749 \r
750 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */\r
751     windowid = get_windowid(pty->frontend);\r
752 #endif\r
753 \r
754     /*\r
755      * Fork and execute the command.\r
756      */\r
757     pid = fork();\r
758     if (pid < 0) {\r
759         perror("fork");\r
760         exit(1);\r
761     }\r
762 \r
763     if (pid == 0) {\r
764         /*\r
765          * We are the child.\r
766          */\r
767 \r
768         slavefd = pty_open_slave(pty);\r
769         if (slavefd < 0) {\r
770             perror("slave pty: open");\r
771             _exit(1);\r
772         }\r
773 \r
774         close(pty->master_fd);\r
775         fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */\r
776         dup2(slavefd, 0);\r
777         dup2(slavefd, 1);\r
778         dup2(slavefd, 2);\r
779         close(slavefd);\r
780         setsid();\r
781 #ifdef TIOCSCTTY\r
782         ioctl(0, TIOCSCTTY, 1);\r
783 #endif\r
784         pgrp = getpid();\r
785         tcsetpgrp(0, pgrp);\r
786         setpgid(pgrp, pgrp);\r
787         close(open(pty->name, O_WRONLY, 0));\r
788         setpgid(pgrp, pgrp);\r
789         {\r
790             char *term_env_var = dupprintf("TERM=%s", cfg->termtype);\r
791             putenv(term_env_var);\r
792             /* We mustn't free term_env_var, as putenv links it into the\r
793              * environment in place.\r
794              */\r
795         }\r
796 #ifndef NOT_X_WINDOWS                  /* for Mac OS X native compilation */\r
797         {\r
798             char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid);\r
799             putenv(windowid_env_var);\r
800             /* We mustn't free windowid_env_var, as putenv links it into the\r
801              * environment in place.\r
802              */\r
803         }\r
804 #endif\r
805         {\r
806             char *e = cfg->environmt;\r
807             char *var, *varend, *val, *varval;\r
808             while (*e) {\r
809                 var = e;\r
810                 while (*e && *e != '\t') e++;\r
811                 varend = e;\r
812                 if (*e == '\t') e++;\r
813                 val = e;\r
814                 while (*e) e++;\r
815                 e++;\r
816 \r
817                 varval = dupprintf("%.*s=%s", varend-var, var, val);\r
818                 putenv(varval);\r
819                 /*\r
820                  * We must not free varval, since putenv links it\r
821                  * into the environment _in place_. Weird, but\r
822                  * there we go. Memory usage will be rationalised\r
823                  * as soon as we exec anyway.\r
824                  */\r
825             }\r
826         }\r
827 \r
828         /*\r
829          * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by\r
830          * our parent, particularly by things like sh -c 'pterm &' and\r
831          * some window or session managers. SIGCHLD, meanwhile, was\r
832          * blocked during pt_main() startup. Reverse all this for our\r
833          * child process.\r
834          */\r
835         putty_signal(SIGINT, SIG_DFL);\r
836         putty_signal(SIGQUIT, SIG_DFL);\r
837         putty_signal(SIGPIPE, SIG_DFL);\r
838         block_signal(SIGCHLD, 0);\r
839         if (pty_argv)\r
840             execvp(pty_argv[0], pty_argv);\r
841         else {\r
842             char *shell = getenv("SHELL");\r
843             char *shellname;\r
844             if (cfg->login_shell) {\r
845                 char *p = strrchr(shell, '/');\r
846                 shellname = snewn(2+strlen(shell), char);\r
847                 p = p ? p+1 : shell;\r
848                 sprintf(shellname, "-%s", p);\r
849             } else\r
850                 shellname = shell;\r
851             execl(getenv("SHELL"), shellname, (void *)NULL);\r
852         }\r
853 \r
854         /*\r
855          * If we're here, exec has gone badly foom.\r
856          */\r
857         perror("exec");\r
858         _exit(127);\r
859     } else {\r
860         pty->child_pid = pid;\r
861         pty->child_dead = FALSE;\r
862         pty->finished = FALSE;\r
863         if (pty->slave_fd > 0)\r
864             close(pty->slave_fd);\r
865         if (!ptys_by_pid)\r
866             ptys_by_pid = newtree234(pty_compare_by_pid);\r
867         add234(ptys_by_pid, pty);\r
868     }\r
869 \r
870     if (pty_signal_pipe[0] < 0) {\r
871         if (pipe(pty_signal_pipe) < 0) {\r
872             perror("pipe");\r
873             exit(1);\r
874         }\r
875         cloexec(pty_signal_pipe[0]);\r
876         cloexec(pty_signal_pipe[1]);\r
877     }\r
878     pty_uxsel_setup(pty);\r
879 \r
880     *backend_handle = pty;\r
881 \r
882     *realhost = dupprintf("\0");\r
883 \r
884     return NULL;\r
885 }\r
886 \r
887 static void pty_reconfig(void *handle, Config *cfg)\r
888 {\r
889     Pty pty = (Pty)handle;\r
890     /*\r
891      * We don't have much need to reconfigure this backend, but\r
892      * unfortunately we do need to pick up the setting of Close On\r
893      * Exit so we know whether to give a `terminated' message.\r
894      */\r
895     pty->cfg = *cfg;                   /* structure copy */\r
896 }\r
897 \r
898 /*\r
899  * Stub routine (never called in pterm).\r
900  */\r
901 static void pty_free(void *handle)\r
902 {\r
903     Pty pty = (Pty)handle;\r
904 \r
905     /* Either of these may fail `not found'. That's fine with us. */\r
906     del234(ptys_by_pid, pty);\r
907     del234(ptys_by_fd, pty);\r
908 \r
909     sfree(pty);\r
910 }\r
911 \r
912 static void pty_try_write(Pty pty)\r
913 {\r
914     void *data;\r
915     int len, ret;\r
916 \r
917     assert(pty->master_fd >= 0);\r
918 \r
919     while (bufchain_size(&pty->output_data) > 0) {\r
920         bufchain_prefix(&pty->output_data, &data, &len);\r
921         ret = write(pty->master_fd, data, len);\r
922 \r
923         if (ret < 0 && (errno == EWOULDBLOCK)) {\r
924             /*\r
925              * We've sent all we can for the moment.\r
926              */\r
927             break;\r
928         }\r
929         if (ret < 0) {\r
930             perror("write pty master");\r
931             exit(1);\r
932         }\r
933         bufchain_consume(&pty->output_data, ret);\r
934     }\r
935 \r
936     pty_uxsel_setup(pty);\r
937 }\r
938 \r
939 /*\r
940  * Called to send data down the pty.\r
941  */\r
942 static int pty_send(void *handle, char *buf, int len)\r
943 {\r
944     Pty pty = (Pty)handle;\r
945 \r
946     if (pty->master_fd < 0)\r
947         return 0;                      /* ignore all writes if fd closed */\r
948 \r
949     bufchain_add(&pty->output_data, buf, len);\r
950     pty_try_write(pty);\r
951 \r
952     return bufchain_size(&pty->output_data);\r
953 }\r
954 \r
955 static void pty_close(Pty pty)\r
956 {\r
957     if (pty->master_fd >= 0) {\r
958         close(pty->master_fd);\r
959         pty->master_fd = -1;\r
960     }\r
961 #ifndef OMIT_UTMP\r
962     if (pty_utmp_helper_pipe >= 0) {\r
963         close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */\r
964         pty_utmp_helper_pipe = -1;\r
965     }\r
966 #endif\r
967 }\r
968 \r
969 /*\r
970  * Called to query the current socket sendability status.\r
971  */\r
972 static int pty_sendbuffer(void *handle)\r
973 {\r
974     /* Pty pty = (Pty)handle; */\r
975     return 0;\r
976 }\r
977 \r
978 /*\r
979  * Called to set the size of the window\r
980  */\r
981 static void pty_size(void *handle, int width, int height)\r
982 {\r
983     Pty pty = (Pty)handle;\r
984     struct winsize size;\r
985 \r
986     pty->term_width = width;\r
987     pty->term_height = height;\r
988 \r
989     size.ws_row = (unsigned short)pty->term_height;\r
990     size.ws_col = (unsigned short)pty->term_width;\r
991     size.ws_xpixel = (unsigned short) pty->term_width *\r
992         font_dimension(pty->frontend, 0);\r
993     size.ws_ypixel = (unsigned short) pty->term_height *\r
994         font_dimension(pty->frontend, 1);\r
995     ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size);\r
996     return;\r
997 }\r
998 \r
999 /*\r
1000  * Send special codes.\r
1001  */\r
1002 static void pty_special(void *handle, Telnet_Special code)\r
1003 {\r
1004     /* Pty pty = (Pty)handle; */\r
1005     /* Do nothing! */\r
1006     return;\r
1007 }\r
1008 \r
1009 /*\r
1010  * Return a list of the special codes that make sense in this\r
1011  * protocol.\r
1012  */\r
1013 static const struct telnet_special *pty_get_specials(void *handle)\r
1014 {\r
1015     /* Pty pty = (Pty)handle; */\r
1016     /*\r
1017      * Hmm. When I get round to having this actually usable, it\r
1018      * might be quite nice to have the ability to deliver a few\r
1019      * well chosen signals to the child process - SIGINT, SIGTERM,\r
1020      * SIGKILL at least.\r
1021      */\r
1022     return NULL;\r
1023 }\r
1024 \r
1025 static int pty_connected(void *handle)\r
1026 {\r
1027     /* Pty pty = (Pty)handle; */\r
1028     return TRUE;\r
1029 }\r
1030 \r
1031 static int pty_sendok(void *handle)\r
1032 {\r
1033     /* Pty pty = (Pty)handle; */\r
1034     return 1;\r
1035 }\r
1036 \r
1037 static void pty_unthrottle(void *handle, int backlog)\r
1038 {\r
1039     /* Pty pty = (Pty)handle; */\r
1040     /* do nothing */\r
1041 }\r
1042 \r
1043 static int pty_ldisc(void *handle, int option)\r
1044 {\r
1045     /* Pty pty = (Pty)handle; */\r
1046     return 0;                          /* neither editing nor echoing */\r
1047 }\r
1048 \r
1049 static void pty_provide_ldisc(void *handle, void *ldisc)\r
1050 {\r
1051     /* Pty pty = (Pty)handle; */\r
1052     /* This is a stub. */\r
1053 }\r
1054 \r
1055 static void pty_provide_logctx(void *handle, void *logctx)\r
1056 {\r
1057     /* Pty pty = (Pty)handle; */\r
1058     /* This is a stub. */\r
1059 }\r
1060 \r
1061 static int pty_exitcode(void *handle)\r
1062 {\r
1063     Pty pty = (Pty)handle;\r
1064     if (!pty->finished)\r
1065         return -1;                     /* not dead yet */\r
1066     else\r
1067         return pty->exit_code;\r
1068 }\r
1069 \r
1070 static int pty_cfg_info(void *handle)\r
1071 {\r
1072     /* Pty pty = (Pty)handle; */\r
1073     return 0;\r
1074 }\r
1075 \r
1076 Backend pty_backend = {\r
1077     pty_init,\r
1078     pty_free,\r
1079     pty_reconfig,\r
1080     pty_send,\r
1081     pty_sendbuffer,\r
1082     pty_size,\r
1083     pty_special,\r
1084     pty_get_specials,\r
1085     pty_connected,\r
1086     pty_exitcode,\r
1087     pty_sendok,\r
1088     pty_ldisc,\r
1089     pty_provide_ldisc,\r
1090     pty_provide_logctx,\r
1091     pty_unthrottle,\r
1092     pty_cfg_info,\r
1093     "pty",\r
1094     -1,\r
1095     0\r
1096 };\r