Move the tty-related stuff out to tty.c as far as possible.
authorPaul Mackerras <paulus@samba.org>
Fri, 30 Jun 2000 04:54:23 +0000 (04:54 +0000)
committerPaul Mackerras <paulus@samba.org>
Fri, 30 Jun 2000 04:54:23 +0000 (04:54 +0000)
pppd/Makefile.linux
pppd/ipcp.c
pppd/main.c
pppd/multilink.c
pppd/options.c
pppd/pppd.h
pppd/sys-linux.c
pppd/tty.c [new file with mode: 0644]

index 1a0662c1959e058b94f97edb64e89cb4f333d785..b642005bad899da912fdace6911ece0539adc73a 100644 (file)
@@ -1,6 +1,6 @@
 #
 # pppd makefile for Linux
-# $Id: Makefile.linux,v 1.37 2000/04/04 07:06:48 paulus Exp $
+# $Id: Makefile.linux,v 1.38 2000/06/30 04:54:20 paulus Exp $
 #
 
 # Default installation locations
@@ -9,13 +9,13 @@ MANDIR = /usr/man
 
 PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
           ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
-          demand.c utils.c multilink.c tdb.c
+          demand.c utils.c multilink.c tdb.c tty.c
 HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
           ipxcp.h cbcp.h tdb.h
 MANPAGES = pppd.8
 PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
           auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
-          tdb.o
+          tdb.o tty.o
 
 all: pppd
 
index 484c00c4b988c9a49f58b6912d9cd55e12a2fd4d..8c09d705be3f5b29f0d4d8926500a23293b56428 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: ipcp.c,v 1.54 2000/04/15 01:27:11 masputra Exp $"
+#define RCSID  "$Id: ipcp.c,v 1.55 2000/06/30 04:54:20 paulus Exp $"
 
 /*
  * TODO:
@@ -53,11 +53,15 @@ void (*ip_up_hook) __P((void)) = NULL;
 /* Hook for a plugin to know when IP protocol has come down */
 void (*ip_down_hook) __P((void)) = NULL;
 
+/* Hook for a plugin to choose the remote IP address */
+void (*ip_choose_hook) __P((u_int32_t *)) = NULL;
+
 /* local vars */
 static int default_route_set[NUM_PPP]; /* Have set up a default route */
 static int proxy_arp_set[NUM_PPP];     /* Have created proxy arp entry */
 static bool usepeerdns;                        /* Ask peer for DNS addrs */
 static int ipcp_is_up;                 /* have called np_up() */
+static bool ask_for_local;             /* request our address from peer */
 
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
@@ -449,15 +453,17 @@ ipcp_resetci(f)
     ipcp_options *go = &ipcp_gotoptions[f->unit];
 
     wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
-    if (wo->ouraddr == 0 || disable_defaultip)
+    if (wo->ouraddr == 0)
        wo->accept_local = 1;
     if (wo->hisaddr == 0)
        wo->accept_remote = 1;
     wo->req_dns1 = usepeerdns; /* Request DNS addresses from the peer */
     wo->req_dns2 = usepeerdns;
     *go = *wo;
-    if (disable_defaultip)
+    if (!ask_for_local)
        go->ouraddr = 0;
+    if (ip_choose_hook)
+       ip_choose_hook(&wo->hisaddr);
 }
 
 
@@ -1300,7 +1306,7 @@ ip_check_options()
      * Default our local IP address based on our hostname.
      * If local IP address already given, don't bother.
      */
-    if (wo->ouraddr == 0) {
+    if (wo->ouraddr == 0 && !disable_defaultip) {
        /*
         * Look up our hostname (possibly with domain name appended)
         * and take the first IP address as our local IP address.
@@ -1313,6 +1319,7 @@ ip_check_options()
                wo->ouraddr = local;
        }
     }
+    ask_for_local = wo->ouraddr != 0 || !disable_defaultip;
 }
 
 
@@ -1335,7 +1342,7 @@ ip_demand_conf(u)
        /* make up an arbitrary address for us */
        wo->ouraddr = htonl(0x0a404040 + ifunit);
        wo->accept_local = 1;
-       disable_defaultip = 1;  /* don't tell the peer this address */
+       ask_for_local = 0;      /* don't tell the peer this address */
     }
     if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
        return 0;
@@ -1379,16 +1386,16 @@ ipcp_up(f)
     if (!ho->neg_addr)
        ho->hisaddr = wo->hisaddr;
 
-    if (ho->hisaddr == 0) {
-       error("Could not determine remote IP address");
-       ipcp_close(f->unit, "Could not determine remote IP address");
-       return;
-    }
     if (go->ouraddr == 0) {
        error("Could not determine local IP address");
        ipcp_close(f->unit, "Could not determine local IP address");
        return;
     }
+    if (ho->hisaddr == 0) {
+       ho->hisaddr = htonl(0x0a404040 + ifunit);
+       warn("Could not determine remote IP address: defaulting to %I",
+            ho->hisaddr);
+    }
     script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0);
     script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1);
 
index 0722e9104076417b68500f16058a98f31f1e8f2d..3d4c16acbad4db134f3ed5ea76eee7174327454d 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.98 2000/04/29 12:32:59 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.99 2000/06/30 04:54:20 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -78,36 +78,32 @@ char *progname;                     /* Name of this program */
 char hostname[MAXNAMELEN];     /* Our hostname */
 static char pidfilename[MAXPATHLEN];   /* name of pid file */
 static char linkpidfile[MAXPATHLEN];   /* name of linkname pid file */
-static char ppp_devnam[MAXPATHLEN]; /* name of PPP tty (maybe ttypx) */
-static uid_t uid;              /* Our real user-id */
-static int conn_running;       /* we have a [dis]connector running */
+char ppp_devnam[MAXPATHLEN];   /* name of PPP tty (maybe ttypx) */
+uid_t uid;                     /* Our real user-id */
+struct notifier *pidchange = NULL;
+struct notifier *phasechange = NULL;
+struct notifier *exitnotify = NULL;
+struct notifier *sigreceived = NULL;
 
-int ttyfd;                     /* Serial port file descriptor */
-mode_t tty_mode = (mode_t)-1;  /* Original access permissions to tty */
-int baud_rate;                 /* Actual bits/second for serial device */
 int hungup;                    /* terminal has been hung up */
 int privileged;                        /* we're running as real uid root */
 int need_holdoff;              /* need holdoff period before restarting */
 int detached;                  /* have detached from terminal */
 struct stat devstat;           /* result of stat() on devnam */
-int prepass = 0;               /* doing prepass to find device name */
-int devnam_fixed;              /* set while in options.ttyxx file */
 volatile int status;           /* exit status for pppd */
 int unsuccess;                 /* # unsuccessful connection attempts */
 int do_callback;               /* != 0 if we should do callback next */
 int doing_callback;            /* != 0 if we are doing callback */
-char *callback_script;         /* script for doing callback */
 TDB_CONTEXT *pppdb;            /* database for storing status etc. */
 char db_key[32];
 
 int (*holdoff_hook) __P((void)) = NULL;
 int (*new_phase_hook) __P((int)) = NULL;
 
+static int conn_running;       /* we have a [dis]connector running */
+static int devfd;              /* fd of underlying device */
 static int fd_ppp = -1;                /* fd for talking PPP */
 static int fd_loop;            /* fd for getting demand-dial packets */
-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) */
 
 int phase;                     /* where the link is at */
 int kill_link;
@@ -125,8 +121,7 @@ u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
 static int n_children;         /* # child processes still running */
 static int got_sigchld;                /* set if we have received a SIGCHLD */
 
-static int locked;             /* lock() has succeeded */
-static int privopen;           /* don't lock, open device as root */
+int privopen;                  /* don't lock, open device as root */
 
 char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
 
@@ -139,8 +134,6 @@ struct pppd_stats link_stats;
 int link_connect_time;
 int link_stats_valid;
 
-static int charshunt_pid;      /* Process ID for charshunt */
-
 /*
  * We maintain a list of child process pids and
  * functions to call when they exit.
@@ -161,7 +154,6 @@ static void setup_signals __P((void));
 static void create_pidfile __P((void));
 static void create_linkpidfile __P((void));
 static void cleanup __P((void));
-static void close_tty __P((void));
 static void get_input __P((void));
 static void calltimeout __P((void));
 static struct timeval *timeleft __P((struct timeval *));
@@ -173,19 +165,11 @@ static void toggle_debug __P((int));
 static void open_ccp __P((int));
 static void bad_signal __P((int));
 static void holdoff_end __P((void *));
-static int device_script __P((char *, int, int, int));
 static int reap_kids __P((int waitfor));
-static void record_child __P((int, char *, void (*) (void *), void *));
 static void update_db_entry __P((void));
 static void add_db_key __P((const char *));
 static void delete_db_key __P((const char *));
 static void cleanup_db __P((void));
-static int open_socket __P((char *));
-static int start_charshunt __P((int, int));
-static void charshunt_done __P((void *));
-static void charshunt __P((int, int, char *));
-static int record_write __P((FILE *, int code, u_char *buf, int nb,
-                            struct timeval *));
 
 extern char    *ttyname __P((int));
 extern char    *getlogin __P((void));
@@ -227,8 +211,7 @@ struct protent *protocols[] = {
 };
 
 /*
- * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the
- * device name.
+ * If PPP_DRV_NAME is not defined, use the default "ppp" as the device name.
  */
 #if !defined(PPP_DRV_NAME)
 #define PPP_DRV_NAME   "ppp"
@@ -240,7 +223,7 @@ main(argc, argv)
     char *argv[];
 {
     int i, fdflags, t;
-    char *p, *connector;
+    char *p;
     struct passwd *pw;
     struct timeval timo;
     sigset_t mask;
@@ -290,25 +273,22 @@ main(argc, argv)
     magic_init();
 
     /*
-     * Initialize to the standard option set, then parse, in order,
-     * the system options file, the user's options file,
-     * the tty's options file, and the command line arguments.
+     * Initialize each protocol.
      */
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
         (*protp->init)(0);
 
     progname = *argv;
 
-    prepass = 0;
+    /*
+     * Parse, in order, the system options file, the user's options file,
+     * the tty's options file, and the command line arguments.
+     */
     if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
-       || !options_from_user())
+       || !options_from_user()
+       || !parse_args(argc-1, argv+1))
        exit(EXIT_OPTION_ERROR);
 
