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