]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/tty.c
Don't change serial port permissions under Linux, since the
[ppp.git] / pppd / tty.c
index 751bfadb5a5261fc220f480d8cf7fa398708d07e..d6026981613a67e736062e4f30a7cd50ed52e822 100644 (file)
@@ -1,26 +1,79 @@
 /*
  * tty.c - code for handling serial ports in pppd.
  *
- * Copyright (C) 2000 Paul Mackerras.
- * All rights reserved.
+ * Copyright (C) 2000-2002 Paul Mackerras. All rights reserved.
  *
- * Portions Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name(s) of the authors of this software must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Paul Mackerras
+ *     <paulus@samba.org>".
+ *
+ * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Portions derived from main.c, which is:
+ *
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: tty.c,v 1.2 2000/07/06 11:17:03 paulus Exp $"
+#define RCSID  "$Id: tty.c,v 1.13 2004/01/13 04:17:59 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
 #include "fsm.h"
 #include "lcp.h"
 
+void tty_process_extra_options __P((void));
+void tty_check_options __P((void));
+int  connect_tty __P((void));
+void disconnect_tty __P((void));
+void tty_close_fds __P((void));
+void cleanup_tty __P((void));
+void tty_do_send_config __P((int, u_int32_t, int, int));
+
+static int setdevname __P((char *, char **, int));
+static int setspeed __P((char *, char **, int));
 static int setxonxoff __P((char **));
+static int setescape __P((char **));
+static void printescape __P((option_t *, void (*)(void *, char *,...),void *));
 static void finish_tty __P((void));
 static int start_charshunt __P((int, int));
 static void stop_charshunt __P((void *, int));
@@ -64,6 +129,7 @@ static int pty_master;               /* fd for master side of pty */
 static int pty_slave;          /* fd for slave side of pty */
 static int real_ttyfd;         /* fd for actual serial port (not pty) */
 static int ttyfd;              /* Serial port file descriptor */
+static char speed_str[16];     /* Serial port speed as string */
 
 mode_t tty_mode = (mode_t)-1;  /* Original access permissions to tty */
 int baud_rate;                 /* Actual bits/second for serial device */
@@ -95,56 +161,175 @@ extern int kill_link;
 /* XXX */
 extern int privopen;           /* don't lock, open device as root */
 
+u_int32_t xmit_accm[8];                /* extended transmit ACCM */
+
 /* option descriptors */
 option_t tty_options[] = {
+    /* device name must be first, or change connect_tty() below! */
+    { "device name", o_wild, (void *) &setdevname,
+      "Serial port device name",
+      OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG  | OPT_A2STRVAL | OPT_STATIC,
+      devnam},
+
+    { "tty speed", o_wild, (void *) &setspeed,
+      "Baud rate for serial port",
+      OPT_PRIO | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, speed_str },
+
     { "lock", o_bool, &lockflag,
-      "Lock serial device with UUCP-style lock file", 1 },
+      "Lock serial device with UUCP-style lock file", OPT_PRIO | 1 },
     { "nolock", o_bool, &lockflag,
-      "Don't lock serial device", OPT_PRIV },
+      "Don't lock serial device", OPT_PRIOSUB | OPT_PRIV },
+
     { "init", o_string, &initializer,
-      "A program to initialize the device",
-      OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
+      "A program to initialize the device", OPT_PRIO | OPT_PRIVFIX },
+
     { "connect", o_string, &connect_script,
-      "A program to set up a connection",
-      OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
+      "A program to set up a connection", OPT_PRIO | OPT_PRIVFIX },
+
     { "disconnect", o_string, &disconnect_script,
-      "Program to disconnect serial device",
-      OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
+      "Program to disconnect serial device", OPT_PRIO | OPT_PRIVFIX },
+
     { "welcome", o_string, &welcomer,
-      "Script to welcome client",
-      OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
+      "Script to welcome client", OPT_PRIO | OPT_PRIVFIX },
+
     { "pty", o_string, &ptycommand,
       "Script to run on pseudo-tty master side",
-      OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
+      OPT_PRIO | OPT_PRIVFIX | OPT_DEVNAM },
+
     { "notty", o_bool, &notty,
       "Input/output is not a tty", OPT_DEVNAM | 1 },