-    /* scan command line and options files to find device name */
-    prepass = 1;
-    parse_args(argc-1, argv+1);
-    prepass = 0;
-
     /*
      * Work out the device name, if it hasn't already been specified.
      */
@@ -325,18 +305,11 @@ main(argc, argv)
     }
 
     /*
-     * Parse the tty options file and the command line.
+     * Parse the tty options file.
      * The per-tty options file should not change
      * ptycommand, pty_socket, notty or devnam.
      */
-    devnam_fixed = 1;
-    if (!using_pty) {
-       if (!options_for_tty())
-           exit(EXIT_OPTION_ERROR);
-    }
-
-    devnam_fixed = 0;
-    if (!parse_args(argc-1, argv+1))
+    if (!using_pty && !options_for_tty())
        exit(EXIT_OPTION_ERROR);
 
     /*
@@ -488,8 +461,7 @@ main(argc, argv)
     for (;;) {
 
        need_holdoff = 1;
-       ttyfd = -1;
-       real_ttyfd = -1;
+       devfd = -1;
        status = EXIT_OK;
        ++unsuccess;
        doing_callback = do_callback;
@@ -539,202 +511,13 @@ main(argc, argv)
 
        new_phase(PHASE_SERIALCONN);
 
-       /*
-        * Get a pty master/slave pair if the pty, notty, socket,
-        * or record options were specified.
-        */
-       strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
-       pty_master = -1;
-       pty_slave = -1;
-       if (using_pty || record_file != NULL) {
-           if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
-               error("Couldn't allocate pseudo-tty");
-               status = EXIT_FATAL_ERROR;
-               goto fail;
-           }
-           set_up_tty(pty_slave, 1);
-       }
-
-       /*
-        * Lock the device if we've been asked to.
-        */
-       status = EXIT_LOCK_FAILED;
-       if (lockflag && !privopen) {
-           if (lock(devnam) < 0)
-               goto fail;
-           locked = 1;
-       }
-
-       /*
-        * Open the serial device and set it up to be the ppp interface.
-        * First we open it in non-blocking mode so we can set the
-        * various termios flags appropriately.  If we aren't dialling
-        * out and we want to use the modem lines, we reopen it later
-        * in order to wait for the carrier detect signal from the modem.
-        */
-       hungup = 0;
-       kill_link = 0;
-       connector = doing_callback? callback_script: connect_script;
-       if (devnam[0] != 0) {
-           for (;;) {
-               /* If the user specified the device name, become the
-                  user before opening it. */
-               int err;
-               if (!devnam_info.priv && !privopen)
-                   seteuid(uid);
-               ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
-               err = errno;
-               if (!devnam_info.priv && !privopen)
-                   seteuid(0);
-               if (ttyfd >= 0)
-                   break;
-               errno = err;
-               if (err != EINTR) {
-                   error("Failed to open %s: %m", devnam);
-                   status = EXIT_OPEN_FAILED;
-               }
-               if (!persist || err != EINTR)
-                   goto fail;
-           }
-           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");
-
-           /*
-            * Do the equivalent of `mesg n' to stop broadcast messages.
-            */
-           if (fstat(ttyfd, &statbuf) < 0
-               || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
-               warn("Couldn't restrict write permissions to %s: %m", devnam);
-           } else
-               tty_mode = statbuf.st_mode;
-
-           /*
-            * Set line speed, flow control, etc.
-            * If we have a non-null connection or initializer script,
-            * on most systems we set CLOCAL for now so that we can talk
-            * to the modem before carrier comes up.  But this has the
-            * side effect that we might miss it if CD drops before we
-            * get to clear CLOCAL below.  On systems where we can talk
-            * successfully to the modem with CLOCAL clear and CD down,
-            * we could clear CLOCAL at this point.
-            */
-           set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
-                              || initializer != NULL));
-           real_ttyfd = ttyfd;
-       }
-
-       /*
-        * If the pty, socket, notty and/or record option was specified,
-        * start up the character shunt now.
-        */
-       status = EXIT_PTYCMD_FAILED;
-       if (ptycommand != NULL) {
-           if (record_file != NULL) {
-               int ipipe[2], opipe[2], ok;
-
-               if (pipe(ipipe) < 0 || pipe(opipe) < 0)
-                   fatal("Couldn't create pipes for record option: %m");
-               ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
-                   && start_charshunt(ipipe[0], opipe[1]);
-               close(ipipe[0]);
-               close(ipipe[1]);
-               close(opipe[0]);
-               close(opipe[1]);
-               if (!ok)
-                   goto fail;
-           } else {
-               if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
-                   goto fail;
-               ttyfd = pty_slave;
-               close(pty_master);
-               pty_master = -1;
-           }
-       } else if (pty_socket != NULL) {
-           int fd = open_socket(pty_socket);
-           if (fd < 0)
-               goto fail;
-           if (!start_charshunt(fd, fd))
-               goto fail;
-       } else if (notty) {
-           if (!start_charshunt(0, 1))
-               goto fail;
-       } else if (record_file != NULL) {
-           if (!start_charshunt(ttyfd, ttyfd))
-               goto fail;
-       }
-
-       /* run connection script */
-       if ((connector && connector[0]) || initializer) {
-           if (real_ttyfd != -1) {
-               /* XXX do this if doing_callback == CALLBACK_DIALIN? */
-               if (!default_device && modem) {
-                   setdtr(real_ttyfd, 0);      /* in case modem is off hook */
-                   sleep(1);
-                   setdtr(real_ttyfd, 1);
-               }
-           }
-
-           if (initializer && initializer[0]) {
-               if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
-                   error("Initializer script failed");
-                   status = EXIT_INIT_FAILED;
-                   goto fail;
-               }
-               if (kill_link)
-                   goto disconnect;
-
-               info("Serial port initialized.");
-           }
-
-           if (connector && connector[0]) {
-               if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
-                   error("Connect script failed");
-                   status = EXIT_CONNECT_FAILED;
-                   goto fail;
-               }
-               if (kill_link)
-                   goto disconnect;
-
-               info("Serial connection established.");
-           }
-
-           /* set line speed, flow control, etc.;
-              clear CLOCAL if modem option */
-           if (real_ttyfd != -1)
-               set_up_tty(real_ttyfd, 0);
-
-           if (doing_callback == CALLBACK_DIALIN)
-               connector = NULL;
-       }
-
-       /* reopen tty if necessary to wait for carrier */
-       if (connector == NULL && modem && devnam[0] != 0) {
-           for (;;) {
-               if ((i = open(devnam, O_RDWR)) >= 0)
-                   break;
-               if (errno != EINTR) {
-                   error("Failed to reopen %s: %m", devnam);
-                   status = EXIT_OPEN_FAILED;
-               }
-               if (!persist || errno != EINTR || hungup || kill_link)
-                   goto fail;
-           }
-           close(i);
-       }
-
-       slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
-       script_setenv("SPEED", numbuf, 0);
-
-       /* run welcome script, if any */
-       if (welcomer && welcomer[0]) {
-           if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
-               warn("Welcome script failed");
-       }
+       devfd = connect_tty();
+       if (devfd < 0)
+           goto fail;
 
        /* set up the serial device as a ppp interface */
        tdb_writelock(pppdb);
-       fd_ppp = establish_ppp(ttyfd);
+       fd_ppp = establish_ppp(devfd);
        if (fd_ppp < 0) {
            tdb_writeunlock(pppdb);
            status = EXIT_FATAL_ERROR;
@@ -763,12 +546,14 @@ main(argc, argv)
         * our packets off his tty before he has it set up.
         */
        add_fd(fd_ppp);
+#ifdef XXX
        if (connect_delay != 0 && (connector != NULL || ptycommand != NULL)) {
            struct timeval t;
            t.tv_sec = connect_delay / 1000;
            t.tv_usec = connect_delay % 1000;
            wait_input(&t);
        }
+#endif
 
        lcp_open(0);            /* Start protocol */
        open_ccp_flag = 0;
@@ -834,7 +619,7 @@ main(argc, argv)
        clean_check();
        if (demand)
            restore_loop();
-       disestablish_ppp(ttyfd);
+       disestablish_ppp(devfd);
        fd_ppp = -1;
        if (!hungup)
            lcp_lowerdown(0);
@@ -846,28 +631,11 @@ main(argc, argv)
         * XXX we may not be able to do this if the line has hung up!
         */
     disconnect:
-       if (disconnect_script && !hungup) {
-           new_phase(PHASE_DISCONNECT);
-           if (real_ttyfd >= 0)
-               set_up_tty(real_ttyfd, 1);
-           if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
-               warn("disconnect script failed");
-           } else {
-               info("Serial link disconnected.");
-           }
-       }
+       new_phase(PHASE_DISCONNECT);
+       disconnect_tty();
 
     fail:
-       if (pty_master >= 0)
-           close(pty_master);
-       if (pty_slave >= 0)
-           close(pty_slave);
-       if (real_ttyfd >= 0)
-           close_tty();
-       if (locked) {
-           unlock();
-           locked = 0;
-       }
+       cleanup_tty();
 
        if (!demand) {
            if (pidfilename[0] != 0
@@ -1023,7 +791,7 @@ set_ifunit(iskey)
     int iskey;
 {
     info("Using interface %s%d", PPP_DRV_NAME, ifunit);
-    slprintf(ifname, sizeof(ifname), PPP_DRV_NAME "%d", ifunit);
+    slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
     script_setenv("IFNAME", ifname, iskey);
     if (iskey) {
        create_pidfile();       /* write pid to file */
@@ -1048,8 +816,7 @@ detach()
     }
     if (pid != 0) {
        /* parent */
-       if (locked)
-           relock(pid);
+       notify(pidchange, pid);
        exit(0);                /* parent dies */
     }
     setsid();
@@ -1310,6 +1077,7 @@ new_phase(p)
     phase = p;
     if (new_phase_hook)
        (*new_phase_hook)(p);
+    notify(phasechange, p);
 }
 
 /*
@@ -1320,6 +1088,7 @@ die(status)
     int status;
 {
     cleanup();
+    notify(exitnotify, status);
     syslog(LOG_INFO, "Exit.");
     exit(status);
 }
@@ -1334,9 +1103,8 @@ cleanup()
     sys_cleanup();
 
     if (fd_ppp >= 0)
-       disestablish_ppp(ttyfd);
-    if (real_ttyfd >= 0)
-       close_tty();
+       disestablish_ppp(devfd);
+    cleanup_tty();
 
     if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT) 
        warn("unable to delete pid file %s: %m", pidfilename);
@@ -1345,42 +1113,10 @@ cleanup()
        warn("unable to delete pid file %s: %m", linkpidfile);
     linkpidfile[0] = 0;
 
-    if (locked)
-       unlock();
-
     if (pppdb != NULL)
        cleanup_db();
 }
 
-/*
- * close_tty - restore the terminal device and close it.
- */
-static void
-close_tty()
-{
-    /* drop dtr to hang up */
-    if (!default_device && modem) {
-       setdtr(real_ttyfd, 0);
-       /*
-        * This sleep is in case the serial port has CLOCAL set by default,
-        * and consequently will reassert DTR when we close the device.
-        */
-       sleep(1);
-    }
-
-    restore_tty(real_ttyfd);
-
-    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);
-       }
-    }
-
-    close(real_ttyfd);
-    real_ttyfd = -1;
-}
-
 /*
  * update_link_stats - get stats at link termination.
  */
