don't fall over in the charshunt if we get an EAGAIN or EINTR
[ppp.git] / pppd / main.c
index 982ab55c4e4198ab401793a79f47ff72337e151e..0722e9104076417b68500f16058a98f31f1e8f2d 100644 (file)
@@ -17,7 +17,7 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
 
-#define RCSID  "$Id: main.c,v 1.91 2000/03/27 06:03:01 paulus Exp $"
+#define RCSID  "$Id: main.c,v 1.98 2000/04/29 12:32:59 paulus Exp $"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -55,6 +55,7 @@
 #include "ccp.h"
 #include "pathnames.h"
 #include "patchlevel.h"
+#include "tdb.h"
 
 #ifdef CBCP_SUPPORT
 #include "cbcp.h"
@@ -82,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 */
-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 */
@@ -96,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 */
+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;
@@ -154,6 +157,7 @@ static struct subprocess *children;
 
 /* 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));
@@ -172,6 +176,10 @@ 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 *));
@@ -218,13 +226,20 @@ struct protent *protocols[] = {
     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;
-    struct sigaction sa;
     char *p, *connector;
     struct passwd *pw;
     struct timeval timo;
@@ -264,7 +279,7 @@ main(argc, argv)
     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);
 
@@ -344,6 +359,9 @@ main(argc, argv)
     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)();
@@ -400,8 +418,6 @@ main(argc, argv)
        && S_ISCHR(statbuf.st_mode) && statbuf.st_rdev == devstat.st_rdev)
        log_to_fd = -1;
 
-    script_setenv("DEVICE", devnam);
-
     /*
      * Initialize system-dependent stuff.
      */
@@ -409,6 +425,18 @@ main(argc, argv)
     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.
@@ -425,81 +453,14 @@ main(argc, argv)
     }
     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);
-
-#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)
+    script_setenv("PPPLOGNAME", p, 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;
 
@@ -512,13 +473,10 @@ main(argc, argv)
        /*
         * Open the loopback channel and set it up to be the ppp interface.
         */
+       tdb_writelock(pppdb);
        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.
@@ -766,7 +724,7 @@ main(argc, argv)
        }
 
        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]) {
@@ -775,20 +733,17 @@ main(argc, argv)
        }
 
        /* set up the serial device as a ppp interface */
+       tdb_writelock(pppdb);
        fd_ppp = establish_ppp(ttyfd);
        if (fd_ppp < 0) {
+           tdb_writeunlock(pppdb);
            status = EXIT_FATAL_ERROR;
            goto disconnect;
        }
 
-       if (!demand && ifunit >= 0) {
-           
-           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
@@ -883,6 +838,8 @@ main(argc, argv)
        fd_ppp = -1;
        if (!hungup)
            lcp_lowerdown(0);
+       if (!demand)
+           script_unsetenv("IFNAME");
 
        /*
         * Run disconnector script, if requested.
@@ -973,6 +930,107 @@ main(argc, argv)
     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.
  */
@@ -980,6 +1038,7 @@ void
 detach()
 {
     int pid;
+    char numbuf[16];
 
     if (detached)
        return;
@@ -999,12 +1058,15 @@ detach()
     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();
+    slprintf(numbuf, sizeof(numbuf), "%d", getpid());
+    script_setenv("PPPD_PID", numbuf, 1);
 }
 
 /*
@@ -1028,7 +1090,6 @@ static void
 create_pidfile()
 {
     FILE *pidfile;
-    char numbuf[16];
 
     slprintf(pidfilename, sizeof(pidfilename), "%s%s.pid",
             _PATH_VARRUN, ifname);
@@ -1039,10 +1100,6 @@ create_pidfile()
        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
@@ -1052,18 +1109,18 @@ create_linkpidfile()
 
     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());
-       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;
     }
-    script_setenv("LINKNAME", linkname);
 }
 
 /*
@@ -1290,6 +1347,9 @@ cleanup()
 
     if (locked)
        unlock();
+
+    if (pppdb != NULL)
+       cleanup_db();
 }
 
 /*
@@ -1338,11 +1398,11 @@ update_link_stats(u)
     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);
-    script_setenv("BYTES_SENT", numbuf);
+    script_setenv("BYTES_SENT", numbuf, 0);
     slprintf(numbuf, sizeof(numbuf), "%d", link_stats.bytes_in);
-    script_setenv("BYTES_RCVD", numbuf);
+    script_setenv("BYTES_RCVD", numbuf, 0);
 }
 
 
@@ -1881,24 +1941,32 @@ novm(msg)
  * 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;
+    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;
 
-    newstring = (char *) malloc(vl);
+    newstring = (char *) malloc(vl+1);
     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) {
-           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;
+               if (iskey && pppdb != NULL)
+                   add_db_key(newstring);
+               update_db_entry();
                return;
            }
        }
@@ -1924,6 +1992,12 @@ script_setenv(var, value)
 
     script_env[i] = newstring;
     script_env[i+1] = 0;
+
+    if (pppdb != NULL) {
+       if (iskey)
+           add_db_key(newstring);
+       update_db_entry();
+    }
 }
 
 /*
@@ -1942,12 +2016,96 @@ script_unsetenv(var)
        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;
        }
     }
+    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);
 }
 
 /*
@@ -1995,7 +2153,7 @@ open_socket(dest)
     sad.sin_family = AF_INET;
     sad.sin_port = htons(port);
     sad.sin_addr.s_addr = host;
-    if (connect(sock, &sad, sizeof(sad)) < 0) {
+    if (connect(sock, (struct sockaddr *)&sad, sizeof(sad)) < 0) {
        error("Can't connect to %s: %m", dest);
        close(sock);
        return -1;
@@ -2259,12 +2417,13 @@ charshunt(ifd, ofd, record_file)
                n = max_level - olevel;
            n = write(ofd, obufp, n);
            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;
                }
-               pty_readable = 0;
-               nobuf = 0;
            } else {
                obufp += n;
                nobuf -= n;
@@ -2277,12 +2436,13 @@ charshunt(ifd, ofd, record_file)
                n = max_level - ilevel;
            n = write(pty_master, ibufp, n);
            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;
                }
-               stdin_readable = 0;
-               nibuf = 0;
            } else {
                ibufp += n;
                nibuf -= n;