]> git.ozlabs.org Git - ppp.git/blob - pppd/tty.c
typo
[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.7 2002/09/12 22:34:47 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         sys_close();
845         if (!nodetach)
846             log_to_fd = -1;
847         charshunt(ifd, ofd, record_file);
848         exit(0);
849     }
850     charshunt_pid = cpid;
851     add_notifier(&sigreceived, stop_charshunt, 0);
852     close(pty_master);
853     pty_master = -1;
854     ttyfd = pty_slave;
855     record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
856     return 1;
857 }
858
859 static void
860 charshunt_done(arg)
861     void *arg;
862 {
863         charshunt_pid = 0;
864 }
865
866 static void
867 stop_charshunt(arg, sig)
868     void *arg;
869     int sig;
870 {
871         if (charshunt_pid)
872                 kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
873 }
874
875 /*
876  * charshunt - the character shunt, which passes characters between
877  * the pty master side and the serial port (or stdin/stdout).
878  * This runs as the user (not as root).
879  * (We assume ofd >= ifd which is true the way this gets called. :-).
880  */
881 static void
882 charshunt(ifd, ofd, record_file)
883     int ifd, ofd;
884     char *record_file;
885 {
886     int n, nfds;
887     fd_set ready, writey;
888     u_char *ibufp, *obufp;
889     int nibuf, nobuf;
890     int flags;
891     int pty_readable, stdin_readable;
892     struct timeval lasttime;
893     FILE *recordf = NULL;
894     int ilevel, olevel, max_level;
895     struct timeval levelt, tout, *top;
896     extern u_char inpacket_buf[];
897
898     /*
899      * Reset signal handlers.
900      */
901     signal(SIGHUP, SIG_IGN);            /* Hangup */
902     signal(SIGINT, SIG_DFL);            /* Interrupt */
903     signal(SIGTERM, SIG_DFL);           /* Terminate */
904     signal(SIGCHLD, SIG_DFL);
905     signal(SIGUSR1, SIG_DFL);
906     signal(SIGUSR2, SIG_DFL);
907     signal(SIGABRT, SIG_DFL);
908     signal(SIGALRM, SIG_DFL);
909     signal(SIGFPE, SIG_DFL);
910     signal(SIGILL, SIG_DFL);
911     signal(SIGPIPE, SIG_DFL);
912     signal(SIGQUIT, SIG_DFL);
913     signal(SIGSEGV, SIG_DFL);
914 #ifdef SIGBUS
915     signal(SIGBUS, SIG_DFL);
916 #endif
917 #ifdef SIGEMT
918     signal(SIGEMT, SIG_DFL);
919 #endif
920 #ifdef SIGPOLL
921     signal(SIGPOLL, SIG_DFL);
922 #endif
923 #ifdef SIGPROF
924     signal(SIGPROF, SIG_DFL);
925 #endif
926 #ifdef SIGSYS
927     signal(SIGSYS, SIG_DFL);
928 #endif
929 #ifdef SIGTRAP
930     signal(SIGTRAP, SIG_DFL);
931 #endif
932 #ifdef SIGVTALRM
933     signal(SIGVTALRM, SIG_DFL);
934 #endif
935 #ifdef SIGXCPU
936     signal(SIGXCPU, SIG_DFL);
937 #endif
938 #ifdef SIGXFSZ
939     signal(SIGXFSZ, SIG_DFL);
940 #endif
941
942     /*
943      * Open the record file if required.
944      */
945     if (record_file != NULL) {
946         recordf = fopen(record_file, "a");
947         if (recordf == NULL)
948             error("Couldn't create record file %s: %m", record_file);
949     }
950
951     /* set all the fds to non-blocking mode */
952     flags = fcntl(pty_master, F_GETFL);
953     if (flags == -1
954         || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
955         warn("couldn't set pty master to nonblock: %m");
956     flags = fcntl(ifd, F_GETFL);
957     if (flags == -1
958         || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
959         warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
960     if (ofd != ifd) {
961         flags = fcntl(ofd, F_GETFL);
962         if (flags == -1
963             || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
964             warn("couldn't set stdout to nonblock: %m");
965     }
966
967     nibuf = nobuf = 0;
968     ibufp = obufp = NULL;
969     pty_readable = stdin_readable = 1;
970
971     ilevel = olevel = 0;
972     gettimeofday(&levelt, NULL);
973     if (max_data_rate) {
974         max_level = max_data_rate / 10;
975         if (max_level < 100)
976             max_level = 100;
977     } else
978         max_level = PPP_MRU + PPP_HDRLEN + 1;
979
980     nfds = (ofd > pty_master? ofd: pty_master) + 1;
981     if (recordf != NULL) {
982         gettimeofday(&lasttime, NULL);
983         putc(7, recordf);       /* put start marker */
984         putc(lasttime.tv_sec >> 24, recordf);
985         putc(lasttime.tv_sec >> 16, recordf);
986         putc(lasttime.tv_sec >> 8, recordf);
987         putc(lasttime.tv_sec, recordf);
988         lasttime.tv_usec = 0;
989     }
990
991     while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
992         top = 0;
993         tout.tv_sec = 0;
994         tout.tv_usec = 10000;
995         FD_ZERO(&ready);
996         FD_ZERO(&writey);
997         if (nibuf != 0) {
998             if (ilevel >= max_level)
999                 top = &tout;
1000             else
1001                 FD_SET(pty_master, &writey);
1002         } else if (stdin_readable)
1003             FD_SET(ifd, &ready);
1004         if (nobuf != 0) {
1005             if (olevel >= max_level)
1006                 top = &tout;
1007             else
1008                 FD_SET(ofd, &writey);
1009         } else if (pty_readable)
1010             FD_SET(pty_master, &ready);
1011         if (select(nfds, &ready, &writey, NULL, top) < 0) {
1012             if (errno != EINTR)
1013                 fatal("select");
1014             continue;
1015         }
1016         if (max_data_rate) {
1017             double dt;
1018             int nbt;
1019             struct timeval now;
1020
1021             gettimeofday(&now, NULL);
1022             dt = (now.tv_sec - levelt.tv_sec
1023                   + (now.tv_usec - levelt.tv_usec) / 1e6);
1024             nbt = (int)(dt * max_data_rate);
1025             ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
1026             olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
1027             levelt = now;
1028         } else
1029             ilevel = olevel = 0;
1030         if (FD_ISSET(ifd, &ready)) {
1031             ibufp = inpacket_buf;
1032             nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
1033             if (nibuf < 0 && errno == EIO)
1034                 nibuf = 0;
1035             if (nibuf < 0) {
1036                 if (!(errno == EINTR || errno == EAGAIN)) {
1037                     error("Error reading standard input: %m");
1038                     break;
1039                 }
1040                 nibuf = 0;
1041             } else if (nibuf == 0) {
1042                 /* end of file from stdin */
1043                 stdin_readable = 0;
1044                 /* do a 0-length write, hopefully this will generate
1045                    an EOF (hangup) on the slave side. */
1046                 write(pty_master, inpacket_buf, 0);
1047                 if (recordf)
1048                     if (!record_write(recordf, 4, NULL, 0, &lasttime))
1049                         recordf = NULL;
1050             } else {
1051                 FD_SET(pty_master, &writey);
1052                 if (recordf)
1053                     if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
1054                         recordf = NULL;
1055             }
1056         }
1057         if (FD_ISSET(pty_master, &ready)) {
1058             obufp = outpacket_buf;
1059             nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
1060             if (nobuf < 0 && errno == EIO)
1061                 nobuf = 0;
1062             if (nobuf < 0) {
1063                 if (!(errno == EINTR || errno == EAGAIN)) {
1064                     error("Error reading pseudo-tty master: %m");
1065                     break;
1066                 }
1067                 nobuf = 0;
1068             } else if (nobuf == 0) {
1069                 /* end of file from the pty - slave side has closed */
1070                 pty_readable = 0;
1071                 stdin_readable = 0;     /* pty is not writable now */
1072                 nibuf = 0;
1073                 close(ofd);
1074                 if (recordf)
1075                     if (!record_write(recordf, 3, NULL, 0, &lasttime))
1076                         recordf = NULL;
1077             } else {
1078                 FD_SET(ofd, &writey);
1079                 if (recordf)
1080                     if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
1081                         recordf = NULL;
1082             }
1083         }
1084         if (FD_ISSET(ofd, &writey)) {
1085             n = nobuf;
1086             if (olevel + n > max_level)
1087                 n = max_level - olevel;
1088             n = write(ofd, obufp, n);
1089             if (n < 0) {
1090                 if (errno == EIO) {
1091                     pty_readable = 0;
1092                     nobuf = 0;
1093                 } else if (errno != EAGAIN && errno != EINTR) {
1094                     error("Error writing standard output: %m");
1095                     break;
1096                 }
1097             } else {
1098                 obufp += n;
1099                 nobuf -= n;
1100                 olevel += n;
1101             }
1102         }
1103         if (FD_ISSET(pty_master, &writey)) {
1104             n = nibuf;
1105             if (ilevel + n > max_level)
1106                 n = max_level - ilevel;
1107             n = write(pty_master, ibufp, n);
1108             if (n < 0) {
1109                 if (errno == EIO) {
1110                     stdin_readable = 0;
1111                     nibuf = 0;
1112                 } else if (errno != EAGAIN && errno != EINTR) {
1113                     error("Error writing pseudo-tty master: %m");
1114                     break;
1115                 }
1116             } else {
1117                 ibufp += n;
1118                 nibuf -= n;
1119                 ilevel += n;
1120             }
1121         }
1122     }
1123     exit(0);
1124 }
1125
1126 static int
1127 record_write(f, code, buf, nb, tp)
1128     FILE *f;
1129     int code;
1130     u_char *buf;
1131     int nb;
1132     struct timeval *tp;
1133 {
1134     struct timeval now;
1135     int diff;
1136
1137     gettimeofday(&now, NULL);
1138     now.tv_usec /= 100000;      /* actually 1/10 s, not usec now */
1139     diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
1140     if (diff > 0) {
1141         if (diff > 255) {
1142             putc(5, f);
1143             putc(diff >> 24, f);
1144             putc(diff >> 16, f);
1145             putc(diff >> 8, f);
1146             putc(diff, f);
1147         } else {
1148             putc(6, f);
1149             putc(diff, f);
1150         }
1151         *tp = now;
1152     }
1153     putc(code, f);
1154     if (buf != NULL) {
1155         putc(nb >> 8, f);
1156         putc(nb, f);
1157         fwrite(buf, nb, 1, f);
1158     }
1159     fflush(f);
1160     if (ferror(f)) {
1161         error("Error writing record file: %m");
1162         return 0;
1163     }
1164     return 1;
1165 }