@@ -1565,8 +1301,7 @@ hup(sig)
     if (conn_running)
        /* Send the signal to the [dis]connector process(es) also */
        kill_my_pg(sig);
-    if (charshunt_pid)
-       kill(charshunt_pid, sig);
+    notify(sigreceived, sig);
     if (waiting)
        siglongjmp(sigjmp, 1);
 }
@@ -1589,8 +1324,7 @@ term(sig)
     if (conn_running)
        /* Send the signal to the [dis]connector process(es) also */
        kill_my_pg(sig);
-    if (charshunt_pid)
-       kill(charshunt_pid, sig);
+    notify(sigreceived, sig);
     if (waiting)
        siglongjmp(sigjmp, 1);
 }
@@ -1660,23 +1394,23 @@ bad_signal(sig)
     error("Fatal signal %d", sig);
     if (conn_running)
        kill_my_pg(SIGTERM);
-    if (charshunt_pid)
-       kill(charshunt_pid, SIGTERM);
+    notify(sigreceived, sig);
     die(127);
 }
 
 
 /*
- * device_script - run a program to talk to the serial device
+ * device_script - run a program to talk to the specified fds
  * (e.g. to run the connector or disconnector script).
+ * stderr gets connected to the log fd or to the _PATH_CONNERRS file.
  */
-static int
+int
 device_script(program, in, out, dont_wait)
     char *program;
     int in, out;
     int dont_wait;
 {
-    int pid;
+    int pid, fd;
     int status = -1;
     int errfd;
 
@@ -1689,68 +1423,67 @@ device_script(program, in, out, dont_wait)
        return -1;
     }
 
