]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/main.c
don't fall over in the charshunt if we get an EAGAIN or EINTR
[ppp.git] / pppd / main.c
index 954fc2b6b977df84133bee29ffa5125095af7011..0722e9104076417b68500f16058a98f31f1e8f2d 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.89 2000/01/18 19:49:52 masputra Exp $"
+#define RCSID  "$Id: main.c,v 1.98 2000/04/29 12:32:59 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
 
 #include <stdio.h>
 #include <ctype.h>
@@ -40,6 +40,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "pppd.h"
 #include "magic.h"
 
 #include "pppd.h"
 #include "magic.h"
@@ -54,6 +55,7 @@
 #include "ccp.h"
 #include "pathnames.h"
 #include "patchlevel.h"
 #include "ccp.h"
 #include "pathnames.h"
 #include "patchlevel.h"
+#include "tdb.h"
 
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
 
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
@@ -81,7 +83,7 @@ static uid_t uid;             /* Our real user-id */
 static int conn_running;       /* we have a [dis]connector running */
 
 int ttyfd;                     /* Serial port file descriptor */
 static int conn_running;       /* we have a [dis]connector running */
 
 int ttyfd;                     /* Serial port file descriptor */
-mode_t tty_mode = -1;          /* Original access permissions to tty */
+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 baud_rate;                 /* Actual bits/second for serial device */
 int hungup;                    /* terminal has been hung up */
 int privileged;                        /* we're running as real uid root */
@@ -95,6 +97,8 @@ 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 */
 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;
 
 int (*holdoff_hook) __P((void)) = NULL;
 int (*new_phase_hook) __P((int)) = NULL;
@@ -153,6 +157,7 @@ static struct subprocess *children;
 
 /* Prototypes for procedures local to this file. */
 
 
 /* Prototypes for procedures local to this file. */
 
+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 create_pidfile __P((void));
 static void create_linkpidfile __P((void));
 static void cleanup __P((void));
@@ -171,6 +176,11 @@ 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 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 start_charshunt __P((int, int));
 static void charshunt_done __P((void *));
 static void charshunt __P((int, int, char *));
@@ -216,13 +226,20 @@ struct protent *protocols[] = {
     NULL
 };
 
     NULL
 };
 