+
     { "socket", o_string, &pty_socket,
-      "Send and receive over socket, arg is host:port", OPT_DEVNAM },
+      "Send and receive over socket, arg is host:port",
+      OPT_PRIO | OPT_DEVNAM },
+
     { "record", o_string, &record_file,
-      "Record characters sent/received to file" },
+      "Record characters sent/received to file", OPT_PRIO },
+
     { "crtscts", o_int, &crtscts,
-      "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
+      "Set hardware (RTS/CTS) flow control",
+      OPT_PRIO | OPT_NOARG | OPT_VAL(1) },
+    { "cdtrcts", o_int, &crtscts,
+      "Set alternate hardware (DTR/CTS) flow control",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(2) },
     { "nocrtscts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_NOARG | OPT_VAL(-1) },
     { "-crtscts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
-    { "cdtrcts", o_int, &crtscts,
-      "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) },
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
     { "nocdtrcts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+      "Disable hardware flow control",
+      OPT_PRIOSUB | OPT_ALIAS | OPT_NOARG | OPT_VAL(-1) },
     { "xonxoff", o_special_noarg, (void *)setxonxoff,
-      "Set software (XON/XOFF) flow control" },
+      "Set software (XON/XOFF) flow control", OPT_PRIOSUB },
+
     { "modem", o_bool, &modem,
-      "Use modem control lines", 1 },
+      "Use modem control lines", OPT_PRIO | 1 },
     { "local", o_bool, &modem,
-      "Don't use modem control lines" },
+      "Don't use modem control lines", OPT_PRIOSUB | 0 },
+
     { "sync", o_bool, &sync_serial,
       "Use synchronous HDLC serial encoding", 1 },
+
     { "datarate", o_int, &max_data_rate,
-      "Maximum data rate in bytes/sec (with pty, notty or record option)" },
+      "Maximum data rate in bytes/sec (with pty, notty or record option)",
+      OPT_PRIO },
+
+    { "escape", o_special, (void *)setescape,
+      "List of character codes to escape on transmission",
+      OPT_A2PRINTER, (void *)printescape },
+
     { NULL }
 };
 
+
+struct channel tty_channel = {
+       tty_options,
+       &tty_process_extra_options,
+       &tty_check_options,
+       &connect_tty,
+       &disconnect_tty,
+       &tty_establish_ppp,
+       &tty_disestablish_ppp,
+       &tty_do_send_config,
+       &tty_recv_config,
+       &cleanup_tty,
+       &tty_close_fds
+};
+
+/*
+ * setspeed - Set the serial port baud rate.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a speed value.
+ */
+static int
+setspeed(arg, argv, doit)
+    char *arg;
+    char **argv;
+    int doit;
+{
+       char *ptr;
+       int spd;
+
+       spd = strtol(arg, &ptr, 0);
+       if (ptr == arg || *ptr != 0 || spd == 0)
+               return 0;
+       if (doit) {
+               inspeed = spd;
+               slprintf(speed_str, sizeof(speed_str), "%d", spd);
+       }
+       return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ * If doit is 0, the call is to check whether this option is
+ * potentially a device name.
+ */
+static int
+setdevname(cp, argv, doit)
+    char *cp;
+    char **argv;
+    int doit;
+{
+       struct stat statbuf;
+       char dev[MAXPATHLEN];
+
+       if (*cp == 0)
+               return 0;
+
+       if (strncmp("/dev/", cp, 5) != 0) {
+               strlcpy(dev, "/dev/", sizeof(dev));
+               strlcat(dev, cp, sizeof(dev));
+               cp = dev;
+       }
+
+       /*
+        * Check if there is a character device by this name.
+        */
+       if (stat(cp, &statbuf) < 0) {
+               if (!doit)
+                       return errno != ENOENT;
+               option_error("Couldn't stat %s: %m", cp);
+               return 0;
+       }
+       if (!S_ISCHR(statbuf.st_mode)) {
+               if (doit)
+                       option_error("%s is not a character device", cp);
+               return 0;
+       }
+
+       if (doit) {
+               strlcpy(devnam, cp, sizeof(devnam));
+               devstat = statbuf;
+               default_device = 0;
+       }
+  
+       return 1;
+}
+
 static int
 setxonxoff(argv)
     char **argv;
@@ -156,20 +341,77 @@ setxonxoff(argv)
        return 1;
 }
 
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+    char **argv;
+{
+    int n, ret;
+    char *p, *endp;
+
+    p = *argv;
+    ret = 1;
+    while (*p) {
+       n = strtol(p, &endp, 16);
+       if (p == endp) {
+           option_error("escape parameter contains invalid hex number '%s'",
+                        p);
+           return 0;
+       }
+       p = endp;
+       if (n < 0 || n == 0x5E || n > 0xFF) {
+           option_error("can't escape character 0x%x", n);
+           ret = 0;
+       } else
+           xmit_accm[n >> 5] |= 1 << (n & 0x1F);
+       while (*p == ',' || *p == ' ')
+           ++p;
+    }
+    lcp_allowoptions[0].asyncmap = xmit_accm[0];
+    return ret;
+}
+
+static void
+printescape(opt, printer, arg)
+    option_t *opt;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+       int n;
+       int first = 1;
+
+       for (n = 0; n < 256; ++n) {
+               if (n == 0x7d)
+                       n += 2;         /* skip 7d, 7e */
+               if (xmit_accm[n >> 5] & (1 << (n & 0x1f))) {
+                       if (!first)
+                               printer(arg, ",");
+                       else
+                               first = 0;
+                       printer(arg, "%x", n);
+               }
+       }
+       if (first)
+               printer(arg, "oops # nothing escaped");
+}
+
 /*
  * tty_init - do various tty-related initializations.
  */
 void tty_init()
 {
     add_notifier(&pidchange, maybe_relock, 0);
-    add_options(tty_options);
+    the_channel = &tty_channel;
+    xmit_accm[3] = 0x60000000;
 }
 
 /*
- * tty_device_check - work out which tty device we are using
+ * tty_process_extra_options - work out which tty device we are using
  * and read its options file.
  */