-    if (pid == 0) {
-       sys_close();
-       closelog();
-       if (in == 2) {
-           /* aargh!!! */
-           int newin = dup(in);
-           if (in == out)
-               out = newin;
-           in = newin;
-       } else if (out == 2) {
-           out = dup(out);
-       }
-       if (log_to_fd >= 0) {
-           if (log_to_fd != 2)
-               dup2(log_to_fd, 2);
+    if (pid != 0) {
+       if (dont_wait) {
+           record_child(pid, program, NULL, NULL);
+           status = 0;
        } else {
-           close(2);
-           errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
-           if (errfd >= 0 && errfd != 2) {
-               dup2(errfd, 2);
-               close(errfd);
+           while (waitpid(pid, &status, 0) < 0) {
+               if (errno == EINTR)
+                   continue;
+               fatal("error waiting for (dis)connection process: %m");
            }
+           --conn_running;
        }
-       if (in != 0) {
-           if (out == 0)
-               out = dup(out);
-           dup2(in, 0);
-       }
-       if (out != 1) {
-           dup2(out, 1);
-       }
-       if (real_ttyfd > 2)
-           close(real_ttyfd);
-       if (pty_master > 2)
-           close(pty_master);
-       if (pty_slave > 2)
-           close(pty_slave);
-       setuid(uid);
-       if (getuid() != uid) {
-           error("setuid failed");
-           exit(1);
+       return (status == 0 ? 0 : -1);
+    }
+
+    /* here we are executing in the child */
+    /* make sure fds 0, 1, 2 are occupied */
+    while ((fd = dup(in)) >= 0) {
+       if (fd > 2) {
+           close(fd);
+           break;
        }
-       setgid(getgid());
-       execl("/bin/sh", "sh", "-c", program, (char *)0);
-       error("could not exec /bin/sh: %m");
-       exit(99);
-       /* NOTREACHED */
     }
 
-    if (dont_wait) {
-       record_child(pid, program, NULL, NULL);
-       status = 0;
+    /* dup in and out to fds > 2 */
+    in = dup(in);
+    out = dup(out);
+    if (log_to_fd >= 0) {
+       errfd = dup(log_to_fd);
     } else {
-       while (waitpid(pid, &status, 0) < 0) {
-           if (errno == EINTR)
-               continue;
-           fatal("error waiting for (dis)connection process: %m");
-       }
-       --conn_running;
+       errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+    }
+
+    /* close fds 0 - 2 and any others we can think of */
+    close(0);
+    close(1);
+    close(2);
+    sys_close();
+    tty_close_fds();
+    closelog();
+
+    /* dup the in, out, err fds to 0, 1, 2 */
+    dup2(in, 0);
+    close(in);
+    dup2(out, 1);
+    close(out);
+    if (errfd >= 0) {
+       dup2(errfd, 2);
+       close(errfd);
     }
 
-    return (status == 0 ? 0 : -1);
+    setuid(uid);
+    if (getuid() != uid) {
+       error("setuid failed");
+       exit(1);
+    }
+    setgid(getgid());
+    execl("/bin/sh", "sh", "-c", program, (char *)0);
+    error("could not exec /bin/sh: %m");
+    exit(99);
+    /* NOTREACHED */
 }
 
 
@@ -1810,9 +1543,7 @@ run_program(prog, args, must_exist, done, arg)
        close (0);
        close (1);
        close (2);
-       close (ttyfd);  /* tty interface to the ppp device */
-       if (real_ttyfd >= 0)
-           close(real_ttyfd);
+       tty_close_fds();
 
         /* Don't pass handles to the PPP device, even by accident. */
        new_fd = open (_PATH_DEVNULL, O_RDWR);
@@ -1857,7 +1588,7 @@ run_program(prog, args, must_exist, done, arg)
  * record_child - add a child process to the list for reap_kids
  * to use.
  */
-static void
+void
 record_child(pid, prog, done, arg)
     int pid;
     char *prog;
@@ -1925,6 +1656,62 @@ reap_kids(waitfor)
     return 0;
 }
 
+/*
+ * add_notifier - add a new function to be called when something happens.
+ */
+void
+add_notifier(notif, func, arg)
+    struct notifier **notif;
+    notify_func func;
+    void *arg;
+{
+    struct notifier *np;
+
+    np = malloc(sizeof(struct notifier));
+    if (np == 0)
+       novm("notifier struct");
+    np->next = *notif;
+    np->func = func;
+    np->arg = arg;
+    *notif = np;
+}
+
+/*
+ * remove_notifier - remove a function from the list of things to
+ * be called when something happens.
+ */
+void
+remove_notifier(notif, func, arg)
+    struct notifier **notif;
+    notify_func func;
+    void *arg;
+{
+    struct notifier *np;
+
+    for (; (np = *notif) != 0; notif = &np->next) {
+       if (np->func == func && np->arg == arg) {
+           *notif = np->next;
+           free(np);
+           break;
+       }
+    }
+}
+
+/*
+ * notify - call a set of functions registered with add_notify.
+ */
+void
+notify(notif, val)
+    struct notifier *notif;
+    int val;
+{
+    struct notifier *np;
+
+    while ((np = notif) != 0) {
+       notif = np->next;
+       (*np->func)(np->arg, val);
+    }
+}
 
 /*
  * novm - log an error message saying we ran out of memory, and die.
@@ -2107,389 +1894,3 @@ cleanup_db()
        if (p[-1])
            delete_db_key(p);
 }
-
-/*
- * open_socket - establish a stream socket connection to the nominated
- * host and port.
- */
-static int
-open_socket(dest)
-    char *dest;
-{
-    char *sep, *endp = NULL;
-    int sock, port = -1;
-    u_int32_t host;
-    struct hostent *hent;
-    struct sockaddr_in sad;
-
-    /* parse host:port and resolve host to an IP address */
-    sep = strchr(dest, ':');
-    if (sep != NULL)
-       port = strtol(sep+1, &endp, 10);
-    if (port < 0 || endp == sep+1 || sep == dest) {
-       error("Can't parse host:port for socket destination");
-       return -1;
-    }
-    *sep = 0;
-    host = inet_addr(dest);
-    if (host == (u_int32_t) -1) {
-       hent = gethostbyname(dest);
-       if (hent == NULL) {
-           error("%s: unknown host in socket option", dest);
-           *sep = ':';
-           return -1;
-       }
-       host = *(u_int32_t *)(hent->h_addr_list[0]);
-    }
-    *sep = ':';
-
-    /* get a socket and connect it to the other end */
-    sock = socket(PF_INET, SOCK_STREAM, 0);
-    if (sock < 0) {
-       error("Can't create socket: %m");
-       return -1;
-    }
-    memset(&sad, 0, sizeof(sad));
-    sad.sin_family = AF_INET;
-    sad.sin_port = htons(port);
-    sad.sin_addr.s_addr = host;
-    if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
-       error("Can't connect to %s: %m", dest);
-       close(sock);
-       return -1;
-    }
-
-    return sock;
-}
-
-/*
- * start_charshunt - create a child process to run the character shunt.
- */
-static int
-start_charshunt(ifd, ofd)
-    int ifd, ofd;
-{
-    int cpid;
-
-    cpid = fork();
-    if (cpid == -1) {
-       error("Can't fork process for character shunt: %m");
-       return 0;
-    }
-    if (cpid == 0) {
-       /* child */
-       close(pty_slave);
-       setuid(uid);
-       if (getuid() != uid)
-           fatal("setuid failed");
-       setgid(getgid());
-       if (!nodetach)
-           log_to_fd = -1;
-       charshunt(ifd, ofd, record_file);
-       exit(0);
-    }
-    charshunt_pid = cpid;
-    close(pty_master);
-    pty_master = -1;
-    ttyfd = pty_slave;
-    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
-    return 1;
-}
-
-static void
-charshunt_done(arg)
-    void *arg;
-{
-    charshunt_pid = 0;
-}
-
-/*
- * charshunt - the character shunt, which passes characters between
- * the pty master side and the serial port (or stdin/stdout).
- * This runs as the user (not as root).
- * (We assume ofd >= ifd which is true the way this gets called. :-).
- */
-static void
-charshunt(ifd, ofd, record_file)
-    int ifd, ofd;
-    char *record_file;
-{
-    int n, nfds;
-    fd_set ready, writey;
-    u_char *ibufp, *obufp;
-    int nibuf, nobuf;
-    int flags;
-    int pty_readable, stdin_readable;
-    struct timeval lasttime;
-    FILE *recordf = NULL;
-    int ilevel, olevel, max_level;
-    struct timeval levelt, tout, *top;
-
-    /*
-     * Reset signal handlers.
-     */
-    signal(SIGHUP, SIG_IGN);           /* Hangup */
-    signal(SIGINT, SIG_DFL);           /* Interrupt */
-    signal(SIGTERM, SIG_DFL);          /* Terminate */
-    signal(SIGCHLD, SIG_DFL);
-    signal(SIGUSR1, SIG_DFL);
-    signal(SIGUSR2, SIG_DFL);
-    signal(SIGABRT, SIG_DFL);
-    signal(SIGALRM, SIG_DFL);
-    signal(SIGFPE, SIG_DFL);
-    signal(SIGILL, SIG_DFL);
-    signal(SIGPIPE, SIG_DFL);
-    signal(SIGQUIT, SIG_DFL);
-    signal(SIGSEGV, SIG_DFL);
-#ifdef SIGBUS
-    signal(SIGBUS, SIG_DFL);
-#endif
-#ifdef SIGEMT
-    signal(SIGEMT, SIG_DFL);
-#endif
-#ifdef SIGPOLL
-    signal(SIGPOLL, SIG_DFL);
-#endif
-#ifdef SIGPROF
-    signal(SIGPROF, SIG_DFL);
-#endif
-#ifdef SIGSYS
-    signal(SIGSYS, SIG_DFL);
-#endif
-#ifdef SIGTRAP
-    signal(SIGTRAP, SIG_DFL);
-#endif
-#ifdef SIGVTALRM
-    signal(SIGVTALRM, SIG_DFL);
-#endif
-#ifdef SIGXCPU
-    signal(SIGXCPU, SIG_DFL);
-#endif
-#ifdef SIGXFSZ
-    signal(SIGXFSZ, SIG_DFL);
-#endif
-
-    /*
-     * Open the record file if required.
-     */
-    if (record_file != NULL) {
-       recordf = fopen(record_file, "a");
-       if (recordf == NULL)
-           error("Couldn't create record file %s: %m", record_file);
-    }
-
-    /* set all the fds to non-blocking mode */
-    flags = fcntl(pty_master, F_GETFL);
-    if (flags == -1
-       || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
-       warn("couldn't set pty master to nonblock: %m");
-    flags = fcntl(ifd, F_GETFL);
-    if (flags == -1
-       || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
-       warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
-    if (ofd != ifd) {
-       flags = fcntl(ofd, F_GETFL);
-       if (flags == -1
-           || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
-           warn("couldn't set stdout to nonblock: %m");
-    }
-
-    nibuf = nobuf = 0;
-    ibufp = obufp = NULL;
-    pty_readable = stdin_readable = 1;
-
-    ilevel = olevel = 0;
-    gettimeofday(&levelt, NULL);
-    if (max_data_rate) {
-       max_level = max_data_rate / 10;
-       if (max_level < 100)
-           max_level = 100;
-    } else
-       max_level = sizeof(inpacket_buf) + 1;
-
-    nfds = (ofd > pty_master? ofd: pty_master) + 1;
-    if (recordf != NULL) {
-       gettimeofday(&lasttime, NULL);
-       putc(7, recordf);       /* put start marker */
-       putc(lasttime.tv_sec >> 24, recordf);
-       putc(lasttime.tv_sec >> 16, recordf);
-       putc(lasttime.tv_sec >> 8, recordf);
-       putc(lasttime.tv_sec, recordf);
-       lasttime.tv_usec = 0;
-    }
-
-    while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
-       top = 0;
-       tout.tv_sec = 0;
-       tout.tv_usec = 10000;
-       FD_ZERO(&ready);
-       FD_ZERO(&writey);
-       if (nibuf != 0) {
-           if (ilevel >= max_level)
-               top = &tout;
-           else
-               FD_SET(pty_master, &writey);
-       } else if (stdin_readable)
-           FD_SET(ifd, &ready);
-       if (nobuf != 0) {
-           if (olevel >= max_level)
-               top = &tout;
-           else
-               FD_SET(ofd, &writey);
-       } else if (pty_readable)
-           FD_SET(pty_master, &ready);
-       if (select(nfds, &ready, &writey, NULL, top) < 0) {
-           if (errno != EINTR)
-               fatal("select");
-           continue;
-       }
-       if (max_data_rate) {
-           double dt;
-           int nbt;
-           struct timeval now;
-
-           gettimeofday(&now, NULL);
-           dt = (now.tv_sec - levelt.tv_sec
-                 + (now.tv_usec - levelt.tv_usec) / 1e6);
-           nbt = (int)(dt * max_data_rate);
-           ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
-           olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
-           levelt = now;
-       } else
-           ilevel = olevel = 0;
-       if (FD_ISSET(ifd, &ready)) {
-           ibufp = inpacket_buf;
-           nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
-           if (nibuf < 0 && errno == EIO)
-               nibuf = 0;
-           if (nibuf < 0) {
-               if (!(errno == EINTR || errno == EAGAIN)) {
-                   error("Error reading standard input: %m");
-                   break;
-               }
-               nibuf = 0;
-           } else if (nibuf == 0) {
-               /* end of file from stdin */
-               stdin_readable = 0;
-               /* do a 0-length write, hopefully this will generate
-                  an EOF (hangup) on the slave side. */
-               write(pty_master, inpacket_buf, 0);
-               if (recordf)
-                   if (!record_write(recordf, 4, NULL, 0, &lasttime))
-                       recordf = NULL;
-           } else {
-               FD_SET(pty_master, &writey);
-               if (recordf)
-                   if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
-                       recordf = NULL;
-           }
-       }
-       if (FD_ISSET(pty_master, &ready)) {
-           obufp = outpacket_buf;
-           nobuf = read(pty_master, obufp, sizeof(outpacket_buf));
-           if (nobuf < 0 && errno == EIO)
-               nobuf = 0;
-           if (nobuf < 0) {
-               if (!(errno == EINTR || errno == EAGAIN)) {
-                   error("Error reading pseudo-tty master: %m");
-                   break;
-               }
-               nobuf = 0;
-           } else if (nobuf == 0) {
-               /* end of file from the pty - slave side has closed */
-               pty_readable = 0;
-               stdin_readable = 0;     /* pty is not writable now */
-               nibuf = 0;
-               close(ofd);
-               if (recordf)
-                   if (!record_write(recordf, 3, NULL, 0, &lasttime))
-                       recordf = NULL;
-           } else {
-               FD_SET(ofd, &writey);
-               if (recordf)
-                   if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
-                       recordf = NULL;
-           }
-       }
-       if (FD_ISSET(ofd, &writey)) {
-           n = nobuf;
-           if (olevel + n > max_level)
-               n = max_level - olevel;
-           n = write(ofd, obufp, n);
-           if (n < 0) {
-               if (errno == EIO) {
-                   pty_readable = 0;
-                   nobuf = 0;
-               } else if (errno != EAGAIN && errno != EINTR) {
-                   error("Error writing standard output: %m");
-                   break;
-               }
-           } else {
-               obufp += n;
-               nobuf -= n;
-               olevel += n;
-           }
-       }
-       if (FD_ISSET(pty_master, &writey)) {
-           n = nibuf;
-           if (ilevel + n > max_level)
-               n = max_level - ilevel;
-           n = write(pty_master, ibufp, n);
-           if (n < 0) {
-               if (errno == EIO) {
-                   stdin_readable = 0;
-                   nibuf = 0;
-               } else if (errno != EAGAIN && errno != EINTR) {
-                   error("Error writing pseudo-tty master: %m");
-                   break;
-               }
-           } else {
-               ibufp += n;
-               nibuf -= n;
-               ilevel += n;
-           }
-       }
-    }
-    exit(0);
-}
-
-static int
-record_write(f, code, buf, nb, tp)
-    FILE *f;
-    int code;
-    u_char *buf;
-    int nb;
-    struct timeval *tp;
-{
-    struct timeval now;
-    int diff;
-
-    gettimeofday(&now, NULL);
-    now.tv_usec /= 100000;     /* actually 1/10 s, not usec now */
-    diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
-    if (diff > 0) {
-       if (diff > 255) {
-           putc(5, f);
-           putc(diff >> 24, f);
-           putc(diff >> 16, f);
-           putc(diff >> 8, f);
-           putc(diff, f);
-       } else {
-           putc(6, f);
-           putc(diff, f);
-       }
-       *tp = now;
-    }
-    putc(code, f);
-    if (buf != NULL) {
-       putc(nb >> 8, f);
-       putc(nb, f);
-       fwrite(buf, nb, 1, f);
-    }
-    fflush(f);
-    if (ferror(f)) {
-       error("Error writing record file: %m");
-       return 0;
-    }
-    return 1;
-}
index c0ad23790cbbbff92f8238c942d891b1d4ea5440..da8b33a40c418b638eafb206bdcf818f6bba36be 100644 (file)
@@ -95,7 +95,6 @@ mp_join_bundle()
                /* not doing multilink */
                if (go->neg_mrru)
                        notice("oops, multilink negotiated only for receive");
-               multilink = 0;
                if (demand) {
                        /* already have a bundle */
                        cfg_bundle(0, 0, 0, 0);
index 9e8158aaf0836aabb22cde749f79631b3210a6a2..1844e3efa7ce9ea2ede568ec0e23ff47e46a80bc 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: options.c,v 1.74 2000/04/15 01:27:13 masputra Exp $"
+#define RCSID  "$Id: options.c,v 1.75 2000/06/30 04:54:21 paulus Exp $"
 
 #include <ctype.h>
 #include <stdio.h>
@@ -70,18 +70,9 @@ int  debug = 0;              /* Debug flag */
 int    kdebugflag = 0;         /* Tell kernel to print debug messages */
 int    default_device = 1;     /* Using /dev/tty or equivalent */
 char   devnam[MAXPATHLEN];     /* Device name */
-int    crtscts = 0;            /* Use hardware flow control */
-bool   modem = 1;              /* Use modem control lines */
-int    inspeed = 0;            /* Input/Output speed requested */
 u_int32_t netmask = 0;         /* IP netmask to set on interface */
-bool   lockflag = 0;           /* Create lock file to lock the serial dev */
 bool   nodetach = 0;           /* Don't detach from controlling tty */
 bool   updetach = 0;           /* Detach once link is up */
-char   *initializer = NULL;    /* Script to initialize physical link */
-char   *connect_script = NULL; /* Script to establish physical link */
-char   *disconnect_script = NULL; /* Script to disestablish physical link */
-char   *welcomer = NULL;       /* Script to run after phys link estab. */
-char   *ptycommand = NULL;     /* Command to run on other side of pty */
 int    maxconnect = 0;         /* Maximum connect time */
 char   user[MAXNAMELEN];       /* Username for PAP */
 char   passwd[MAXSECRETLEN];   /* Password for PAP */
@@ -92,24 +83,17 @@ char        *ipparam = NULL;        /* Extra parameter for ip up/down scripts */
 int    idle_time_limit = 0;    /* Disconnect if idle for this many seconds */
 int    holdoff = 30;           /* # seconds to pause before reconnecting */
 bool   holdoff_specified;      /* true if a holdoff value has been given */
-bool   notty = 0;              /* Stdin/out is not a tty */
-char   *pty_socket = NULL;     /* Socket to connect to pty */
-char   *record_file = NULL;    /* File to record chars sent/received */
-int    using_pty = 0;
-bool   sync_serial = 0;        /* Device is synchronous serial device */
 int    log_to_fd = 1;          /* send log messages to this fd too */
 int    maxfail = 10;           /* max # of unsuccessful connection attempts */
 char   linkname[MAXPATHLEN];   /* logical name for link */
 bool   tune_kernel;            /* may alter kernel settings */
 int    connect_delay = 1000;   /* wait this many ms after connect script */
-int    max_data_rate;          /* max bytes/sec through charshunt */
 int    req_unit = -1;          /* requested interface unit */
 bool   multilink = 0;          /* Enable multilink operation */
 char   *bundle_name = NULL;    /* bundle name for multilink */
 
 extern option_t auth_options[];
 extern struct stat devstat;
-extern int prepass;            /* Doing pre-pass to find device name */
 
 struct option_info initializer_info;
 struct option_info connect_script_info;
@@ -129,6 +113,7 @@ int  privileged_option;             /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
 bool log_to_file;              /* log_to_fd is a file opened by us */
 bool log_to_specific_fd;       /* log_to_fd was specified by user option */
+bool no_override;              /* don't override previously-set options */
 
 /*
  * Prototypes
@@ -139,7 +124,6 @@ static int setspeed __P((char *));
 static int noopt __P((char **));
 static int setdomain __P((char **));
 static int setnetmask __P((char **));
-static int setxonxoff __P((char **));
 static int readfile __P((char **));
 static int callfile __P((char **));
 static int showversion __P((char **));
@@ -190,55 +174,16 @@ option_t general_options[] = {
       "Set time in seconds before retrying connection" },
     { "idle", o_int, &idle_time_limit,
       "Set time in seconds before disconnecting idle link" },
-    { "lock", o_bool, &lockflag,
-      "Lock serial device with UUCP-style lock file", 1 },
     { "-all", o_special_noarg, (void *)noopt,
       "Don't request/allow any LCP or IPCP options (useless)" },
-    { "init", o_string, &initializer,
-      "A program to initialize the device",
-      OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
-    { "connect", o_string, &connect_script,
-      "A program to set up a connection",
-      OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
-    { "disconnect", o_string, &disconnect_script,
-      "Program to disconnect serial device",
-      OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
-    { "welcome", o_string, &welcomer,
-      "Script to welcome client",
-      OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
-    { "pty", o_string, &ptycommand,
-      "Script to run on pseudo-tty master side",
-      OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
-    { "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 },
-    { "record", o_string, &record_file,
-      "Record characters sent/received to file" },
     { "maxconnect", o_int, &maxconnect,
       "Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF },
-    { "crtscts", o_int, &crtscts,
-      "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
-    { "nocrtscts", o_int, &crtscts,
-      "Disable hardware flow control", 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) },
-    { "nocdtrcts", o_int, &crtscts,
-      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
-    { "xonxoff", o_special_noarg, (void *)setxonxoff,
-      "Set software (XON/XOFF) flow control" },
     { "domain", o_special, (void *)setdomain,
       "Add given domain name to hostname" },
     { "mtu", o_int, &lcp_allowoptions[0].mru,
       "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
     { "netmask", o_special, (void *)setnetmask,
       "set netmask" },
-    { "modem", o_bool, &modem,
-      "Use modem control lines", 1 },
-    { "local", o_bool, &modem,
-      "Don't use modem control lines" },
     { "file", o_special, (void *)readfile,
       "Take options from a file", OPT_PREPASS },
     { "call", o_special, (void *)callfile,
@@ -255,8 +200,6 @@ option_t general_options[] = {
       "Show brief listing of options" },
     { "-h", o_special_noarg, (void *)showhelp,
       "Show brief listing of options" },
-    { "sync", o_bool, &sync_serial,
-      "Use synchronous HDLC serial encoding", 1 },
     { "logfd", o_int, &log_to_fd,
       "Send log messages to this file descriptor",
       0, &log_to_specific_fd },
@@ -279,8 +222,6 @@ option_t general_options[] = {
       "Don't alter kernel settings", 0 },
     { "connect-delay", o_int, &connect_delay,
       "Maximum time (in ms) to wait after connect script finishes" },
-    { "datarate", o_int, &max_data_rate,
-      "Maximum data rate in bytes/sec (with pty, notty or record option)" },
     { "unit", o_int, &req_unit,
       "PPP interface unit number to use if possible", OPT_LLIMIT, 0, 0 },
 #ifdef HAVE_MULTILINK
@@ -336,8 +277,6 @@ See pppd(8) for more options.\n\
 
 /*
  * parse_args - parse a string of arguments from the command line.
- * If prepass is true, we are scanning for the device name and only
- * processing a few options, so error messages are suppressed.
  */
 int
 parse_args(argc, argv)
@@ -377,8 +316,7 @@ parse_args(argc, argv)
         */
        if ((ret = setdevname(arg)) == 0
            && (ret = setspeed(arg)) == 0
-           && (ret = setipaddr(arg)) == 0
-           && !prepass) {
+           && (ret = setipaddr(arg)) == 0) {
            option_error("unrecognized option '%s'", arg);
            usage();
            return 0;
@@ -452,9 +390,12 @@ options_from_file(filename, must_exist, check_prot, priv)
     if (check_prot)
        seteuid(0);
     if (f == NULL) {
-       if (!must_exist && err == ENOENT)
-           return 1;
        errno = err;
+       if (!must_exist) {
+           if (err != ENOENT && err != ENOTDIR)
+               warn("Warning: can't open options file %s: %m", filename);
+           return 1;
+       }
        option_error("Can't open options file %s: %m", filename);
        return 0;
     }
@@ -483,7 +424,7 @@ options_from_file(filename, must_exist, check_prot, priv)
                argv[i] = args[i];
            }
            current_option = cmd;
-           if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
+           if ((opt->flags & OPT_DEVEQUIV) && no_override) {
                option_error("the %s option may not be used in the %s file",
                             cmd, filename);
                goto err;
@@ -544,6 +485,9 @@ options_from_user()
 /*
  * options_for_tty - See if an options file exists for the serial
  * device, and if so, interpret options from it.
+ * We only allow the per-tty options file to override anything from
+ * the command line if it is something that the user can't override
+ * once it has been set by root.
  */
 int
 options_for_tty()
@@ -566,7 +510,9 @@ options_for_tty()
     for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
        if (*p == '/')
            *p = '.';
+    no_override = 1;
     ret = options_from_file(path, 0, 0, 1);
+    no_override = 0;
     free(path);
     return ret;
 }
@@ -673,8 +619,14 @@ process_option(opt, argv)
     char *sv;
     int (*parser) __P((char **));
 
-    if ((opt->flags & OPT_PREPASS) == 0 && prepass)
-       return 1;
+    if (no_override && (opt->flags & OPT_SEENIT)) {
+       struct option_info *ip = (struct option_info *) opt->addr2;
+       if (!(privileged && (opt->flags & OPT_PRIVFIX)))
+           return 1;
+       if (!ip || ip->priv)
+           return 1;
+       warn("%s option from per-tty file overrides command line", opt->name);
+    }
     if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
        option_error("it's too late to use the %s option", opt->name);
        return 0;
@@ -694,6 +646,7 @@ process_option(opt, argv)
            return 0;
        }
     }
+    opt->flags |= OPT_SEENIT;
 
     switch (opt->type) {
     case o_bool:
@@ -877,10 +830,6 @@ option_error __V((char *fmt, ...))
     va_start(args);
     fmt = va_arg(args, char *);
 #endif
-    if (prepass) {
-       va_end(args);
-       return;
-    }
     vslprintf(buf, sizeof(buf), fmt, args);
     va_end(args);
     if (phase == PHASE_INITIALIZE)
@@ -1350,12 +1299,13 @@ setspeed(arg)
     char *ptr;
     int spd;
 
-    if (prepass)
+    if (no_override && inspeed != 0)
        return 1;
     spd = strtol(arg, &ptr, 0);
     if (ptr == arg || *ptr != 0 || spd == 0)
        return 0;
-    inspeed = spd;
+    if (!no_override || inspeed == 0)
+       inspeed = spd;
     return 1;
 }
 
@@ -1396,7 +1346,8 @@ setdevname(cp)
     if (phase != PHASE_INITIALIZE) {
        option_error("device name cannot be changed after initialization");
        return -1;
-    } else if (devnam_fixed) {
+    }
+    if (no_override) {
        option_error("per-tty options file may not specify device name");
        return -1;
     }
@@ -1427,19 +1378,18 @@ setipaddr(arg)
     char *colon;
     u_int32_t local, remote;
     ipcp_options *wo = &ipcp_wantoptions[0];
-  
+    static int seen_local = 0, seen_remote = 0;
+
     /*
      * IP address pair separated by ":".
      */
     if ((colon = strchr(arg, ':')) == NULL)
        return 0;
-    if (prepass)
-       return 1;
   
     /*
      * If colon first character, then no local addr.
      */
-    if (colon != arg) {
+    if (colon != arg && !(no_override && seen_local)) {
        *colon = '\0';
        if ((local = inet_addr(arg)) == (u_int32_t) -1) {
            if ((hp = gethostbyname(arg)) == NULL) {
@@ -1456,12 +1406,13 @@ setipaddr(arg)
        if (local != 0)
            wo->ouraddr = local;
        *colon = ':';
+       seen_local = 1;
     }
   
     /*
      * If colon last character, then no remote addr.
      */
-    if (*++colon != '\0') {
+    if (*++colon != '\0' && !(no_override && seen_remote)) {
        if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
            if ((hp = gethostbyname(colon)) == NULL) {
                option_error("unknown host: %s", colon);
@@ -1478,6 +1429,7 @@ setipaddr(arg)
        }
        if (remote != 0)
            wo->hisaddr = remote;
+       seen_remote = 1;
     }
 
     return 1;
@@ -1546,17 +1498,6 @@ parse_dotted_ip(p, vp)
     return p - p0;
 }
 
-static int
-setxonxoff(argv)
-    char **argv;
-{
-    lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
-    lcp_wantoptions[0].neg_asyncmap = 1;
-
-    crtscts = -2;
-    return (1);
-}
-
 static int
 setlogfile(argv)
     char **argv;
index 24909fb44d005e5d72273a79f2c7dc7a03208b87..6baaebcbec65baf77bda958b6d5a1244dbc155d4 100644 (file)
@@ -16,7 +16,7 @@
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
- * $Id: pppd.h,v 1.54 2000/04/15 10:10:25 paulus Exp $
+ * $Id: pppd.h,v 1.55 2000/06/30 04:54:22 paulus Exp $
  */
 
 /*
@@ -105,6 +105,7 @@ typedef struct {
 #define OPT_INITONLY   0x2000000 /* option can only be set in init phase */
 #define OPT_DEVEQUIV   0x4000000 /* equiv to device name */
 #define OPT_DEVNAM     (OPT_PREPASS | OPT_INITONLY | OPT_DEVEQUIV)
+#define OPT_SEENIT     0x10000000 /* have seen this option already */
 
 #define OPT_VAL(x)     ((x) & OPT_VALUE)
 
@@ -152,6 +153,14 @@ struct epdisc {
 #define EPD_MAGIC      4
 #define EPD_PHONENUM   5
 
+typedef void (*notify_func) __P((void *, int));
+
+struct notifier {
+    struct notifier *next;
+    notify_func            func;
+    void           *arg;
+};
+
 /*
  * Global variables.
  */
@@ -159,7 +168,6 @@ struct epdisc {
 extern int     hungup;         /* Physical layer has disconnected */
 extern int     ifunit;         /* Interface unit number */
 extern char    ifname[];       /* Interface name */
-extern int     ttyfd;          /* Serial device file descriptor */
 extern char    hostname[];     /* Our hostname */
 extern u_char  outpacket_buf[]; /* Buffer for outgoing packets */
 extern int     phase;          /* Current state of link - see values below */
@@ -186,6 +194,11 @@ extern int devnam_fixed;   /* can no longer change devnam */
 extern int     unsuccess;      /* # unsuccessful connection attempts */
 extern int     do_callback;    /* set if we want to do callback next */
 extern int     doing_callback; /* set if this is a callback */
+extern char    ppp_devnam[MAXPATHLEN];
+extern struct notifier *pidchange;   /* for notifications of pid changing */
+extern struct notifier *phasechange; /* for notifications of phase changes */
+extern struct notifier *exitnotify;  /* for notification that we're exiting */
+extern struct notifier *sigreceived; /* notification of received signal */
 
 /* Values for do_callback and doing_callback */
 #define CALLBACK_DIALIN                1       /* we are expecting the call back */
@@ -325,6 +338,9 @@ void timeout __P((void (*func)(void *), void *arg, int t));
                                /* Call func(arg) after t seconds */
 void untimeout __P((void (*func)(void *), void *arg));
                                /* Cancel call to func(arg) */
+void record_child __P((int, char *, void (*) (void *), void *));
+int  device_script __P((char *cmd, int in, int out, int dont_wait));
+                               /* Run `cmd' with given stdin and stdout */
 pid_t run_program __P((char *prog, char **args, int must_exist,
                       void (*done)(void *), void *arg));
                                /* Run program prog with args in child */
@@ -333,6 +349,16 @@ void update_link_stats __P((int)); /* Get stats at link termination */
 void script_setenv __P((char *, char *, int)); /* set script env var */
 void script_unsetenv __P((char *));            /* unset script env var */
 void new_phase __P((int));     /* signal start of new phase */
+void add_notifier __P((struct notifier **, notify_func, void *));
+void remove_notifier __P((struct notifier **, notify_func, void *));
+void notify __P((struct notifier *, int));
+
+/* Procedures exported from tty.c. */
+void tty_init __P((void));
+int  connect_tty __P((void));
+void disconnect_tty __P((void));
+void tty_close_fds __P((void));
+void cleanup_tty __P((void));
 
 /* Procedures exported from utils.c. */
 void log_packet __P((u_char *, int, char *, int));
@@ -528,6 +554,7 @@ extern void (*pap_logout_hook) __P((void));
 extern int (*pap_passwd_hook) __P((char *user, char *passwd));
 extern void (*ip_up_hook) __P((void));
 extern void (*ip_down_hook) __P((void));
+extern void (*ip_choose_hook) __P((u_int32_t *));
 
 /*
  * Inline versions of get/put char/short/long.
index 676839bf359ad52d6b2d8715fa60cdfa0cb27dc8..f25e6c3c393dc70dac0300b8ac1f270dd1273fe3 100644 (file)
@@ -282,7 +282,7 @@ void sys_init(void)
 #ifdef INET6
     sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock6_fd < 0)
-       fatal("Couldn't create IPv6 socket: %m(%d)", errno);
+       sock6_fd = -errno;      /* save errno for later */
 #endif
 
     FD_ZERO(&in_fds);
@@ -2299,6 +2299,11 @@ int sif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_rtmsg rt6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof (ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
@@ -2343,6 +2348,11 @@ int cif6addr (int unit, eui64_t our_eui64, eui64_t his_eui64)
     struct ifreq ifr;
     struct in6_ifreq ifr6;
 
+    if (sock6_fd < 0) {
+       errno = -sock6_fd;
+       error("IPv6 socket creation failed: %m");
+       return 0;
+    }
     memset(&ifr, 0, sizeof(ifr));
     strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
     if (ioctl(sock6_fd, SIOCGIFINDEX, (caddr_t) &ifr) < 0) {
diff --git a/pppd/tty.c b/pppd/tty.c
new file mode 100644 (file)
index 0000000..3e02e27
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+ * tty.c - code for handling serial ports in pppd.
+ *
+ * Copyright (C) 2000 Paul Mackerras.
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#define RCSID  "$Id: tty.c,v 1.1 2000/06/30 04:54:23 paulus Exp $"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+
+static int setxonxoff __P((char **));
+static void finish_tty __P((void));
+static int start_charshunt __P((int, int));
+static void stop_charshunt __P((void *, int));
+static void charshunt_done __P((void *));
+static void charshunt __P((int, int, char *));
+static int record_write __P((FILE *, int code, u_char *buf, int nb,
+                            struct timeval *));
+static int open_socket __P((char *));
+static void maybe_relock __P((void *, int));
+
+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 */
+
+mode_t tty_mode = (mode_t)-1;  /* Original access permissions to tty */
+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 */
+
+/* option variables */
+int    crtscts = 0;            /* Use hardware flow control */
+bool   modem = 1;              /* Use modem control lines */
+int    inspeed = 0;            /* Input/Output speed requested */
+bool   lockflag = 0;           /* Create lock file to lock the serial dev */
+char   *initializer = NULL;    /* Script to initialize physical link */
+char   *connect_script = NULL; /* Script to establish physical link */
+char   *disconnect_script = NULL; /* Script to disestablish physical link */
+char   *welcomer = NULL;       /* Script to run after phys link estab. */
+char   *ptycommand = NULL;     /* Command to run on other side of pty */
+bool   notty = 0;              /* Stdin/out is not a tty */
+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;
+
+extern uid_t uid;
+extern int kill_link;
+
+/* XXX */
+extern int privopen;           /* don't lock, open device as root */
+
+/* option descriptors */
+option_t tty_options[] = {
+    { "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 },
+    { "connect", o_string, &connect_script,
+      "A program to set up a connection",
+      OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
+    { "disconnect", o_string, &disconnect_script,
+      "Program to disconnect serial device",
+      OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
+    { "welcome", o_string, &welcomer,
+      "Script to welcome client",
+      OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
+    { "pty", o_string, &ptycommand,
+      "Script to run on pseudo-tty master side",
+      OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
+    { "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 },
+    { "record", o_string, &record_file,
+      "Record characters sent/received to file" },
+    { "crtscts", o_int, &crtscts,
+      "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
+    { "nocrtscts", o_int, &crtscts,
+      "Disable hardware flow control", 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) },
+    { "nocdtrcts", o_int, &crtscts,
+      "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
+    { "xonxoff", o_special_noarg, (void *)setxonxoff,
+      "Set software (XON/XOFF) flow control" },
+    { "modem", o_bool, &modem,
+      "Use modem control lines", 1 },
+    { "local", o_bool, &modem,
+      "Don't use modem control lines" },
+    { "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)" },
+    { NULL }
+};
+
+static int
+setxonxoff(argv)
+    char **argv;
+{
+       lcp_wantoptions[0].asyncmap |= 0x000A0000;      /* escape ^S and ^Q */
+       lcp_wantoptions[0].neg_asyncmap = 1;
+
+       crtscts = -2;
+       return 1;
+}
+
+/*
+ * tty_init - do various tty-related initializations.
+ */
+void tty_init()
+{
+    add_notifier(&pidchange, maybe_relock, 0);
+    add_options(tty_options);
+}
+
+/*
+ * connect_tty - get the serial port ready to start doing PPP.
+ * That is, open the serial port, set its speed and mode, and run
+ * the connector and/or welcomer.
+ */
+int connect_tty()
+{
+       char *connector;
+       int fdflags;
+       struct stat statbuf;
+       char numbuf[16];
+
+       /*
+        * Get a pty master/slave pair if the pty, notty, socket,
+        * or record options were specified.
+        */
+       strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
+       pty_master = -1;
+       pty_slave = -1;
+       real_ttyfd = -1;
+       if (using_pty || record_file != NULL) {
+               if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
+                       error("Couldn't allocate pseudo-tty");
+                       status = EXIT_FATAL_ERROR;
+                       return -1;
+               }
+               set_up_tty(pty_slave, 1);
+       }
+
+       /*
+        * Lock the device if we've been asked to.
+        */
+       status = EXIT_LOCK_FAILED;
+       if (lockflag && !privopen) {
+               if (lock(devnam) < 0)
+                       return -1;
+               locked = 1;
+       }
+
+       /*
+        * Open the serial device and set it up to be the ppp interface.
+        * First we open it in non-blocking mode so we can set the
+        * various termios flags appropriately.  If we aren't dialling
+        * out and we want to use the modem lines, we reopen it later
+        * in order to wait for the carrier detect signal from the modem.
+        */
+       hungup = 0;
+       kill_link = 0;
+       connector = doing_callback? callback_script: connect_script;
+       if (devnam[0] != 0) {
+               for (;;) {
+                       /* If the user specified the device name, become the
+                          user before opening it. */
+                       int err;
+                       if (!devnam_info.priv && !privopen)
+                               seteuid(uid);
+                       ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0);
+                       err = errno;
+                       if (!devnam_info.priv && !privopen)
+                               seteuid(0);
+                       if (ttyfd >= 0)
+                               break;
+                       errno = err;
+                       if (err != EINTR) {
+                               error("Failed to open %s: %m", devnam);
+                               status = EXIT_OPEN_FAILED;
+                       }
+                       if (!persist || err != EINTR)
+                               return -1;
+               }
+               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");
+
+               /*
+                * Do the equivalent of `mesg n' to stop broadcast messages.
+                */
+               if (fstat(ttyfd, &statbuf) < 0
+                   || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+                       warn("Couldn't restrict write permissions to %s: %m", devnam);
+               } else
+                       tty_mode = statbuf.st_mode;
+
+               /*
+                * Set line speed, flow control, etc.
+                * If we have a non-null connection or initializer script,
+                * on most systems we set CLOCAL for now so that we can talk
+                * to the modem before carrier comes up.  But this has the
+                * side effect that we might miss it if CD drops before we
+                * get to clear CLOCAL below.  On systems where we can talk
+                * successfully to the modem with CLOCAL clear and CD down,
+                * we could clear CLOCAL at this point.
+                */
+               set_up_tty(ttyfd, ((connector != NULL && connector[0] != 0)
+                                  || initializer != NULL));
+               real_ttyfd = ttyfd;
+       }
+
+       /*
+        * If the pty, socket, notty and/or record option was specified,
+        * start up the character shunt now.
+        */
+       status = EXIT_PTYCMD_FAILED;
+       if (ptycommand != NULL) {
+               if (record_file != NULL) {
+                       int ipipe[2], opipe[2], ok;
+
+                       if (pipe(ipipe) < 0 || pipe(opipe) < 0)
+                               fatal("Couldn't create pipes for record option: %m");
+                       ok = device_script(ptycommand, opipe[0], ipipe[1], 1) == 0
+                               && start_charshunt(ipipe[0], opipe[1]);
+                       close(ipipe[0]);
+                       close(ipipe[1]);
+                       close(opipe[0]);
+                       close(opipe[1]);
+                       if (!ok)
+                               return -1;
+               } else {
+                       if (device_script(ptycommand, pty_master, pty_master, 1) < 0)
+                               return -1;
+                       ttyfd = pty_slave;
+                       close(pty_master);
+                       pty_master = -1;
+               }
+       } else if (pty_socket != NULL) {
+               int fd = open_socket(pty_socket);
+               if (fd < 0)
+                       return -1;
+               if (!start_charshunt(fd, fd))
+                       return -1;
+       } else if (notty) {
+               if (!start_charshunt(0, 1))
+                       return -1;
+       } else if (record_file != NULL) {
+               if (!start_charshunt(ttyfd, ttyfd))
+                       return -1;
+       }
+
+       /* run connection script */
+       if ((connector && connector[0]) || initializer) {
+               if (real_ttyfd != -1) {
+                       /* XXX do this if doing_callback == CALLBACK_DIALIN? */
+                       if (!default_device && modem) {
+                               setdtr(real_ttyfd, 0);  /* in case modem is off hook */
+                               sleep(1);
+                               setdtr(real_ttyfd, 1);
+                       }
+               }
+
+               if (initializer && initializer[0]) {
+                       if (device_script(initializer, ttyfd, ttyfd, 0) < 0) {
+                               error("Initializer script failed");
+                               status = EXIT_INIT_FAILED;
+                               return -1;
+                       }
+                       if (kill_link) {
+                               disconnect_tty();
+                               return -1;
+                       }
+                       info("Serial port initialized.");
+               }
+
+               if (connector && connector[0]) {
+                       if (device_script(connector, ttyfd, ttyfd, 0) < 0) {
+                               error("Connect script failed");
+                               status = EXIT_CONNECT_FAILED;
+                               return -1;
+                       }
+                       if (kill_link) {
+                               disconnect_tty();
+                               return -1;
+                       }
+                       info("Serial connection established.");
+               }
+
+               /* set line speed, flow control, etc.;
+                  clear CLOCAL if modem option */
+               if (real_ttyfd != -1)
+                       set_up_tty(real_ttyfd, 0);
+
+               if (doing_callback == CALLBACK_DIALIN)
+                       connector = NULL;
+       }
+
+       /* reopen tty if necessary to wait for carrier */
+       if (connector == NULL && modem && devnam[0] != 0) {
+               int i;
+               for (;;) {
+                       if ((i = open(devnam, O_RDWR)) >= 0)
+                               break;
+                       if (errno != EINTR) {
+                               error("Failed to reopen %s: %m", devnam);
+                               status = EXIT_OPEN_FAILED;
+                       }
+                       if (!persist || errno != EINTR || hungup || kill_link)
+                               return -1;
+               }
+               close(i);
+       }
+
+       slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
+       script_setenv("SPEED", numbuf, 0);
+
+       /* run welcome script, if any */
+       if (welcomer && welcomer[0]) {
+               if (device_script(welcomer, ttyfd, ttyfd, 0) < 0)
+                       warn("Welcome script failed");
+       }
+
+       return ttyfd;
+}
+
+
+void disconnect_tty()
+{
+       if (disconnect_script == NULL || hungup)
+               return;
+       if (real_ttyfd >= 0)
+               set_up_tty(real_ttyfd, 1);
+       if (device_script(disconnect_script, ttyfd, ttyfd, 0) < 0) {
+               warn("disconnect script failed");
+       } else {
+               info("Serial link disconnected.");
+       }
+}
+
+void tty_close_fds()
+{
+       if (pty_master >= 0)
+               close(pty_master);
+       if (pty_slave >= 0)
+               close(pty_slave);
+       if (real_ttyfd >= 0) {
+               close(real_ttyfd);
+               real_ttyfd = -1;
+       }
+       /* N.B. ttyfd will == either pty_slave or real_ttyfd */
+}
+
+void cleanup_tty()
+{
+       if (real_ttyfd >= 0)
+               finish_tty();
+       tty_close_fds();
+       if (locked) {
+               unlock();
+               locked = 0;
+       }
+}
+
+/*
+ * finish_tty - restore the terminal device to its original settings
+ */
+static void
+finish_tty()
+{
+       /* drop dtr to hang up */
+       if (!default_device && modem) {
+               setdtr(real_ttyfd, 0);
+               /*
+                * This sleep is in case the serial port has CLOCAL set by default,
+                * and consequently will reassert DTR when we close the device.
+                */
+               sleep(1);
+       }
+
+       restore_tty(real_ttyfd);
+
+       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);
+               }
+       }
+
+       close(real_ttyfd);
+       real_ttyfd = -1;
+}
+
+/*
+ * maybe_relock - our PID has changed, maybe update the lock file.
+ */
+static void
+maybe_relock(arg, pid)
+    void *arg;
+    int pid;
+{
+    if (locked)
+       relock(pid);
+}
+
+/*
+ * open_socket - establish a stream socket connection to the nominated
+ * host and port.
+ */
+static int
+open_socket(dest)
+    char *dest;
+{
+    char *sep, *endp = NULL;
+    int sock, port = -1;
+    u_int32_t host;
+    struct hostent *hent;
+    struct sockaddr_in sad;
+
+    /* parse host:port and resolve host to an IP address */
+    sep = strchr(dest, ':');
+    if (sep != NULL)
+       port = strtol(sep+1, &endp, 10);
+    if (port < 0 || endp == sep+1 || sep == dest) {
+       error("Can't parse host:port for socket destination");
+       return -1;
+    }
+    *sep = 0;
+    host = inet_addr(dest);
+    if (host == (u_int32_t) -1) {
+       hent = gethostbyname(dest);
+       if (hent == NULL) {
+           error("%s: unknown host in socket option", dest);
+           *sep = ':';
+           return -1;
+       }
+       host = *(u_int32_t *)(hent->h_addr_list[0]);
+    }
+    *sep = ':';
+
+    /* get a socket and connect it to the other end */
+    sock = socket(PF_INET, SOCK_STREAM, 0);
+    if (sock < 0) {
+       error("Can't create socket: %m");
+       return -1;
+    }
+    memset(&sad, 0, sizeof(sad));
+    sad.sin_family = AF_INET;
+    sad.sin_port = htons(port);
+    sad.sin_addr.s_addr = host;
+    if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
+       error("Can't connect to %s: %m", dest);
+       close(sock);
+       return -1;
+    }
+
+    return sock;
+}
+
+
+/*
+ * start_charshunt - create a child process to run the character shunt.
+ */
+static int
+start_charshunt(ifd, ofd)
+    int ifd, ofd;
+{
+    int cpid;
+
+    cpid = fork();
+    if (cpid == -1) {
+       error("Can't fork process for character shunt: %m");
+       return 0;
+    }
+    if (cpid == 0) {
+       /* child */
+       close(pty_slave);
+       setuid(uid);
+       if (getuid() != uid)
+           fatal("setuid failed");
+       setgid(getgid());
+       if (!nodetach)
+           log_to_fd = -1;
+       charshunt(ifd, ofd, record_file);
+       exit(0);
+    }
+    charshunt_pid = cpid;
+    add_notifier(&sigreceived, stop_charshunt, 0);
+    close(pty_master);
+    pty_master = -1;
+    ttyfd = pty_slave;
+    record_child(cpid, "pppd (charshunt)", charshunt_done, NULL);
+    return 1;
+}
+
+static void
+charshunt_done(arg)
+    void *arg;
+{
+       charshunt_pid = 0;
+}
+
+static void
+stop_charshunt(arg, sig)
+    void *arg;
+    int sig;
+{
+       if (charshunt_pid)
+               kill(charshunt_pid, (sig == SIGINT? sig: SIGTERM));
+}
+
+/*
+ * charshunt - the character shunt, which passes characters between
+ * the pty master side and the serial port (or stdin/stdout).
+ * This runs as the user (not as root).
+ * (We assume ofd >= ifd which is true the way this gets called. :-).
+ */
+static void
+charshunt(ifd, ofd, record_file)
+    int ifd, ofd;
+    char *record_file;
+{
+    int n, nfds;
+    fd_set ready, writey;
+    u_char *ibufp, *obufp;
+    int nibuf, nobuf;
+    int flags;
+    int pty_readable, stdin_readable;
+    struct timeval lasttime;
+    FILE *recordf = NULL;
+    int ilevel, olevel, max_level;
+    struct timeval levelt, tout, *top;
+    extern u_char inpacket_buf[];
+
+    /*
+     * Reset signal handlers.
+     */
+    signal(SIGHUP, SIG_IGN);           /* Hangup */
+    signal(SIGINT, SIG_DFL);           /* Interrupt */
+    signal(SIGTERM, SIG_DFL);          /* Terminate */
+    signal(SIGCHLD, SIG_DFL);
+    signal(SIGUSR1, SIG_DFL);
+    signal(SIGUSR2, SIG_DFL);
+    signal(SIGABRT, SIG_DFL);
+    signal(SIGALRM, SIG_DFL);
+    signal(SIGFPE, SIG_DFL);
+    signal(SIGILL, SIG_DFL);
+    signal(SIGPIPE, SIG_DFL);
+    signal(SIGQUIT, SIG_DFL);
+    signal(SIGSEGV, SIG_DFL);
+#ifdef SIGBUS
+    signal(SIGBUS, SIG_DFL);
+#endif
+#ifdef SIGEMT
+    signal(SIGEMT, SIG_DFL);
+#endif
+#ifdef SIGPOLL
+    signal(SIGPOLL, SIG_DFL);
+#endif
+#ifdef SIGPROF
+    signal(SIGPROF, SIG_DFL);
+#endif
+#ifdef SIGSYS
+    signal(SIGSYS, SIG_DFL);
+#endif
+#ifdef SIGTRAP
+    signal(SIGTRAP, SIG_DFL);
+#endif
+#ifdef SIGVTALRM
+    signal(SIGVTALRM, SIG_DFL);
+#endif
+#ifdef SIGXCPU
+    signal(SIGXCPU, SIG_DFL);
+#endif
+#ifdef SIGXFSZ
+    signal(SIGXFSZ, SIG_DFL);
+#endif
+
+    /*
+     * Open the record file if required.
+     */
+    if (record_file != NULL) {
+       recordf = fopen(record_file, "a");
+       if (recordf == NULL)
+           error("Couldn't create record file %s: %m", record_file);
+    }
+
+    /* set all the fds to non-blocking mode */
+    flags = fcntl(pty_master, F_GETFL);
+    if (flags == -1
+       || fcntl(pty_master, F_SETFL, flags | O_NONBLOCK) == -1)
+       warn("couldn't set pty master to nonblock: %m");
+    flags = fcntl(ifd, F_GETFL);
+    if (flags == -1
+       || fcntl(ifd, F_SETFL, flags | O_NONBLOCK) == -1)
+       warn("couldn't set %s to nonblock: %m", (ifd==0? "stdin": "tty"));
+    if (ofd != ifd) {
+       flags = fcntl(ofd, F_GETFL);
+       if (flags == -1
+           || fcntl(ofd, F_SETFL, flags | O_NONBLOCK) == -1)
+           warn("couldn't set stdout to nonblock: %m");
+    }
+
+    nibuf = nobuf = 0;
+    ibufp = obufp = NULL;
+    pty_readable = stdin_readable = 1;
+
+    ilevel = olevel = 0;
+    gettimeofday(&levelt, NULL);
+    if (max_data_rate) {
+       max_level = max_data_rate / 10;
+       if (max_level < 100)
+           max_level = 100;
+    } else
+       max_level = PPP_MRU + PPP_HDRLEN + 1;
+
+    nfds = (ofd > pty_master? ofd: pty_master) + 1;
+    if (recordf != NULL) {
+       gettimeofday(&lasttime, NULL);
+       putc(7, recordf);       /* put start marker */
+       putc(lasttime.tv_sec >> 24, recordf);
+       putc(lasttime.tv_sec >> 16, recordf);
+       putc(lasttime.tv_sec >> 8, recordf);
+       putc(lasttime.tv_sec, recordf);
+       lasttime.tv_usec = 0;
+    }
+
+    while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
+       top = 0;
+       tout.tv_sec = 0;
+       tout.tv_usec = 10000;
+       FD_ZERO(&ready);
+       FD_ZERO(&writey);
+       if (nibuf != 0) {
+           if (ilevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(pty_master, &writey);
+       } else if (stdin_readable)
+           FD_SET(ifd, &ready);
+       if (nobuf != 0) {
+           if (olevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(ofd, &writey);
+       } else if (pty_readable)
+           FD_SET(pty_master, &ready);
+       if (select(nfds, &ready, &writey, NULL, top) < 0) {
+           if (errno != EINTR)
+               fatal("select");
+           continue;
+       }
+       if (max_data_rate) {
+           double dt;
+           int nbt;
+           struct timeval now;
+
+           gettimeofday(&now, NULL);
+           dt = (now.tv_sec - levelt.tv_sec
+                 + (now.tv_usec - levelt.tv_usec) / 1e6);
+           nbt = (int)(dt * max_data_rate);
+           ilevel = (nbt < 0 || nbt > ilevel)? 0: ilevel - nbt;
+           olevel = (nbt < 0 || nbt > olevel)? 0: olevel - nbt;
+           levelt = now;
+       } else
+           ilevel = olevel = 0;
+       if (FD_ISSET(ifd, &ready)) {
+           ibufp = inpacket_buf;
+           nibuf = read(ifd, ibufp, PPP_MRU + PPP_HDRLEN);
+           if (nibuf < 0 && errno == EIO)
+               nibuf = 0;
+           if (nibuf < 0) {
+               if (!(errno == EINTR || errno == EAGAIN)) {
+                   error("Error reading standard input: %m");
+                   break;
+               }
+               nibuf = 0;
+           } else if (nibuf == 0) {
+               /* end of file from stdin */
+               stdin_readable = 0;
+               /* do a 0-length write, hopefully this will generate
+                  an EOF (hangup) on the slave side. */
+               write(pty_master, inpacket_buf, 0);
+               if (recordf)
+                   if (!record_write(recordf, 4, NULL, 0, &lasttime))
+                       recordf = NULL;
+           } else {
+               FD_SET(pty_master, &writey);
+               if (recordf)
+                   if (!record_write(recordf, 2, ibufp, nibuf, &lasttime))
+                       recordf = NULL;
+           }
+       }
+       if (FD_ISSET(pty_master, &ready)) {
+           obufp = outpacket_buf;
+           nobuf = read(pty_master, obufp, PPP_MRU + PPP_HDRLEN);
+           if (nobuf < 0 && errno == EIO)
+               nobuf = 0;
+           if (nobuf < 0) {
+               if (!(errno == EINTR || errno == EAGAIN)) {
+                   error("Error reading pseudo-tty master: %m");
+                   break;
+               }
+               nobuf = 0;
+           } else if (nobuf == 0) {
+               /* end of file from the pty - slave side has closed */
+               pty_readable = 0;
+               stdin_readable = 0;     /* pty is not writable now */
+               nibuf = 0;
+               close(ofd);
+               if (recordf)
+                   if (!record_write(recordf, 3, NULL, 0, &lasttime))
+                       recordf = NULL;
+           } else {
+               FD_SET(ofd, &writey);
+               if (recordf)
+                   if (!record_write(recordf, 1, obufp, nobuf, &lasttime))
+                       recordf = NULL;
+           }
+       }
+       if (FD_ISSET(ofd, &writey)) {
+           n = nobuf;
+           if (olevel + n > max_level)
+               n = max_level - olevel;
+           n = write(ofd, obufp, n);
+           if (n < 0) {
+               if (errno == EIO) {
+                   pty_readable = 0;
+                   nobuf = 0;
+               } else if (errno != EAGAIN && errno != EINTR) {
+                   error("Error writing standard output: %m");
+                   break;
+               }
+           } else {
+               obufp += n;
+               nobuf -= n;
+               olevel += n;
+           }
+       }
+       if (FD_ISSET(pty_master, &writey)) {
+           n = nibuf;
+           if (ilevel + n > max_level)
+               n = max_level - ilevel;
+           n = write(pty_master, ibufp, n);
+           if (n < 0) {
+               if (errno == EIO) {
+                   stdin_readable = 0;
+                   nibuf = 0;
+               } else if (errno != EAGAIN && errno != EINTR) {
+                   error("Error writing pseudo-tty master: %m");
+                   break;
+               }
+           } else {
+               ibufp += n;
+               nibuf -= n;
+               ilevel += n;
+           }
+       }
+    }
+    exit(0);
+}
+
+static int
+record_write(f, code, buf, nb, tp)
+    FILE *f;
+    int code;
+    u_char *buf;
+    int nb;
+    struct timeval *tp;
+{
+    struct timeval now;
+    int diff;
+
+    gettimeofday(&now, NULL);
+    now.tv_usec /= 100000;     /* actually 1/10 s, not usec now */
+    diff = (now.tv_sec - tp->tv_sec) * 10 + (now.tv_usec - tp->tv_usec);
+    if (diff > 0) {
+       if (diff > 255) {
+           putc(5, f);
+           putc(diff >> 24, f);
+           putc(diff >> 16, f);
+           putc(diff >> 8, f);
+           putc(diff, f);
+       } else {
+           putc(6, f);
+           putc(diff, f);
+       }
+       *tp = now;
+    }
+    putc(code, f);
+    if (buf != NULL) {
+       putc(nb >> 8, f);
+       putc(nb, f);
+       fwrite(buf, nb, 1, f);
+    }
+    fflush(f);
+    if (ferror(f)) {
+       error("Error writing record file: %m");
+       return 0;
+    }
+    return 1;
+}