+/*
+ * If PPP_DRV_NAME is not defined, use the legacy "ppp" as the
+ * device name.
+ */
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME   "ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
 int
 main(argc, argv)
     int argc;
     char *argv[];
 {
     int i, fdflags, t;
 int
 main(argc, argv)
     int argc;
     char *argv[];
 {
     int i, fdflags, t;
-    struct sigaction sa;
     char *p, *connector;
     struct passwd *pw;
     struct timeval timo;
     char *p, *connector;
     struct passwd *pw;
     struct timeval timo;
@@ -262,7 +279,7 @@ main(argc, argv)
     uid = getuid();
     privileged = uid == 0;
     slprintf(numbuf, sizeof(numbuf), "%d", uid);
     uid = getuid();
     privileged = uid == 0;
     slprintf(numbuf, sizeof(numbuf), "%d", uid);
-    script_setenv("ORIG_UID", numbuf);
+    script_setenv("ORIG_UID", numbuf, 0);
 
     ngroups = getgroups(NGROUPS_MAX, groups);
 
 
     ngroups = getgroups(NGROUPS_MAX, groups);
 
@@ -295,7 +312,7 @@ main(argc, argv)
     /*
      * Work out the device name, if it hasn't already been specified.
      */
     /*
      * Work out the device name, if it hasn't already been specified.
      */
-    using_pty = notty || ptycommand != NULL;
+    using_pty = notty || ptycommand != NULL || pty_socket != NULL;
     if (!using_pty && default_device) {
        char *p;
        if (!isatty(0) || (p = ttyname(0)) == NULL) {
     if (!using_pty && default_device) {
        char *p;
        if (!isatty(0) || (p = ttyname(0)) == NULL) {
@@ -310,7 +327,7 @@ main(argc, argv)
     /*
      * Parse the tty options file and the command line.
      * The per-tty options file should not change
     /*
      * Parse the tty options file and the command line.
      * The per-tty options file should not change
-     * ptycommand, notty or devnam.
+     * ptycommand, pty_socket, notty or devnam.
      */
     devnam_fixed = 1;
     if (!using_pty) {
      */
     devnam_fixed = 1;
     if (!using_pty) {
@@ -342,6 +359,9 @@ main(argc, argv)
     if (!sys_check_options())
        exit(EXIT_OPTION_ERROR);
     auth_check_options();
     if (!sys_check_options())
        exit(EXIT_OPTION_ERROR);
     auth_check_options();
+#ifdef HAVE_MULTILINK
+    mp_check_options();
+#endif
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
        if (protp->check_options != NULL)
            (*protp->check_options)();
     for (i = 0; (protp = protocols[i]) != NULL; ++i)
        if (protp->check_options != NULL)
            (*protp->check_options)();
@@ -363,6 +383,10 @@ main(argc, argv)
            option_error("pty option is incompatible with notty option");
            exit(EXIT_OPTION_ERROR);
        }
            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;
        default_device = notty;
        lockflag = 0;
        modem = 0;
@@ -394,8 +418,6 @@ main(argc, argv)
        && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
        log_to_fd = -1;
 
        && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
        log_to_fd = -1;
 
-    script_setenv("DEVICE", devnam);
-
     /*
      * Initialize system-dependent stuff.
      */
     /*
      * Initialize system-dependent stuff.
      */
@@ -403,6 +425,18 @@ main(argc, argv)
     if (debug)
        setlogmask(LOG_UPTO(LOG_DEBUG));
 
     if (debug)
        setlogmask(LOG_UPTO(LOG_DEBUG));
 
+    pppdb = tdb_open(_PATH_PPPDB, 0, 0, O_RDWR|O_CREAT, 0644);
+    if (pppdb != NULL) {
+       slprintf(db_key, sizeof(db_key), "pppd%d", getpid());
+       update_db_entry();
+    } else {
+       warn("Warning: couldn't open ppp database %s", _PATH_PPPDB);
+       if (multilink) {
+           warn("Warning: disabling multilink");
+           multilink = 0;
+       }
+    }
+
     /*
      * Detach ourselves from the terminal, if required,
      * and identify who is running us.
     /*
      * Detach ourselves from the terminal, if required,
      * and identify who is running us.
@@ -419,81 +453,14 @@ main(argc, argv)
     }
     syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
           VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
     }
     syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
           VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
-    script_setenv("PPPLOGNAME", p);
-
-    /*
-     * Compute mask of all interesting signals and install signal handlers
-     * for each.  Only one signal handler may be active at a time.  Therefore,
-     * all other signals should be masked when any handler is executing.
-     */
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGHUP);
-    sigaddset(&mask, SIGINT);
-    sigaddset(&mask, SIGTERM);
-    sigaddset(&mask, SIGCHLD);
-    sigaddset(&mask, SIGUSR2);
+    script_setenv("PPPLOGNAME", p, 0);
 
 
-#define SIGNAL(s, handler)     do { \
-       sa.sa_handler = handler; \
-       if (sigaction(s, &sa, NULL) < 0) \
-           fatal("Couldn't establish signal handler (%d): %m", s); \
-    } while (0)
-
-    sa.sa_mask = mask;
-    sa.sa_flags = 0;
-    SIGNAL(SIGHUP, hup);               /* Hangup */
-    SIGNAL(SIGINT, term);              /* Interrupt */
-    SIGNAL(SIGTERM, term);             /* Terminate */
-    SIGNAL(SIGCHLD, chld);
-
-    SIGNAL(SIGUSR1, toggle_debug);     /* Toggle debug flag */
-    SIGNAL(SIGUSR2, open_ccp);         /* Reopen CCP */
-
-    /*
-     * Install a handler for other signals which would otherwise
-     * cause pppd to exit without cleaning up.
-     */
-    SIGNAL(SIGABRT, bad_signal);
-    SIGNAL(SIGALRM, bad_signal);
-    SIGNAL(SIGFPE, bad_signal);
-    SIGNAL(SIGILL, bad_signal);
-    SIGNAL(SIGPIPE, bad_signal);
-    SIGNAL(SIGQUIT, bad_signal);
-    SIGNAL(SIGSEGV, bad_signal);
-#ifdef SIGBUS
-    SIGNAL(SIGBUS, bad_signal);
-#endif
-#ifdef SIGEMT
-    SIGNAL(SIGEMT, bad_signal);
-#endif
-#ifdef SIGPOLL
-    SIGNAL(SIGPOLL, bad_signal);
-#endif
-#ifdef SIGPROF
-    SIGNAL(SIGPROF, bad_signal);
-#endif
-#ifdef SIGSYS
-    SIGNAL(SIGSYS, bad_signal);
-#endif
-#ifdef SIGTRAP
-    SIGNAL(SIGTRAP, bad_signal);
-#endif
-#ifdef SIGVTALRM
-    SIGNAL(SIGVTALRM, bad_signal);
-#endif
-#ifdef SIGXCPU
-    SIGNAL(SIGXCPU, bad_signal);
-#endif
-#ifdef SIGXFSZ
-    SIGNAL(SIGXFSZ, bad_signal);
-#endif
+    if (devnam[0])
+       script_setenv("DEVICE", devnam, 1);
+    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+    script_setenv("PPPD_PID", numbuf, 1);
 
 
-    /*
-     * Apparently we can get a SIGPIPE when we call syslog, if
-     * syslogd has died and been restarted.  Ignoring it seems
-     * be sufficient.
-     */
-    signal(SIGPIPE, SIG_IGN);
+    setup_signals();
 
     waiting = 0;
 
 
     waiting = 0;
 
@@ -506,13 +473,10 @@ main(argc, argv)
        /*
         * Open the loopback channel and set it up to be the ppp interface.
         */
        /*
         * Open the loopback channel and set it up to be the ppp interface.
         */
+       tdb_writelock(pppdb);
        fd_loop = open_ppp_loopback();
        fd_loop = open_ppp_loopback();
-
-       syslog(LOG_INFO, "Using interface ppp%d", ifunit);
-       slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
-       script_setenv("IFNAME", ifname);
-
-       create_pidfile();       /* write pid to file */
+       set_ifunit(1);
+       tdb_writeunlock(pppdb);
 
        /*
         * Configure the interface and mark it up, etc.
 
        /*
         * Configure the interface and mark it up, etc.
@@ -576,13 +540,13 @@ main(argc, argv)
        new_phase(PHASE_SERIALCONN);
 
        /*
        new_phase(PHASE_SERIALCONN);
 
        /*
-        * Get a pty master/slave pair if the pty, notty, or record
-        * options were specified.
+        * 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;
         */
        strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam));
        pty_master = -1;
        pty_slave = -1;
-       if (ptycommand != NULL || notty || record_file != NULL) {
+       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;
            if (!get_pty(&pty_master, &pty_slave, ppp_devnam, uid)) {
                error("Couldn't allocate pseudo-tty");
                status = EXIT_FATAL_ERROR;
@@ -661,7 +625,7 @@ main(argc, argv)
        }
 
        /*
        }
 
        /*
-        * If the notty and/or record option was specified,
+        * If the pty, socket, notty and/or record option was specified,
         * start up the character shunt now.
         */
        status = EXIT_PTYCMD_FAILED;
         * start up the character shunt now.
         */
        status = EXIT_PTYCMD_FAILED;
@@ -686,6 +650,12 @@ main(argc, argv)
                close(pty_master);
                pty_master = -1;
            }
                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 (notty) {
            if (!start_charshunt(0, 1))
                goto fail;
@@ -754,7 +724,7 @@ main(argc, argv)
        }
 
        slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
        }
 
        slprintf(numbuf, sizeof(numbuf), "%d", baud_rate);
-       script_setenv("SPEED", numbuf);
+       script_setenv("SPEED", numbuf, 0);
 
        /* run welcome script, if any */
        if (welcomer && welcomer[0]) {
 
        /* run welcome script, if any */
        if (welcomer && welcomer[0]) {
@@ -763,20 +733,17 @@ main(argc, argv)
        }
 
        /* set up the serial device as a ppp interface */
        }
 
        /* set up the serial device as a ppp interface */
+       tdb_writelock(pppdb);
        fd_ppp = establish_ppp(ttyfd);
        if (fd_ppp < 0) {
        fd_ppp = establish_ppp(ttyfd);
        if (fd_ppp < 0) {
+           tdb_writeunlock(pppdb);
            status = EXIT_FATAL_ERROR;
            goto disconnect;
        }
 
            status = EXIT_FATAL_ERROR;
            goto disconnect;
        }
 
-       if (!demand) {
-           
-           info("Using interface ppp%d", ifunit);
-           slprintf(ifname, sizeof(ifname), "ppp%d", ifunit);
-           script_setenv("IFNAME", ifname);
-
-           create_pidfile();   /* write pid to file */
-       }
+       if (!demand && ifunit >= 0)
+           set_ifunit(1);
+       tdb_writeunlock(pppdb);
 
        /*
         * Start opening the connection and wait for
 
        /*
         * Start opening the connection and wait for
@@ -871,6 +838,8 @@ main(argc, argv)
        fd_ppp = -1;
        if (!hungup)
            lcp_lowerdown(0);
        fd_ppp = -1;
        if (!hungup)
            lcp_lowerdown(0);
+       if (!demand)
+           script_unsetenv("IFNAME");
 
        /*
         * Run disconnector script, if requested.
 
        /*
         * Run disconnector script, if requested.
@@ -961,6 +930,107 @@ main(argc, argv)
     return 0;
 }
 
     return 0;
 }
 
+/*
+ * setup_signals - initialize signal handling.
+ */
+static void
+setup_signals()
+{
+    struct sigaction sa;
+    sigset_t mask;
+
+    /*
+     * Compute mask of all interesting signals and install signal handlers
+     * for each.  Only one signal handler may be active at a time.  Therefore,
+     * all other signals should be masked when any handler is executing.
+     */
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGHUP);
+    sigaddset(&mask, SIGINT);
+    sigaddset(&mask, SIGTERM);
+    sigaddset(&mask, SIGCHLD);
+    sigaddset(&mask, SIGUSR2);
+
+#define SIGNAL(s, handler)     do { \
+       sa.sa_handler = handler; \
+       if (sigaction(s, &sa, NULL) < 0) \
+           fatal("Couldn't establish signal handler (%d): %m", s); \
+    } while (0)
+
+    sa.sa_mask = mask;
+    sa.sa_flags = 0;
+    SIGNAL(SIGHUP, hup);               /* Hangup */
+    SIGNAL(SIGINT, term);              /* Interrupt */
+    SIGNAL(SIGTERM, term);             /* Terminate */
+    SIGNAL(SIGCHLD, chld);
+
+    SIGNAL(SIGUSR1, toggle_debug);     /* Toggle debug flag */
+    SIGNAL(SIGUSR2, open_ccp);         /* Reopen CCP */
+
+    /*
+     * Install a handler for other signals which would otherwise
+     * cause pppd to exit without cleaning up.
+     */
+    SIGNAL(SIGABRT, bad_signal);
+    SIGNAL(SIGALRM, bad_signal);
+    SIGNAL(SIGFPE, bad_signal);
+    SIGNAL(SIGILL, bad_signal);
+    SIGNAL(SIGPIPE, bad_signal);
+    SIGNAL(SIGQUIT, bad_signal);
+    SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+    SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+    SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+    SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+    SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+    SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+    SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+    SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+    SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+    SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+    /*
+     * Apparently we can get a SIGPIPE when we call syslog, if
+     * syslogd has died and been restarted.  Ignoring it seems
+     * be sufficient.
+     */
+    signal(SIGPIPE, SIG_IGN);
+}
+
+/*
+ * set_ifunit - do things we need to do once we know which ppp
+ * unit we are using.
+ */
+void
+set_ifunit(iskey)
+    int iskey;
+{
+    info("Using interface %s%d", PPP_DRV_NAME, ifunit);
+    slprintf(ifname, sizeof(ifname), PPP_DRV_NAME "%d", ifunit);
+    script_setenv("IFNAME", ifname, iskey);
+    if (iskey) {
+       create_pidfile();       /* write pid to file */
+       create_linkpidfile();
+    }
+}
+
 /*
  * detach - detach us from the controlling terminal.
  */
 /*
  * detach - detach us from the controlling terminal.
  */