-void tty_device_check()
+void tty_process_extra_options()
 {
        using_pty = notty || ptycommand != NULL || pty_socket != NULL;
        if (using_pty)
@@ -314,12 +556,14 @@ int connect_tty()
                for (;;) {
                        /* If the user specified the device name, become the
                           user before opening it. */
-                       int err;
-                       if (!devnam_info.priv && !privopen)
+                       int err, prio;
+
+                       prio = privopen? OPRIO_ROOT: tty_options[0].priority;
+                       if (prio < OPRIO_ROOT)
                                seteuid(uid);
                        ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
                        err = errno;
-                       if (!devnam_info.priv && !privopen)
+                       if (prio < OPRIO_ROOT)
                                seteuid(0);
                        if (ttyfd >= 0)
                                break;
@@ -331,10 +575,16 @@ int connect_tty()
                        if (!persist || err != EINTR)
                                return -1;
                }
+               real_ttyfd = ttyfd;
                if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
                    || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
                        warn("Couldn't reset non-blocking mode on device: %m");
 
+#ifndef __linux__
+               /*
+                * Linux 2.4 and above blocks normal writes to the tty
+                * when it is in PPP line discipline, so this isn't needed.
+                */
                /*
                 * Do the equivalent of `mesg n' to stop broadcast messages.
                 */
@@ -343,6 +593,7 @@ int connect_tty()
                        warn("Couldn't restrict write permissions to %s: %m", devnam);
                } else
                        tty_mode = statbuf.st_mode;
+#endif /* __linux__ */
 
                /*
                 * Set line speed, flow control, etc.
@@ -356,7 +607,6 @@ int connect_tty()
                 */
                set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
                                   || initializer != NULL));
-               real_ttyfd = ttyfd;
        }
 
        /*
@@ -370,6 +620,11 @@ int connect_tty()
 
                        if (pipe(ipipe) < 0 || pipe(opipe) < 0)
                                fatal("Couldn't create pipes for record option: %m");
+
+                       /* don't leak these to the ptycommand */
+                       (void) fcntl(ipipe[0], F_SETFD, FD_CLOEXEC);
+                       (void) fcntl(opipe[1], F_SETFD, FD_CLOEXEC);
+
                        ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
                                && start_charshunt(ipipe[0], opipe[1]);
                        close(ipipe[0]);
@@ -470,6 +725,11 @@ int connect_tty()
                        warn("Welcome script failed");
        }
 
+       /*
+        * If we are initiating this connection, wait for a short
+        * time for something from the peer.  This can avoid bouncing
+        * our packets off his tty before he has it set up.
+        */
        if (connector != NULL || ptycommand != NULL)
                listen_time = connect_delay;
 
@@ -514,6 +774,20 @@ void cleanup_tty()
        }
 }
 
+/*
+ * tty_do_send_config - set transmit-side PPP configuration.
+ * We set the extended transmit ACCM here as well.
+ */
+void
+tty_do_send_config(mtu, accm, pcomp, accomp)
+    int mtu;
+    u_int32_t accm;
+    int pcomp, accomp;
+{
+       tty_set_xaccm(xmit_accm);
+       tty_send_config(mtu, accm, pcomp, accomp);
+}
+
 /*
  * finish_tty - restore the terminal device to its original settings
  */
@@ -532,12 +806,14 @@ finish_tty()
 
        restore_tty(real_ttyfd);
 
+#ifndef __linux__
        if (tty_mode != (mode_t) -1) {
                if (fchmod(real_ttyfd, tty_mode) != 0) {
                        /* XXX if devnam is a symlink, this will change the link */
                        chmod(devnam, tty_mode);
                }
        }
+#endif /* __linux__ */
 
        close(real_ttyfd);
        real_ttyfd = -1;
@@ -619,7 +895,7 @@ start_charshunt(ifd, ofd)
 {
     int cpid;
 
-    cpid = fork();
+    cpid = safe_fork();
     if (cpid == -1) {
        error("Can't fork process for character shunt: %m");
        return 0;
@@ -728,6 +1004,13 @@ charshunt(ifd, ofd, record_file)
     signal(SIGXFSZ, SIG_DFL);
 #endif
 
+    /*
+     * Check that the fds won't overrun the fd_sets
+     */
+    if (ifd >= FD_SETSIZE || ofd >= FD_SETSIZE || pty_master >= FD_SETSIZE)
+       fatal("internal error: file descriptor too large (%d, %d, %d)",
+             ifd, ofd, pty_master);
+
     /*
      * Open the record file if required.
      */