]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/tty.c
Rework options processing stuff to implement option priorities
[ppp.git] / pppd / tty.c
index 3e02e27dd16ab2147b9d3a405a5902625d49b27c..b7e61650d48357f692d3353a77ea2db71ef7fcee 100644 (file)
@@ -20,7 +20,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: tty.c,v 1.1 2000/06/30 04:54:23 paulus Exp $"
+#define RCSID  "$Id: tty.c,v 1.4 2001/02/22 03:15:21 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -49,6 +49,8 @@
 #include "fsm.h"
 #include "lcp.h"
 
+static int setdevname __P((char *, char **, int));
+static int setspeed __P((char *, char **, int));
 static int setxonxoff __P((char **));
 static void finish_tty __P((void));
 static int start_charshunt __P((int, int));
@@ -70,6 +72,7 @@ int baud_rate;                        /* Actual bits/second for serial device */
 char *callback_script;         /* script for doing callback */
 int charshunt_pid;             /* Process ID for charshunt */
 int locked;                    /* lock() has succeeded */
+struct stat devstat;           /* result of stat() on devnam */
 
 /* option variables */
 int    crtscts = 0;            /* Use hardware flow control */
@@ -86,7 +89,7 @@ char  *record_file = NULL;    /* File to record chars sent/received */
 int    max_data_rate;          /* max bytes/sec through charshunt */
 bool   sync_serial = 0;        /* Device is synchronous serial device */
 char   *pty_socket = NULL;     /* Socket to connect to pty */
-int    using_pty = 0;
+int    using_pty = 0;          /* we're allocating a pty as the device */
 
 extern uid_t uid;
 extern int kill_link;
@@ -96,25 +99,25 @@ extern int privopen;                /* don't lock, open device as root */
 
 /* 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 },
+    { "tty speed", o_wild, (void *) &setspeed,
+      "Baud rate for serial port", OPT_NOARG },
     { "lock", o_bool, &lockflag,
       "Lock serial device with UUCP-style lock file", 1 },
     { "nolock", o_bool, &lockflag,
       "Don't lock serial device", 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_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_PRIVFIX },
     { "disconnect", o_string, &disconnect_script,
-      "Program to disconnect serial device",
-      OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
+      "Program to disconnect serial device", OPT_PRIVFIX },
     { "welcome", o_string, &welcomer,
-      "Script to welcome client",
-      OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
+      "Script to welcome client", OPT_PRIVFIX },
     { "pty", o_string, &ptycommand,
-      "Script to run on pseudo-tty master side",
-      OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
+      "Script to run on pseudo-tty master side", OPT_PRIVFIX | OPT_DEVNAM },
     { "notty", o_bool, &notty,
       "Input/output is not a tty", OPT_DEVNAM | 1 },
     { "socket", o_string, &pty_socket,
@@ -144,6 +147,77 @@ option_t tty_options[] = {
     { NULL }
 };
 
+
+/*
+ * 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;
+       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;
@@ -164,6 +238,102 @@ void tty_init()
     add_options(tty_options);
 }
 
+/*
+ * tty_device_check - work out which tty device we are using
+ * and read its options file.
+ */
+void tty_device_check()
+{
+       using_pty = notty || ptycommand != NULL || pty_socket != NULL;
+       if (using_pty)
+               return;
+       if (default_device) {
+               char *p;
+               if (!isatty(0) || (p = ttyname(0)) == NULL) {
+                       option_error("no device specified and stdin is not a tty");
+                       exit(EXIT_OPTION_ERROR);
+               }
+               strlcpy(devnam, p, sizeof(devnam));
+               if (stat(devnam, &devstat) < 0)
+                       fatal("Couldn't stat default device %s: %m", devnam);
+       }
+
+
+       /*
+        * Parse the tty options file.
+        * The per-tty options file should not change
+        * ptycommand, pty_socket, notty or devnam.
+        * options_for_tty doesn't override options set on the command line,
+        * except for some privileged options.
+        */
+       if (!options_for_tty())
+               exit(EXIT_OPTION_ERROR);
+}
+
+/*
+ * tty_check_options - do consistency checks on the options we were given.
+ */
+void
+tty_check_options()
+{
+       struct stat statbuf;
+       int fdflags;
+
+       if (demand && connect_script == 0) {
+               option_error("connect script is required for demand-dialling\n");
+               exit(EXIT_OPTION_ERROR);
+       }
+       /* default holdoff to 0 if no connect script has been given */
+       if (connect_script == 0 && !holdoff_specified)
+               holdoff = 0;
+
+       if (using_pty) {
+               if (!default_device) {
+                       option_error("%s option precludes specifying device name",
+                                    notty? "notty": "pty");
+                       exit(EXIT_OPTION_ERROR);
+               }
+               if (ptycommand != NULL && notty) {
+                       option_error("pty option is incompatible with notty option");
+                       exit(EXIT_OPTION_ERROR);
+               }
+               if (pty_socket != NULL && (ptycommand != NULL || notty)) {
+                       option_error("socket option is incompatible with pty and notty");
+                       exit(EXIT_OPTION_ERROR);
+               }
+               default_device = notty;
+               lockflag = 0;
+               modem = 0;
+               if (notty && log_to_fd <= 1)
+                       log_to_fd = -1;
+       } else {
+               /*
+                * If the user has specified a device which is the same as
+                * the one on stdin, pretend they didn't specify any.
+                * If the device is already open read/write on stdin,
+                * we assume we don't need to lock it, and we can open it
+                * as root.
+                */
+               if (fstat(0, &statbuf) >= 0 && S_ISCHR(statbuf.st_mode)
+                   && statbuf.st_rdev == devstat.st_rdev) {
+                       default_device = 1;
+                       fdflags = fcntl(0, F_GETFL);
+                       if (fdflags != -1 && (fdflags & O_ACCMODE) == O_RDWR)
+                               privopen = 1;
+               }
+       }
+       if (default_device)
+               nodetach = 1;
+
+       /*
+        * Don't send log messages to the serial port, it tends to
+        * confuse the peer. :-)
+        */
+       if (log_to_fd >= 0 && fstat(log_to_fd, &statbuf) >= 0
+           && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
+               log_to_fd = -1;
+}
+
 /*
  * connect_tty - get the serial port ready to start doing PPP.
  * That is, open the serial port, set its speed and mode, and run
@@ -217,12 +387,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;
@@ -234,6 +406,7 @@ 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");
@@ -259,7 +432,6 @@ int connect_tty()
                 */
                set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
                                   || initializer != NULL));
-               real_ttyfd = ttyfd;
        }
 
        /*
@@ -373,6 +545,14 @@ 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;
+
        return ttyfd;
 }