@@ -968,6 +1038,7 @@ void
 detach()
 {
     int pid;
 detach()
 {
     int pid;
+    char numbuf[16];
 
     if (detached)
        return;
 
     if (detached)
        return;
@@ -987,12 +1058,15 @@ detach()
     close(1);
     close(2);
     detached = 1;
     close(1);
     close(2);
     detached = 1;
-    log_to_fd = -1;
+    if (!log_to_file && !log_to_specific_fd)
+       log_to_fd = -1;
     /* update pid files if they have been written already */
     if (pidfilename[0])
        create_pidfile();
     if (linkpidfile[0])
        create_linkpidfile();
     /* update pid files if they have been written already */
     if (pidfilename[0])
        create_pidfile();
     if (linkpidfile[0])
        create_linkpidfile();
+    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+    script_setenv("PPPD_PID", numbuf, 1);
 }
 
 /*
 }
 
 /*
@@ -1016,7 +1090,6 @@ static void
 create_pidfile()
 {
     FILE *pidfile;
 create_pidfile()
 {
     FILE *pidfile;
-    char numbuf[16];
 
     slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
             _PATH_VARRUN, ifname);
 
     slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
             _PATH_VARRUN, ifname);
@@ -1027,10 +1100,6 @@ create_pidfile()
        error("Failed to create pid file %s: %m", pidfilename);
        pidfilename[0] = 0;
     }
        error("Failed to create pid file %s: %m", pidfilename);
        pidfilename[0] = 0;
     }
-    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
-    script_setenv("PPPD_PID", numbuf);
-    if (linkpidfile[0])
-       create_linkpidfile();
 }
 
 static void
 }
 
 static void
@@ -1040,18 +1109,18 @@ create_linkpidfile()
 
     if (linkname[0] == 0)
        return;
 
     if (linkname[0] == 0)
        return;
+    script_setenv("LINKNAME", linkname, 1);
     slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
             _PATH_VARRUN, linkname);
     if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
        fprintf(pidfile, "%d\n", getpid());
     slprintf(linkpidfile, sizeof(linkpidfile), "%sppp-%s.pid",
             _PATH_VARRUN, linkname);
     if ((pidfile = fopen(linkpidfile, "w")) != NULL) {
        fprintf(pidfile, "%d\n", getpid());
-       if (pidfilename[0])
+       if (ifname[0])
            fprintf(pidfile, "%s\n", ifname);
        (void) fclose(pidfile);
     } else {
        error("Failed to create pid file %s: %m", linkpidfile);
        linkpidfile[0] = 0;
     }
            fprintf(pidfile, "%s\n", ifname);
        (void) fclose(pidfile);
     } else {
        error("Failed to create pid file %s: %m", linkpidfile);
        linkpidfile[0] = 0;
     }
-    script_setenv("LINKNAME", linkname);
 }
 
 /*
 }
 
 /*
@@ -1278,6 +1347,9 @@ cleanup()
 
     if (locked)
        unlock();
 
     if (locked)
        unlock();
+
+    if (pppdb != NULL)
+       cleanup_db();
 }
 
 /*
 }
 
 /*
@@ -1326,11 +1398,11 @@ update_link_stats(u)
     link_stats_valid = 1;
 
     slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
     link_stats_valid = 1;
 
     slprintf(numbuf, sizeof(numbuf), "%d", link_connect_time);
-    script_setenv("CONNECT_TIME", numbuf);
+    script_setenv("CONNECT_TIME", numbuf, 0);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_out);
-    script_setenv("BYTES_SENT", numbuf);
+    script_setenv("BYTES_SENT", numbuf, 0);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
-    script_setenv("BYTES_RCVD", numbuf);
+    script_setenv("BYTES_RCVD", numbuf, 0);
 }
 
 
 }
 
 
@@ -1826,9 +1898,9 @@ reap_kids(waitfor)
        return 0;
     while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1
           && pid != 0) {
        return 0;
     while ((pid = waitpid(-1, &status, (waitfor? 0: WNOHANG))) != -1
           && pid != 0) {
-       --n_children;
        for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
            if (chp->pid == pid) {
        for (prevp = &children; (chp = *prevp) != NULL; prevp = &chp->next) {
            if (chp->pid == pid) {
+               --n_children;
                *prevp = chp->next;
                break;
            }
                *prevp = chp->next;
                break;
            }
@@ -1869,24 +1941,32 @@ novm(msg)
  * for scripts that we run (e.g. ip-up, auth-up, etc.)
  */
 void
  * for scripts that we run (e.g. ip-up, auth-up, etc.)
  */
 void
-script_setenv(var, value)
+script_setenv(var, value, iskey)
     char *var, *value;
     char *var, *value;
+    int iskey;
 {
 {
-    size_t vl = strlen(var) + strlen(value) + 2;
+    size_t varl = strlen(var);
+    size_t vl = varl + strlen(value) + 2;
     int i;
     char *p, *newstring;
 
     int i;
     char *p, *newstring;
 
-    newstring = (char *) malloc(vl);
+    newstring = (char *) malloc(vl+1);
     if (newstring == 0)
        return;
     if (newstring == 0)
        return;
+    *newstring++ = iskey;
     slprintf(newstring, vl, "%s=%s", var, value);
 
     /* check if this variable is already set */
     if (script_env != 0) {
        for (i = 0; (p = script_env[i]) != 0; ++i) {
     slprintf(newstring, vl, "%s=%s", var, value);
 
     /* check if this variable is already set */
     if (script_env != 0) {
        for (i = 0; (p = script_env[i]) != 0; ++i) {
-           if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
-               free(p);
+           if (strncmp(p, var, varl) == 0 && p[varl] == '=') {
+               if (p[-1] && pppdb != NULL)
+                   delete_db_key(p);
+               free(p-1);
                script_env[i] = newstring;
                script_env[i] = newstring;
+               if (iskey && pppdb != NULL)
+                   add_db_key(newstring);
+               update_db_entry();
                return;
            }
        }
                return;
            }
        }
@@ -1912,6 +1992,12 @@ script_setenv(var, value)
 
     script_env[i] = newstring;
     script_env[i+1] = 0;
 
     script_env[i] = newstring;
     script_env[i+1] = 0;
+
+    if (pppdb != NULL) {
+       if (iskey)
+           add_db_key(newstring);
+       update_db_entry();
+    }
 }
 
 /*
 }
 
 /*
@@ -1930,12 +2016,150 @@ script_unsetenv(var)
        return;
     for (i = 0; (p = script_env[i]) != 0; ++i) {
        if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
        return;
     for (i = 0; (p = script_env[i]) != 0; ++i) {
        if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
-           free(p);
+           if (p[-1] && pppdb != NULL)
+               delete_db_key(p);
+           free(p-1);
            while ((script_env[i] = script_env[i+1]) != 0)
                ++i;
            break;
        }
     }
            while ((script_env[i] = script_env[i+1]) != 0)
                ++i;
            break;
        }
     }
+    if (pppdb != NULL)
+       update_db_entry();
+}
+
+/*
+ * update_db_entry - update our entry in the database.
+ */
+static void
+update_db_entry()
+{
+    TDB_DATA key, dbuf;
+    int vlen, i;
+    char *p, *q, *vbuf;
+
+    if (script_env == NULL)
+       return;
+    vlen = 0;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+       vlen += strlen(p) + 1;
+    vbuf = malloc(vlen);
+    if (vbuf == 0)
+       novm("database entry");
+    q = vbuf;
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+       q += slprintf(q, vbuf + vlen - q, "%s;", p);
+
+    key.dptr = db_key;
+    key.dsize = strlen(db_key);
+    dbuf.dptr = vbuf;
+    dbuf.dsize = vlen;
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+       error("tdb_store failed: %s", tdb_error(pppdb));
+
+}
+
+/*
+ * add_db_key - add a key that we can use to look up our database entry.
+ */
+static void
+add_db_key(str)
+    const char *str;
+{
+    TDB_DATA key, dbuf;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    dbuf.dptr = db_key;
+    dbuf.dsize = strlen(db_key);
+    if (tdb_store(pppdb, key, dbuf, TDB_REPLACE))
+       error("tdb_store key failed: %s", tdb_error(pppdb));
+}
+
+/*
+ * delete_db_key - delete a key for looking up our database entry.
+ */
+static void
+delete_db_key(str)
+    const char *str;
+{
+    TDB_DATA key;
+
+    key.dptr = (char *) str;
+    key.dsize = strlen(str);
+    tdb_delete(pppdb, key);
+}
+
+/*
+ * cleanup_db - delete all the entries we put in the database.
+ */
+static void
+cleanup_db()
+{
+    TDB_DATA key;
+    int i;
+    char *p;
+
+    key.dptr = db_key;
+    key.dsize = strlen(db_key);
+    tdb_delete(pppdb, key);
+    for (i = 0; (p = script_env[i]) != 0; ++i)
+       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;
 }
 
 /*
 }
 
 /*
@@ -1998,6 +2222,8 @@ charshunt(ifd, ofd, record_file)
     int pty_readable, stdin_readable;
     struct timeval lasttime;
     FILE *recordf = NULL;
     int pty_readable, stdin_readable;
     struct timeval lasttime;
     FILE *recordf = NULL;
+    int ilevel, olevel, max_level;
+    struct timeval levelt, tout, *top;
 
     /*
      * Reset signal handlers.
 
     /*
      * Reset signal handlers.
@@ -2071,6 +2297,16 @@ charshunt(ifd, ofd, record_file)
     nibuf = nobuf = 0;
     ibufp = obufp = NULL;
     pty_readable = stdin_readable = 1;
     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);
     nfds = (ofd > pty_master? ofd: pty_master) + 1;
     if (recordf != NULL) {
        gettimeofday(&lasttime, NULL);
@@ -2083,21 +2319,44 @@ charshunt(ifd, ofd, record_file)
     }
 
     while (nibuf != 0 || nobuf != 0 || pty_readable || stdin_readable) {
     }
 
     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);
        FD_ZERO(&ready);
        FD_ZERO(&writey);
-       if (nibuf != 0)
-           FD_SET(pty_master, &writey);
-       else if (stdin_readable)
+       if (nibuf != 0) {
+           if (ilevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(pty_master, &writey);
+       } else if (stdin_readable)
            FD_SET(ifd, &ready);
            FD_SET(ifd, &ready);
-       if (nobuf != 0)
-           FD_SET(ofd, &writey);
-       else if (pty_readable)
+       if (nobuf != 0) {
+           if (olevel >= max_level)
+               top = &tout;
+           else
+               FD_SET(ofd, &writey);
+       } else if (pty_readable)
            FD_SET(pty_master, &ready);
            FD_SET(pty_master, &ready);
-       if (select(nfds, &ready, &writey, NULL, NULL) < 0) {
+       if (select(nfds, &ready, &writey, NULL, top) < 0) {
            if (errno != EINTR)
                fatal("select");
            continue;
        }
            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 (FD_ISSET(ifd, &ready)) {
            ibufp = inpacket_buf;
            nibuf = read(ifd, ibufp, sizeof(inpacket_buf));
@@ -2153,31 +2412,41 @@ charshunt(ifd, ofd, record_file)
            }
        }
        if (FD_ISSET(ofd, &writey)) {
            }
        }
        if (FD_ISSET(ofd, &writey)) {
-           n = write(ofd, obufp, nobuf);
+           n = nobuf;
+           if (olevel + n > max_level)
+               n = max_level - olevel;
+           n = write(ofd, obufp, n);
            if (n < 0) {
            if (n < 0) {
-               if (errno != EIO) {
+               if (errno == EIO) {
+                   pty_readable = 0;
+                   nobuf = 0;
+               } else if (errno != EAGAIN && errno != EINTR) {
                    error("Error writing standard output: %m");
                    break;
                }
                    error("Error writing standard output: %m");
                    break;
                }
-               pty_readable = 0;
-               nobuf = 0;
            } else {
                obufp += n;
                nobuf -= n;
            } else {
                obufp += n;
                nobuf -= n;
+               olevel += n;
            }
        }
        if (FD_ISSET(pty_master, &writey)) {
            }
        }
        if (FD_ISSET(pty_master, &writey)) {
-           n = write(pty_master, ibufp, nibuf);
+           n = nibuf;
+           if (ilevel + n > max_level)
+               n = max_level - ilevel;
+           n = write(pty_master, ibufp, n);
            if (n < 0) {
            if (n < 0) {
-               if (errno != EIO) {
+               if (errno == EIO) {
+                   stdin_readable = 0;
+                   nibuf = 0;
+               } else if (errno != EAGAIN && errno != EINTR) {
                    error("Error writing pseudo-tty master: %m");
                    break;
                }
                    error("Error writing pseudo-tty master: %m");
                    break;
                }
-               stdin_readable = 0;
-               nibuf = 0;
            } else {
                ibufp += n;
                nibuf -= n;
            } else {
                ibufp += n;
                nibuf -= n;
+               ilevel += n;
            }
        }
     }
            }
        }
     }