]> git.ozlabs.org Git - ppp.git/blob - pppd/tty.c
Patches from Frank Cusack:
[ppp.git] / pppd / tty.c
1 /*
2  * tty.c - code for handling serial ports in pppd.
3  *
4  * Copyright (C) 2000 Paul Mackerras.
5  * All rights reserved.
6  *
7  * Portions Copyright (c) 1989 Carnegie Mellon University.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that the above copyright notice and this paragraph are
12  * duplicated in all such forms and that any documentation,
13  * advertising materials, and other materials related to such
14  * distribution and use acknowledge that the software was developed
15  * by Carnegie Mellon University.  The name of the
16  * University may not be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21  */
22
23 #define RCSID   "$Id: tty.c,v 1.6 2001/03/12 22:59:01 paulus Exp $"
24
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <syslog.h>
34 #include <netdb.h>
35 #include <utmp.h>
36 #include <pwd.h>
37 #include <setjmp.h>
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #include <sys/stat.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include "pppd.h"
49 #include "fsm.h"
50 #include "lcp.h"
51
52 void tty_process_extra_options __P((void));
53 void tty_check_options __P((void));
54 int  connect_tty __P((void));
55 void disconnect_tty __P((void));
56 void tty_close_fds __P((void));
57 void cleanup_tty __P((void));
58 void tty_do_send_config __P((int, u_int32_t, int, int));
59
60 static int setdevname __P((char *, char **, int));
61 static int setspeed __P((char *, char **, int));
62 static int setxonxoff __P((char **));
63 static int setescape __P((char **));
64 static void printescape __P((option_t *, void (*)(void *, char *,...),void *));
65 static void finish_tty __P((void));
66 static int start_charshunt __P((int, int));
67 static void stop_charshunt __P((void *, int));
68 static void charshunt_done __P((void *));
69 static void charshunt __P((int, int, char *));
70 static int record_write __P((FILE *, int code, u_char *buf, int nb,
71                              struct timeval *));
72 static int open_socket __P((char *));
73 static void maybe_relock __P((void *, int));
74
75 static int pty_master;          /* fd for master side of pty */
76 static int pty_slave;           /* fd for slave side of pty */
77 static int real_ttyfd;          /* fd for actual serial port (not pty) */
78 static int ttyfd;               /* Serial port file descriptor */
79 static char speed_str[16];      /* Serial port speed as string */
80
81 mode_t tty_mode = (mode_t)-1;   /* Original access permissions to tty */
82 int baud_rate;                  /* Actual bits/second for serial device */
83 char *callback_script;          /* script for doing callback */
84 int charshunt_pid;              /* Process ID for charshunt */
85 int locked;                     /* lock() has succeeded */
86 struct stat devstat;            /* result of stat() on devnam */
87
88 /* option variables */
89 int     crtscts = 0;            /* Use hardware flow control */
90 bool    modem = 1;              /* Use modem control lines */
91 int     inspeed = 0;            /* Input/Output speed requested */
92 bool    lockflag = 0;           /* Create lock file to lock the serial dev */
93 char    *initializer = NULL;    /* Script to initialize physical link */
94 char    *connect_script = NULL; /* Script to establish physical link */
95 char    *disconnect_script = NULL; /* Script to disestablish physical link */
96 char    *welcomer = NULL;       /* Script to run after phys link estab. */
97 char    *ptycommand = NULL;     /* Command to run on other side of pty */
98 bool    notty = 0;              /* Stdin/out is not a tty */
99 char    *record_file = NULL;    /* File to record chars sent/received */
100 int     max_data_rate;          /* max bytes/sec through charshunt */
101 bool    sync_serial = 0;        /* Device is synchronous serial device */
102 char    *pty_socket = NULL;     /* Socket to connect to pty */
103 int     using_pty = 0;          /* we're allocating a pty as the device */
104
105 extern uid_t uid;
106 extern int kill_link;
107
108 /* XXX */
109 extern int privopen;            /* don't lock, open device as root */
110
111 u_int32_t xmit_accm[8];         /* extended transmit ACCM */
112
113 /* option descriptors */
114 option_t tty_options[] = {
115     /* device name must be first, or change connect_tty() below! */
116     { "device name", o_wild, (void *) &setdevname,
117       "Serial port device name",
118       OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
119       devnam},
120
121     { "tty speed", o_wild, (void *) &setspeed,
122       "Baud rate for serial port",
123       OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },
124
125     { "lock", o_bool, &lockflag,
126       "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },
127     { "nolock", o_bool, &lockflag,
128       "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },
129
130     { "init", o_string, &initializer,
131       "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },
132
133     { "connect", o_string, &connect_script,
134       "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },
135
136     { "disconnect", o_string, &disconnect_script,
137       "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },
138
139     { "welcome", o_string, &welcomer,
140       "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },
141
142     { "pty", o_string, &ptycommand,
143       "Script to run on pseudo-tty master side",
144       OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },
145
146     { "notty", o_bool, &notty,
147       "Input/output is not a tty", OPT_DEVNAM | 1 },
148
149     { "socket", o_string, &pty_socket,
150       "Send and receive over socket, arg is host:port",
151       OPT_PRIO | OPT_DEVNAM },
152
153     { "record", o_string, &record_file,
154       "Record characters sent/received to file", OPT_PRIO },
155
156     { "crtscts", o_int, &crtscts,
157       "Set hardware (RTS/CTS) flow control",
158       OPT_PRIO | OPT_NOARG | OPT_VAL(1) },
159     { "cdtrcts", o_int, &crtscts,
160       "Set alternate hardware (DTR/CTS) flow control",
161       OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },
162     { "nocrtscts", o_int, &crtscts,
163       "Disable hardware flow control",
164       OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
165     { "-crtscts", o_int, &crtscts,
166       "Disable hardware flow control",
167       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
168     { "nocdtrcts", o_int, &crtscts,
169       "Disable hardware flow control",
170       OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
171     { "xonxoff", o_special_noarg, (void *)setxonxoff,
172       "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
173
174     { "modem", o_bool, &modem,
175       "Use modem control lines", OPT_PRIO | 1 },
176     { "local", o_bool, &modem,
177       "Don't use modem control lines", OPT_PRIOSUB | 0 },
178
179     { "sync", o_bool, &sync_serial,
180       "Use synchronous HDLC serial encoding", 1 },
181
182     { "datarate", o_int, &max_data_rate,
183       "Maximum data rate in bytes/sec (with pty, notty or record option)",
184       OPT_PRIO },
185
186     { "escape", o_special, (void *)setescape,
187       "List of character codes to escape on transmission",
188       OPT_A2PRINTER, (void *)printescape },
189
190     { NULL }
191 };
192
193
194 struct channel tty_channel = {
195         tty_options,
196         &tty_process_extra_options,
197         &tty_check_options,
198         &connect_tty,
199         &disconnect_tty,
200         &tty_establish_ppp,
201         &tty_disestablish_ppp,
202         &tty_do_send_config,
203         &tty_recv_config,
204         &cleanup_tty,
205         &tty_close_fds
206 };
207
208 /*
209  * setspeed - Set the serial port baud rate.
210  * If doit is 0, the call is to check whether this option is
211  * potentially a speed value.
212  */
213 static int
214 setspeed(arg, argv, doit)
215     char *arg;
216     char **argv;
217     int doit;
218 {
219         char *ptr;
220         int spd;
221
222         spd = strtol(arg, &ptr, 0);
223         if (ptr == arg || *ptr != 0 || spd == 0)
224                 return 0;
225         if (doit) {
226                 inspeed = spd;
227                 slprintf(speed_str, sizeof(speed_str), "%d", spd);
228         }
229         return 1;
230 }
231
232
233 /*
234  * setdevname - Set the device name.
235  * If doit is 0, the call is to check whether this option is
236  * potentially a device name.
237  */
238 static int
239 setdevname(cp, argv, doit)
240     char *cp;
241     char **argv;
242     int doit;
243 {
244         struct stat statbuf;
245         char dev[MAXPATHLEN];
246
247         if (*cp == 0)
248                 return 0;
249
250         if (strncmp("/dev/", cp, 5) != 0) {
251                 strlcpy(dev, "/dev/", sizeof(dev));
252                 strlcat(dev, cp, sizeof(dev));
253                 cp = dev;
254         }
255
256         /*
257          * Check if there is a character device by this name.
258          */
259         if (stat(cp, &statbuf) < 0) {
260                 if (!doit)
261                         return errno != ENOENT;
262                 option_error("Couldn't stat %s: %m", cp);
263                 return 0;
264         }
265         if (!S_ISCHR(statbuf.st_mode)) {
266                 if (doit)
267                         option_error("%s is not a character device", cp);
268                 return 0;
269         }
270
271         if (doit) {
272                 strlcpy(devnam, cp, sizeof(devnam));
273                 devstat = statbuf;
274                 default_device = 0;
275         }
276   
277         return 1;
278 }
279
280 static int
281 setxonxoff(argv)
282     char **argv;
283 {
284         lcp_wantoptions[0].asyncmap |= 0x000A0000;      /* escape ^S and ^Q */
285         lcp_wantoptions[0].neg_asyncmap = 1;
286
287         crtscts = -2;
288         return 1;
289 }
290
291 /*
292  * setescape - add chars to the set we escape on transmission.
293  */
294 static int
295 setescape(argv)
296     char **argv;
297 {
298     int n, ret;
299     char *p, *endp;
300
301     p = *argv;
302     ret = 1;
303     while (*p) {
304         n = strtol(p, &endp, 16);
305         if (p == endp) {
306             option_error("escape parameter contains invalid hex number '%s'",
307                          p);
308             return 0;
309         }
310         p = endp;
311         if (n < 0 || n == 0x5E || n > 0xFF) {
312             option_error("can't escape character 0x%x", n);
313             ret = 0;
314         } else
315             xmit_accm[n >> 5] |= 1 << (n & 0x1F);
316         while (*p == ',' || *p == ' ')
317             ++p;
318     }
319     lcp_allowoptions[0].asyncmap = xmit_accm[0];
320     return ret;
321 }
322
323 static void
324 printescape(opt, printer, arg)
325     option_t *opt;
326     void (*printer) __P((void *, char *, ...));
327     void *arg;
328 {
329         int n;
330         int first = 1;
331
332         for (n = 0; n < 256; ++n) {
333                 if (n == 0x7d)
334                         n += 2;         /* skip 7d, 7e */
335                 if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {
336                         if (!first)
337                                 printer(arg, ",");
338                         else
339                                 first = 0;
340                         printer(arg, "%x", n);
341                 }
342         }
343         if (first)
344                 printer(arg, "oops # nothing escaped");
345 }
346
347 /*
348  * tty_init - do various tty-related initializations.
349  */
350 void tty_init()
351 {
352     add_notifier(&pidchange, maybe_relock, 0);
353     the_channel = &tty_channel;
354     xmit_accm[3] = 0x60000000;
355 }
356
357 /*
358  * tty_process_extra_options - work out which tty device we are using
359  * and read its options file.
360  */
361 void tty_process_extra_options()
362 {
363         using_pty = notty || ptycommand != NULL || pty_socket != NULL;
364         if (using_pty)
365                 return;
366         if (default_device) {
367                 char *p;
368                 if (!isatty(0) || (p = ttyname(0)) == NULL) {
369                         option_error("no device specified and stdin is not a tty");
370                         exit(EXIT_OPTION_ERROR);
371                 }
372                 strlcpy(devnam, p, sizeof(devnam));
373                 if (stat(devnam, &devstat) < 0)
374                         fatal("Couldn't stat default device %s: %m", devnam);
375         }
376
377
378         /*
379          * Parse the tty options file.
380          * The per-tty options file should not change
381          * ptycommand, pty_socket, notty or devnam.
382          * options_for_tty doesn't override options set on the command line,
383          * except for some privileged options.
384          */
385         if (!options_for_tty())
386                 exit(EXIT_OPTION_ERROR);
387 }
388
389 /*
390  * tty_check_options - do consistency checks on the options we were given.
391  */
392 void
393 tty_check_options()
394 {
395         struct stat statbuf;
396         int fdflags;
397
398         if (demand && connect_script == 0) {
399                 option_error("connect script is required for demand-dialling\n");
400                 exit(EXIT_OPTION_ERROR);
401         }
402         /* default holdoff to 0 if no connect script has been given */
403         if (connect_script == 0 && !holdoff_specified)
404                 holdoff = 0;
405
406         if (using_pty) {
407                 if (!default_device) {
408                         option_error("%s option precludes specifying device name",
409                                      notty? "notty": "pty");
410                         exit(EXIT_OPTION_ERROR);
411                 }
412                 if (ptycommand != NULL && notty) {
413                         option_error("pty option is incompatible with notty option");
414                         exit(EXIT_OPTION_ERROR);
415                 }
416                 if (pty_socket != NULL && (ptycommand != NULL || notty)) {
417                         option_error("socket option is incompatible with pty and notty");
418                         exit(EXIT_OPTION_ERROR);
419                 }
420                 default_device = notty;
421                 lockflag = 0;
422                 modem = 0;
423                 if (notty && log_to_fd <= 1)
424                         log_to_fd = -1;
425         } else {
426                 /*
427                  * If the user has specified a device which is the same as
428                  * the one on stdin, pretend they didn't specify any.
429                  * If the device is already open read/write on stdin,
430                  * we assume we don't need to lock it, and we can open it
431                  * as root.
432                  */
433                 if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
434                     && statbuf.st_rdev == devstat.st_rdev) {
435                         default_device = 1;
436                         fdflags = fcntl(0, F_GETFL);
437                         if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
438                                 privopen = 1;
439                 }
440         }
441         if (default_device)
442                 nodetach = 1;
443
444         /*
445          * Don't send log messages to the serial port, it tends to
446          * confuse the peer. :-)
447          */
448         if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
449             && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
450                 log_to_fd = -1;
451 }
452
453 /*
454  * connect_tty - get the serial port ready to start doing PPP.
455  * That is, open the serial port, set its speed and mode, and run
456  * the connector and/or welcomer.
457  */
458 int connect_tty()
459 {
460         char *connector;
461         int fdflags;
462         struct stat statbuf;
463         char numbuf[16];
464
465         /*
466          * Get a pty master/slave pair if the pty, notty, socket,
467          * or record options were specified.
468          */
469         strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
470         pty_master = -1;
471         pty_slave = -1;
472         real_ttyfd = -1;
473         if (using_pty || record_file != NULL) {
474                 if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
475                         error("Couldn't allocate pseudo-tty");
476                         status = EXIT_FATAL_ERROR;
477                         return -1;
478                 }
479                 set_up_tty(pty_slave, 1);
480         }
481
482         /*
483          * Lock the device if we've been asked to.
484          */
485         status = EXIT_LOCK_FAILED;
486         if (lockflag && !privopen) {
487                 if (lock(devnam) < 0)
488                         return -1;
489                 locked = 1;
490         }
491
492         /*
493          * Open the serial device and set it up to be the ppp interface.
494          * First we open it in non-blocking mode so we can set the
495          * various termios flags appropriately.  If we aren't dialling
496          * out and we want to use the modem lines, we reopen it later
497          * in order to wait for the carrier detect signal from the modem.
498          */
499         hungup = 0;
500         kill_link = 0;
501         connector = doing_callback? callback_script: connect_script;
502         if (devnam[0] != 0) {
503                 for (;;) {
504                         /* If the user specified the device name, become the
505                            user before opening it. */
506                         int err, prio;
507
508                         prio = privopen? OPRIO_ROOT: tty_options[0].priority;
509                         if (prio < OPRIO_ROOT)
510                                 seteuid(uid);
511                         ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
512                         err = errno;
513                         if (prio < OPRIO_ROOT)
514                                 seteuid(0);
515                         if (ttyfd >= 0)
516                                 break;
517                         errno = err;
518                         if (err != EINTR) {
519                                 error("Failed to open %s: %m", devnam);
520                                 status = EXIT_OPEN_FAILED;
521                         }
522                         if (!persist || err != EINTR)
523                                 return -1;
524                 }
525                 real_ttyfd = ttyfd;
526                 if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
527                     || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
528                         warn("Couldn't reset non-blocking mode on device: %m");
529
530                 /*
531                  * Do the equivalent of `mesg n' to stop broadcast messages.
532                  */
533                 if (fstat(ttyfd, &statbuf) < 0
534                     || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
535                         warn("Couldn't restrict write permissions to %s: %m", devnam);
536                 } else
537                         tty_mode = statbuf.st_mode;
538
539                 /*
540                  * Set line speed, flow control, etc.
541                  * If we have a non-null connection or initializer script,
542                  * on most systems we set CLOCAL for now so that we can talk
543                  * to the modem before carrier comes up.  But this has the
544                  * side effect that we might miss it if CD drops before we
545                  * get to clear CLOCAL below.  On systems where we can talk
546                  * successfully to the modem with CLOCAL clear and CD down,
547                  * we could clear CLOCAL at this point.
548                  */
549                 set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
550                                    || initializer != NULL));
551         }
552
553         /*
554          * If the pty, socket, notty and/or record option was specified,
555          * start up the character shunt now.
556          */
557         status = EXIT_PTYCMD_FAILED;
558         if (ptycommand != NULL) {
559                 if (record_file != NULL) {
560                         int ipipe[2], opipe[2], ok;
561
562                         if (pipe(ipipe) < 0 || pipe(opipe) < 0)
563                                 fatal("Couldn't create pipes for record option: %m");
564                         ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
565                                 && start_charshunt(ipipe[0], opipe[1]);
566                         close(ipipe[0]);
567                         close(ipipe[1]);
568                         close(opipe[0]);
569                         close(opipe[1]);
570                         if (!ok)
571                                 return -1;
572                 } else {
573                         if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
574                                 return -1;
575                         ttyfd = pty_slave;
576                         close(pty_master);
577                         pty_master = -1;
578                 }
579         } else if (pty_socket != NULL) {
580                 int fd = open_socket(pty_socket);
581                 if (fd < 0)
582                         return -1;
583                 if (!start_charshunt(fd, fd))
584                         return -1;
585         } else if (notty) {
586                 if (!start_charshunt(0, 1))
587                         return -1;
588         } else if (record_file != NULL) {
589                 if (!start_charshunt(ttyfd, ttyfd))
590                         return -1;
591         }
592
593         /* run connection script */
594         if ((connector && connector[0]) || initializer) {
595                 if (real_ttyfd != -1) {
596                         /* XXX do this if doing_callback == CALLBACK_DIALIN? */
597                         if (!default_device && modem) {
598                                 setdtr(real_ttyfd, 0);  /* in case modem is off hook */
599                                 sleep(1);
600                                 setdtr(real_ttyfd, 1);
601                         }
602                 }
603
604                 if (initializer && initializer[0]) {
605                         if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
606                                 error("Initializer script failed");
607                                 status = EXIT_INIT_FAILED;
608                                 return -1;
609                         }
610                         if (kill_link) {
611                                 disconnect_tty();
612                                 return -1;
613                         }
614                         info("Serial port initialized.");
615                 }
616
617                 if (connector && connector[0]) {
618                         if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
619                                 error("Connect script failed");
620                                 status = EXIT_CONNECT_FAILED;
621                                 return -1;
622                         }
623                         if (kill_link) {
624                                 disconnect_tty();
625                                 return -1;
626                         }
627                         info("Serial connection established.");
628                 }
629
630                 /* set line speed, flow control, etc.;
631                    clear CLOCAL if modem option */
632                 if (real_ttyfd != -1)
633                         set_up_tty(real_ttyfd, 0);
634
635                 if (doing_callback == CALLBACK_DIALIN)
636                         connector = NULL;
637         }
638
639         /* reopen tty if necessary to wait for carrier */
640         if (connector == NULL && modem && devnam[0] != 0) {
641                 int i;
642                 for (;;) {
643                         if ((i = open(devnam, O_RDWR)) >= 0)
644                                 break;
645                         if (errno != EINTR) {
646                                 error("Failed to reopen %s: %m", devnam);
647                                 status = EXIT_OPEN_FAILED;
648                         }
649                         if (!persist || errno != EINTR || hungup || kill_link)
650                                 return -1;
651                 }
652                 close(i);
653         }
654
655         slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
656         script_setenv("SPEED", numbuf, 0);
657
658         /* run welcome script, if any */
659         if (welcomer && welcomer[0]) {
660                 if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
661                         warn("Welcome script failed");
662         }
663
664         /*
665          * If we are initiating this connection, wait for a short
666          * time for something from the peer.  This can avoid bouncing
667          * our packets off his tty before he has it set up.
668          */
669         if (connector != NULL || ptycommand != NULL)
670                 listen_time = connect_delay;
671
672         return ttyfd;
673 }
674
675
676 void disconnect_tty()
677 {
678         if (disconnect_script == NULL || hungup)
679                 return;
680         if (real_ttyfd >= 0)
681                 set_up_tty(real_ttyfd, 1);
682         if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
683                 warn("disconnect script failed");
684         } else {
685                 info("Serial link disconnected.");
686         }
687 }
688
689 void tty_close_fds()
690 {
691         if (pty_master >= 0)
692                 close(pty_master);
693         if (pty_slave >= 0)
694                 close(pty_slave);
695         if (real_ttyfd >= 0) {
696                 close(real_ttyfd);
697                 real_ttyfd = -1;
698         }
699         /* N.B. ttyfd will == either pty_slave or real_ttyfd */
700 }
701
702 void cleanup_tty()
703 {
704         if (real_ttyfd >= 0)
705                 finish_tty();
706         tty_close_fds();
707         if (locked) {
708                 unlock();
709                 locked = 0;
710         }
711 }
712
713 /*
714  * tty_do_send_config - set transmit-side PPP configuration.
715  * We set the extended transmit ACCM here as well.
716  */
717 void
718 tty_do_send_config(mtu, accm, pcomp, accomp)
719     int mtu;
720     u_int32_t accm;
721     int pcomp, accomp;
722 {
723         tty_set_xaccm(xmit_accm);
724         tty_send_config(mtu, accm, pcomp, accomp);
725 }
726
727 /*
728  * finish_tty - restore the terminal device to its original settings
729  */
730 static void
731 finish_tty()
732 {
733         /* drop dtr to hang up */
734         if (!default_device && modem) {
735                 setdtr(real_ttyfd, 0);
736                 /*
737                  * This sleep is in case the serial port has CLOCAL set by default,
738                  * and consequently will reassert DTR when we close the device.
739                  */
740                 sleep(1);
741         }
742
743         restore_tty(real_ttyfd);
744
745         if (tty_mode != (mode_t) -1) {
746                 if (fchmod(real_ttyfd, tty_mode) != 0) {
747                         /* XXX if devnam is a symlink, this will change the link */
748                         chmod(devnam, tty_mode);
749                 }
750         }
751
752         close(real_ttyfd);
753         real_ttyfd = -1;
754 }
755
756 /*
757  * maybe_relock - our PID has changed, maybe update the lock file.
758  */
759 static void
760 maybe_relock(arg, pid)
761     void *arg;
762     int pid;
763 {
764     if (locked)
765         relock(pid);
766 }
767
768 /*
769  * open_socket - establish a stream socket connection to the nominated
770  * host and port.
771  */
772 static int
773 open_socket(dest)
774     char *dest;
775 {
776     char *sep, *endp = NULL;
777     int sock, port = -1;
778     u_int32_t host;
779     struct hostent *hent;
780     struct sockaddr_in sad;
781
782     /* parse host:port and resolve host to an IP address */
783     sep = strchr(dest, ':');
784     if (sep != NULL)
785         port = strtol(sep+1, &endp, 10);
786     if (port < 0 || endp == sep+1 || sep == dest) {
787         error("Can't parse host:port for socket destination");
788         return -1;
789     }
790     *sep = 0;
791     host = inet_addr(dest);
792     if (host == (u_int32_t) -1) {
793         hent = gethostbyname(dest);
794         if (hent == NULL) {
795             error("%s: unknown host in socket option", dest);
796             *sep = ':';
797             return -1;
798         }
799         host = *(u_int32_t *)(hent->h_addr_list[0]);
800     }
801     *sep = ':';
802
803     /* get a socket and connect it to the other end */
804     sock = socket(PF_INET, SOCK_STREAM, 0);
805     if (sock < 0) {
806         error("Can't create socket: %m");
807         return -1;
808     }
809     memset(&sad, 0, sizeof(sad));
810     sad.sin_family = AF_INET;
811     sad.sin_port = htons(port);
812     sad.sin_addr.s_addr = host;
813     if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
814         error("Can't connect to %s: %m", dest);
815         close(sock);
816         return -1;
817     }
818
819     return sock;
820 }
821
822
823 /*
824  * start_charshunt - create a child process to run the character shunt.
825  */
826 static int
827 start_charshunt(ifd, ofd)
828     int ifd, ofd;
829 {
830     int cpid;
831
832     cpid = fork();
833     if (cpid == -1) {
834         error("Can't fork process for character shunt: %m");
835         return 0;
836     }
837     if (cpid == 0) {
838         /* child */
839         close(pty_slave);
840         setuid(uid);
841         if (getuid() != uid)
842             fatal("setuid failed");
843         setgid(getgid());
844         if (!nodetach)
845             log_to_fd = -1;
846         charshunt(ifd, ofd, record_file);
847         exit(0);
848     }
849     charshunt_pid = cpid;
850     add_notifier(&sigreceived, stop_charshunt, 0);
851     close(pty_master);
852     pty_master = -1;
853     ttyfd = pty_slave;
854     record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
855     return 1;
856 }
857
858 static void
859 charshunt_done(arg)
860     void *arg;
861 {
862         charshunt_pid = 0;
863 }
864
865 static void
866 stop_charshunt(arg, sig)
867     void *arg;
868     int sig;
869 {
870         if (charshunt_pid)
871                 kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
872 }
873
874 /*
875  * charshunt - the character shunt, which passes characters between
876  * the pty master side and the serial port (or stdin/stdout).
877  * This runs as the user (not as root).
878  * (We assume ofd >= ifd which is true the way this gets called. :-).
879  */
880 static void
881 charshunt(ifd, ofd, record_file)
882     int ifd, ofd;
883     char *record_file;
884 {
885     int n, nfds;
886     fd_set ready, writey;
887     u_char *ibufp, *obufp;
888     int nibuf, nobuf;
889     int flags;
890     int pty_readable, stdin_readable;
891     struct timeval lasttime;
892     FILE *recordf = NULL;
893     int ilevel, olevel, max_level;
894     struct timeval levelt, tout, *top;
895     extern u_char inpacket_buf[];
896
897     /*
898      * Reset signal handlers.
899      */
900     signal(SIGHUP, SIG_IGN);            /* Hangup */
901     signal(SIGINT, SIG_DFL);            /* Interrupt */
902     signal(SIGTERM, SIG_DFL);           /* Terminate */
903     signal(SIGCHLD, SIG_DFL);
904     signal(SIGUSR1, SIG_DFL);
905     signal(SIGUSR2, SIG_DFL);
906     signal(SIGABRT, SIG_DFL);
907     signal(SIGALRM, SIG_DFL);
908     signal(SIGFPE, SIG_DFL);
909     signal(SIGILL, SIG_DFL);
910     signal(SIGPIPE, SIG_DFL);
911     signal(SIGQUIT, SIG_DFL);
912     signal(SIGSEGV, SIG_DFL);
913 #ifdef SIGBUS
914     signal(SIGBUS, SIG_DFL);
915 #endif
916 #ifdef SIGEMT
917     signal(SIGEMT, SIG_DFL);
918 #endif
919 #ifdef SIGPOLL
920     signal(SIGPOLL, SIG_DFL);
921 #endif
922 #ifdef SIGPROF
923     signal(SIGPROF, SIG_DFL);
924 #endif
925 #ifdef SIGSYS
926     signal(SIGSYS, SIG_DFL);
927 #endif
928 #ifdef SIGTRAP
929     signal(SIGTRAP, SIG_DFL);
930 #endif
931 #ifdef SIGVTALRM
932     signal(SIGVTALRM, SIG_DFL);
933 #endif
934 #ifdef SIGXCPU
935     signal(SIGXCPU, SIG_DFL);
936 #endif
937 #ifdef SIGXFSZ
938     signal(SIGXFSZ, SIG_DFL);
939 #endif
940
941     /*
942      * Open the record file if required.
943      */
944     if (record_file != NULL) {
945         recordf = fopen(record_file, "a");
946         if (recordf == NULL)
947             error("Couldn't create record file %s: %m", record_file);
948     }
949
950     /* set all the fds to non-blocking mode */
951     flags = fcntl(pty_master, F_GETFL);
952     if (flags == -1
953         || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
954         warn("couldn't set pty master to nonblock: %m");
955     flags = fcntl(ifd, F_GETFL);
956     if (flags == -1
957         || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
958         warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
959     if (ofd != ifd) {
960         flags = fcntl(ofd, F_GETFL);
961         if (flags == -1
962             || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
963             warn("couldn't set stdout to nonblock: %m");
964     }
965
966     nibuf = nobuf = 0;
967     ibufp = obufp = NULL;
968     pty_readable = stdin_readable = 1;
969
970     ilevel = olevel = 0;
971     gettimeofday(&levelt, NULL);
972     if (max_data_rate) {
973         max_level = max_data_rate / 10;
974         if (max_level < 100)
975             max_level = 100;
976     } else
977         max_level = PPP_MRU + PPP_HDRLEN + 1;
978
979     nfds = (ofd > pty_master? ofd: pty_master) + 1;
980     if (recordf != NULL) {
981         gettimeofday(&lasttime, NULL);
982         putc(7, recordf);       /* put start marker */
983         putc(lasttime.tv_sec >> 24, recordf);
984         putc(lasttime.tv_sec >> 16, recordf);
985         putc(lasttime.tv_sec >> 8, recordf);
986         putc(lasttime.tv_sec, recordf);
987         lasttime.tv_usec = 0;
988     }
989
990     while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
991         top = 0;
992         tout.tv_sec = 0;
993         tout.tv_usec = 10000;
994         FD_ZERO(&ready);
995         FD_ZERO(&writey);
996         if (nibuf != 0) {
997             if (ilevel >= max_level)
998                 top = &tout;
999             else
1000                 FD_SET(pty_master, &writey);
1001         } else if (stdin_readable)
1002             FD_SET(ifd, &ready);
1003         if (nobuf != 0) {
1004             if (olevel >= max_level)
1005                 top = &tout;
1006             else
1007                 FD_SET(ofd, &writey);
1008         } else if (pty_readable)
1009             FD_SET(pty_master, &ready);
1010         if (select(nfds, &ready, &writey, NULL, top) < 0) {
1011             if (errno != EINTR)
1012                 fatal("select");
1013             continue;
1014         }
1015         if (max_data_rate) {
1016             double dt;
1017             int nbt;
1018             struct timeval now;
1019
1020             gettimeofday(&now, NULL);
1021             dt = (now.tv_sec - levelt.tv_sec
1022                   + (now.tv_usec - levelt.tv_usec) / 1e6);
1023             nbt = (int)(dt * max_data_rate);
1024             ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
1025             olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
1026             levelt = now;
1027         } else
1028             ilevel = olevel = 0;
1029         if (FD_ISSET(ifd, &ready)) {
1030             ibufp = inpacket_buf;
1031             nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
1032             if (nibuf < 0 && errno == EIO)
1033                 nibuf = 0;
1034             if (nibuf < 0) {
1035                 if (!(errno == EINTR || errno == EAGAIN)) {
1036                     error("Error reading standard input: %m");
1037                     break;
1038                 }
1039                 nibuf = 0;
1040             } else if (nibuf == 0) {
1041                 /* end of file from stdin */
1042                 stdin_readable = 0;
1043                 /* do a 0-length write, hopefully this will generate
1044                    an EOF (hangup) on the slave side. */
1045                 write(pty_master, inpacket_buf, 0);
1046                 if (recordf)
1047                     if (!record_write(recordf, 4, NULL, 0, &lasttime))
1048                         recordf = NULL;
1049             } else {
1050                 FD_SET(pty_master, &writey);
1051                 if (recordf)
1052                     if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
1053                         recordf = NULL;
1054             }
1055         }
1056         if (FD_ISSET(pty_master, &ready)) {
1057             obufp = outpacket_buf;
1058             nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
1059             if (nobuf < 0 && errno == EIO)
1060                 nobuf = 0;
1061             if (nobuf < 0) {
1062                 if (!(errno == EINTR || errno == EAGAIN)) {
1063                     error("Error reading pseudo-tty master: %m");
1064                     break;
1065                 }
1066                 nobuf = 0;
1067             } else if (nobuf == 0) {
1068                 /* end of file from the pty - slave side has closed */
1069                 pty_readable = 0;
1070                 stdin_readable = 0;     /* pty is not writable now */
1071                 nibuf = 0;
1072                 close(ofd);
1073                 if (recordf)
1074                     if (!record_write(recordf, 3, NULL, 0, &lasttime))
1075                         recordf = NULL;
1076             } else {
1077                 FD_SET(ofd, &writey);
1078                 if (recordf)
1079                     if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
1080                         recordf = NULL;
1081             }
1082         }
1083         if (FD_ISSET(ofd, &writey)) {
1084             n = nobuf;
1085             if (olevel + n > max_level)
1086                 n = max_level - olevel;
1087             n = write(ofd, obufp, n);
1088             if (n < 0) {
1089                 if (errno == EIO) {
1090                     pty_readable = 0;
1091                     nobuf = 0;
1092                 } else if (errno != EAGAIN && errno != EINTR) {
1093                     error("Error writing standard output: %m");
1094                     break;
1095                 }
1096             } else {
1097                 obufp += n;
1098                 nobuf -= n;
1099                 olevel += n;
1100             }
1101         }
1102         if (FD_ISSET(pty_master, &writey)) {
1103             n = nibuf;
1104             if (ilevel + n > max_level)
1105                 n = max_level - ilevel;
1106             n = write(pty_master, ibufp, n);
1107             if (n < 0) {
1108                 if (errno == EIO) {
1109                     stdin_readable = 0;
1110                     nibuf = 0;
1111                 } else if (errno != EAGAIN && errno != EINTR) {
1112                     error("Error writing pseudo-tty master: %m");
1113                     break;
1114                 }
1115             } else {
1116                 ibufp += n;
1117                 nibuf -= n;
1118                 ilevel += n;
1119             }
1120         }
1121     }
1122     exit(0);
1123 }
1124
1125 static int
1126 record_write(f, code, buf, nb, tp)
1127     FILE *f;
1128     int code;
1129     u_char *buf;
1130     int nb;
1131     struct timeval *tp;
1132 {
1133     struct timeval now;
1134     int diff;
1135
1136     gettimeofday(&now, NULL);
1137     now.tv_usec /= 100000;      /* actually 1/10 s, not usec now */
1138     diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
1139     if (diff > 0) {
1140         if (diff > 255) {
1141             putc(5, f);
1142             putc(diff >> 24, f);
1143             putc(diff >> 16, f);
1144             putc(diff >> 8, f);
1145             putc(diff, f);
1146         } else {
1147             putc(6, f);
1148             putc(diff, f);
1149         }
1150         *tp = now;
1151     }
1152     putc(code, f);
1153     if (buf != NULL) {
1154         putc(nb >> 8, f);
1155         putc(nb, f);
1156         fwrite(buf, nb, 1, f);
1157     }
1158     fflush(f);
1159     if (ferror(f)) {
1160         error("Error writing record file: %m");
1161         return 0;
1162     }
1163     return 1;
1164 }