]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/options.c
Cleanup pppoe-discovery fatal functions
[ppp.git] / pppd / options.c
index 223d1fa63a835bfc53b3ebbdfefc8a3faa189a41..f8d6c001e195d5b64d671b535f58e2f9be7d3cb5 100644 (file)
@@ -1,25 +1,47 @@
 /*
  * options.c - handles option processing for PPP.
  *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any legal
+ *    details, please contact
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define RCSID  "$Id: options.c,v 1.84 2002/07/13 06:24:36 kad Exp $"
-
 #include <ctype.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
 #ifdef PLUGIN
 #include <dlfcn.h>
 #endif
+
 #ifdef PPP_FILTER
 #include <pcap.h>
-#include <pcap-int.h>  /* XXX: To get struct pcap */
+/*
+ * There have been 3 or 4 different names for this in libpcap CVS, but
+ * this seems to be what they have settled on...
+ * For older versions of libpcap, use DLT_PPP - but that means
+ * we lose the inbound and outbound qualifiers.
+ */
+#ifndef DLT_PPP_PPPD
+#ifdef DLT_PPP_WITHDIRECTION
+#define DLT_PPP_PPPD   DLT_PPP_WITHDIRECTION
+#else
+#define DLT_PPP_PPPD   DLT_PPP
 #endif
+#endif
+#endif /* PPP_FILTER */
 
 #include "pppd.h"
 #include "pathnames.h"
 
 #if defined(ultrix) || defined(NeXT)
-char *strdup __P((char *));
+char *strdup(char *);
 #endif
 
-static const char rcsid[] = RCSID;
 
 struct option_value {
     struct option_value *next;
@@ -54,15 +88,16 @@ struct option_value {
 /*
  * Option variables and default values.
  */
-#ifdef PPP_FILTER
-int    dflag = 0;              /* Tell libpcap we want debugging */
-#endif
 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 */
 bool   nodetach = 0;           /* Don't detach from controlling tty */
 bool   updetach = 0;           /* Detach once link is up */
+bool   master_detach;          /* Detach when we're (only) multilink master */
+#ifdef SYSTEMD
+bool   up_sdnotify = 0;        /* Notify systemd once link is up */
+#endif
 int    maxconnect = 0;         /* Maximum connect time */
 char   user[MAXNAMELEN];       /* Username for PAP */
 char   passwd[MAXSECRETLEN];   /* Password for PAP */
@@ -80,11 +115,17 @@ 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    req_unit = -1;          /* requested interface unit */
+char   path_ipup[MAXPATHLEN];  /* pathname of ip-up script */
+char   path_ipdown[MAXPATHLEN];/* pathname of ip-down script */
+char   req_ifname[MAXIFNAMELEN];       /* requested interface name */
 bool   multilink = 0;          /* Enable multilink operation */
 char   *bundle_name = NULL;    /* bundle name for multilink */
 bool   dump_options;           /* print out option values */
 bool   dryrun;                 /* print out option values and exit */
 char   *domain;                /* domain name set by domain option */
+int    child_wait = 5;         /* # seconds to wait for children at exit */
+struct userenv *userenv_list;  /* user environment variables */
+int    dfl_route_metric = -1;  /* metric of the default route to set over the PPP link */
 
 #ifdef MAXOCTETS
 unsigned int  maxoctets = 0;    /* default - no limit */
@@ -99,9 +140,9 @@ extern struct stat devstat;
 #ifdef PPP_FILTER
 struct bpf_program pass_filter;/* Filter program for packets to pass */
 struct bpf_program active_filter; /* Filter program for link-active pkts */
-pcap_t  pc;                    /* Fake struct pcap so we can compile expr */
 #endif
 
+static option_t *curopt;       /* pointer to option being processed */
 char *current_option;          /* the name of the option being parsed */
 int  privileged_option;                /* set iff the current option came from root */
 char *option_source;           /* string saying where the option came from */
@@ -114,30 +155,35 @@ static char logfile_name[MAXPATHLEN];     /* name of log file */
 /*
  * Prototypes
  */
-static int setdomain __P((char **));
-static int readfile __P((char **));
-static int callfile __P((char **));
-static int showversion __P((char **));
-static int showhelp __P((char **));
-static void usage __P((void));
-static int setlogfile __P((char **));
+static int setdomain(char **);
+static int readfile(char **);
+static int callfile(char **);
+static int showversion(char **);
+static int showhelp(char **);
+static void usage(void);
+static int setlogfile(char **);
 #ifdef PLUGIN
-static int loadplugin __P((char **));
+static int loadplugin(char **);
 #endif
 
 #ifdef PPP_FILTER
-static int setpassfilter __P((char **));
-static int setactivefilter __P((char **));
+static int setpassfilter(char **);
+static int setactivefilter(char **);
 #endif
 
 #ifdef MAXOCTETS
-static int setmodir __P((char **));
+static int setmodir(char **);
 #endif
 
-static option_t *find_option __P((const char *name));
-static int process_option __P((option_t *, char *, char **));
-static int n_arguments __P((option_t *));
-static int number_option __P((char *, u_int32_t *, int));
+static int user_setenv(char **);
+static void user_setprint(option_t *, printer_func, void *);
+static int user_unsetenv(char **);
+static void user_unsetprint(option_t *, printer_func, void *);
+
+static option_t *find_option(char *name);
+static int process_option(option_t *, char *, char **);
+static int n_arguments(option_t *);
+static int number_option(char *, u_int32_t *, int);
 
 /*
  * Structure to store extra lists of options.
@@ -166,12 +212,21 @@ option_t general_options[] = {
       "Don't detach from controlling tty", OPT_PRIO | 1 },
     { "-detach", o_bool, &nodetach,
       "Don't detach from controlling tty", OPT_ALIAS | OPT_PRIOSUB | 1 },
+#ifdef SYSTEMD
+    { "up_sdnotify", o_bool, &up_sdnotify,
+      "Notify systemd once link is up (implies nodetach)",
+      OPT_PRIOSUB | OPT_A2COPY | 1, &nodetach },
+#endif
     { "updetach", o_bool, &updetach,
       "Detach from controlling tty once link is up",
       OPT_PRIOSUB | OPT_A2CLR | 1, &nodetach },
 
+    { "master_detach", o_bool, &master_detach,
+      "Detach when we're multilink master but have no link", 1 },
+
     { "holdoff", o_int, &holdoff,
-      "Set time in seconds before retrying connection", OPT_PRIO },
+      "Set time in seconds before retrying connection",
+      OPT_PRIO, &holdoff_specified },
 
     { "idle", o_int, &idle_time_limit,
       "Set time in seconds before disconnecting idle link", OPT_PRIO },
@@ -238,11 +293,37 @@ option_t general_options[] = {
       "PPP interface unit number to use if possible",
       OPT_PRIO | OPT_LLIMIT, 0, 0 },
 
+    { "ifname", o_string, req_ifname,
+      "Set PPP interface name",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXIFNAMELEN },
+
     { "dump", o_bool, &dump_options,
       "Print out option values after parsing all options", 1 },
     { "dryrun", o_bool, &dryrun,
       "Stop after parsing, printing, and checking options", 1 },
 
+    { "child-timeout", o_int, &child_wait,
+      "Number of seconds to wait for child processes at exit",
+      OPT_PRIO },
+
+    { "set", o_special, (void *)user_setenv,
+      "Set user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_setprint },
+    { "unset", o_special, (void *)user_unsetenv,
+      "Unset user environment variable",
+      OPT_A2PRINTER | OPT_NOPRINT, (void *)user_unsetprint },
+
+    { "defaultroute-metric", o_int, &dfl_route_metric,
+      "Metric to use for the default route (Linux only; -1 for default behavior)",
+      OPT_PRIV|OPT_LLIMIT|OPT_INITONLY, NULL, 0, -1 },
+
+    { "ip-up-script", o_string, path_ipup,
+      "Set pathname of ip-up script",
+      OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
+    { "ip-down-script", o_string, path_ipdown,
+      "Set pathname of ip-down script",
+      OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
+
 #ifdef HAVE_MULTILINK
     { "multilink", o_bool, &multilink,
       "Enable multilink operation", OPT_PRIO | 1 },
@@ -263,13 +344,10 @@ option_t general_options[] = {
 #endif
 
 #ifdef PPP_FILTER
-    { "pdebug", o_int, &dflag,
-      "libpcap debugging", OPT_PRIO },
-
-    { "pass-filter", 1, setpassfilter,
+    { "pass-filter", o_special, setpassfilter,
       "set filter for packets to pass", OPT_PRIO },
 
-    { "active-filter", 1, setactivefilter,
+    { "active-filter", o_special, setactivefilter,
       "set filter for active pkts", OPT_PRIO },
 #endif
 
@@ -315,9 +393,7 @@ See pppd(8) for more options.\n\
  * parse_args - parse a string of arguments from the command line.
  */
 int
-parse_args(argc, argv)
-    int argc;
-    char **argv;
+parse_args(int argc, char **argv)
 {
     char *arg;
     option_t *opt;
@@ -353,27 +429,27 @@ parse_args(argc, argv)
  * and interpret them.
  */
 int
-options_from_file(filename, must_exist, check_prot, priv)
-    char *filename;
-    int must_exist;
-    int check_prot;
-    int priv;
+options_from_file(char *filename, int must_exist, int check_prot, int priv)
 {
     FILE *f;
     int i, newline, ret, err;
     option_t *opt;
     int oldpriv, n;
     char *oldsource;
+    uid_t euid;
     char *argv[MAXARGS];
     char args[MAXARGS][MAXWORDLEN];
     char cmd[MAXWORDLEN];
 
-    if (check_prot)
-       seteuid(getuid());
+    euid = geteuid();
+    if (check_prot && seteuid(getuid()) == -1) {
+       option_error("unable to drop privileges to open %s: %m", filename);
+       return 0;
+    }
     f = fopen(filename, "r");
     err = errno;
-    if (check_prot)
-       seteuid(0);
+    if (check_prot && seteuid(euid) == -1)
+       fatal("unable to regain privileges");
     if (f == NULL) {
        errno = err;
        if (!must_exist) {
@@ -426,7 +502,7 @@ err:
  * and if so, interpret options from it.
  */
 int
-options_from_user()
+options_from_user(void)
 {
     char *user, *path, *file;
     int ret;
@@ -457,15 +533,15 @@ options_from_user()
  * files a lower priority than the command line.
  */
 int
-options_for_tty()
+options_for_tty(void)
 {
     char *dev, *path, *p;
     int ret;
     size_t pl;
 
     dev = devnam;
-    if (strncmp(dev, "/dev/", 5) == 0)
-       dev += 5;
+    if ((p = strstr(dev, "/dev/")) != NULL)
+       dev = p + 5;
     if (dev[0] == 0 || strcmp(dev, "tty") == 0)
        return 1;               /* don't look for /etc/ppp/options.tty */
     pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
@@ -487,9 +563,7 @@ options_for_tty()
  * options_from_list - process a string of options in a wordlist.
  */
 int
-options_from_list(w, priv)
-    struct wordlist *w;
-    int priv;
+options_from_list(struct wordlist *w, int priv)
 {
     char *argv[MAXARGS];
     option_t *opt;
@@ -533,18 +607,15 @@ err:
  * match_option - see if this option matches an option_t structure.
  */
 static int
-match_option(name, opt, dowild)
-    char *name;
-    option_t *opt;
-    int dowild;
+match_option(char *name, option_t *opt, int dowild)
 {
-       int (*match) __P((char *, char **, int));
+       int (*match)(char *, char **, int);
 
        if (dowild != (opt->type == o_wild))
                return 0;
        if (!dowild)
                return strcmp(name, opt->name) == 0;
-       match = (int (*) __P((char *, char **, int))) opt->addr;
+       match = (int (*)(char *, char **, int)) opt->addr;
        return (*match)(name, NULL, 0);
 }
 
@@ -554,8 +625,7 @@ match_option(name, opt, dowild)
  * This could be optimized by using a hash table.
  */
 static option_t *
-find_option(name)
-    const char *name;
+find_option(char *name)
 {
        option_t *opt;
        struct option_list *list;
@@ -588,20 +658,18 @@ find_option(name)
  * process_option - process one new-style option.
  */
 static int
-process_option(opt, cmd, argv)
-    option_t *opt;
-    char *cmd;
-    char **argv;
+process_option(option_t *opt, char *cmd, char **argv)
 {
     u_int32_t v;
     int iv, a;
     char *sv;
-    int (*parser) __P((char **));
-    int (*wildp) __P((char *, char **, int));
+    int (*parser)(char **);
+    int (*wildp)(char *, char **, int);
     char *optopt = (opt->type == o_wild)? "": " option";
     int prio = option_priority;
     option_t *mainopt = opt;
 
+    current_option = opt->name;
     if ((opt->flags & OPT_PRIVFIX) && privileged_option)
        prio += OPRIO_ROOT;
     while (mainopt->flags & OPT_PRIOSUB)
@@ -653,8 +721,6 @@ process_option(opt, cmd, argv)
            *(u_char *)(opt->addr2) &= ~v;
        else if (opt->addr2 && (opt->flags & OPT_A2OR))
            *(u_char *)(opt->addr2) |= v;
-       if (opt->addr3 && (opt->flags & OPT_A3OR))
-           *(u_char *)(opt->addr3) |= v;
        break;
 
     case o_int:
@@ -677,7 +743,7 @@ process_option(opt, cmd, argv)
                    break;
                case OPT_LIMITS:
                    option_error("%s value must be%s between %d and %d",
-                               opt->name, opt->lower_limit, opt->upper_limit);
+                               opt->name, zok, opt->lower_limit, opt->upper_limit);
                    break;
                }
                return 0;
@@ -720,41 +786,57 @@ process_option(opt, cmd, argv)
        if (opt->flags & OPT_STATIC) {
            strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
        } else {
+           char **optptr = (char **)(opt->addr);
            sv = strdup(*argv);
            if (sv == NULL)
                novm("option argument");
-           *(char **)(opt->addr) = sv;
+           if (*optptr)
+               free(*optptr);
+           *optptr = sv;
+       }
+       /* obfuscate original argument for things like password */
+       if (opt->flags & OPT_HIDE) {
+           memset(*argv, '?', strlen(*argv));
+           *argv = "********";
        }
        break;
 
     case o_special_noarg:
     case o_special:
-       parser = (int (*) __P((char **))) opt->addr;
+       parser = (int (*)(char **)) opt->addr;
+       curopt = opt;
        if (!(*parser)(argv))
            return 0;
        if (opt->flags & OPT_A2LIST) {
-           struct option_value *ovp, **pp;
+           struct option_value *ovp, *pp;
 
            ovp = malloc(sizeof(*ovp) + strlen(*argv));
            if (ovp != 0) {
                strcpy(ovp->value, *argv);
                ovp->source = option_source;
                ovp->next = NULL;
-               pp = (struct option_value **) &opt->addr2;
-               while (*pp != 0)
-                   pp = &(*pp)->next;
-               *pp = ovp;
+               if (opt->addr2 == NULL) {
+                   opt->addr2 = ovp;
+               } else {
+                   for (pp = opt->addr2; pp->next != NULL; pp = pp->next)
+                       ;
+                   pp->next = ovp;
+               }
            }
        }
        break;
 
     case o_wild:
-       wildp = (int (*) __P((char *, char **, int))) opt->addr;
+       wildp = (int (*)(char *, char **, int)) opt->addr;
        if (!(*wildp)(cmd, argv, 1))
            return 0;
        break;
     }
 
+    /*
+     * If addr2 wasn't used by any flag (OPT_A2COPY, etc.) but is set,
+     * treat it as a bool and set/clear it based on the OPT_A2CLR bit.
+     */
     if (opt->addr2 && (opt->flags & (OPT_A2COPY|OPT_ENABLE
                |OPT_A2PRINTER|OPT_A2STRVAL|OPT_A2LIST|OPT_A2OR)) == 0)
        *(bool *)(opt->addr2) = !(opt->flags & OPT_A2CLR);
@@ -772,10 +854,7 @@ process_option(opt, cmd, argv)
  * and source of the option value.  Otherwise returns 0.
  */
 int
-override_value(option, priority, source)
-    const char *option;
-    int priority;
-    const char *source;
+override_value(char *option, int priority, const char *source)
 {
        option_t *opt;
 
@@ -796,8 +875,7 @@ override_value(option, priority, source)
  * n_arguments - tell how many arguments an option takes
  */
 static int
-n_arguments(opt)
-    option_t *opt;
+n_arguments(option_t *opt)
 {
        return (opt->type == o_bool || opt->type == o_special_noarg
                || (opt->flags & OPT_NOARG))? 0: 1;
@@ -807,8 +885,7 @@ n_arguments(opt)
  * add_options - add a list of options to the set we grok.
  */
 void
-add_options(opt)
-    option_t *opt;
+add_options(option_t *opt)
 {
     struct option_list *list;
 
@@ -824,7 +901,7 @@ add_options(opt)
  * check_options - check that options are valid and consistent.
  */
 void
-check_options()
+check_options(void)
 {
        if (logfile_fd >= 0 && logfile_fd != log_to_fd)
                close(logfile_fd);
@@ -834,10 +911,7 @@ check_options()
  * print_option - print out an option and its value
  */
 static void
-print_option(opt, mainopt, printer, arg)
-    option_t *opt, *mainopt;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+print_option(option_t *opt, option_t *mainopt, printer_func printer, void *arg)
 {
        int i, v;
        char *p;
@@ -899,16 +973,15 @@ print_option(opt, mainopt, printer, arg)
                        printer(arg, " ");
                }
                if (opt->flags & OPT_A2PRINTER) {
-                       void (*oprt) __P((option_t *,
-                                         void ((*)__P((void *, char *, ...))),
-                                         void *));
-                       oprt = opt->addr2;
+                       void (*oprt)(option_t *, printer_func, void *);
+                       oprt = (void (*)(option_t *, printer_func, void *))
+                               opt->addr2;
                        (*oprt)(opt, printer, arg);
                } else if (opt->flags & OPT_A2STRVAL) {
                        p = (char *) opt->addr2;
                        if ((opt->flags & OPT_STATIC) == 0)
                                p = *(char **)p;
-                       printer("%q", p);
+                       printer(arg, "%q", p);
                } else if (opt->flags & OPT_A2LIST) {
                        struct option_value *ovp;
 
@@ -937,10 +1010,7 @@ print_option(opt, mainopt, printer, arg)
  * array of options.
  */
 static void
-print_option_list(opt, printer, arg)
-    option_t *opt;
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+print_option_list(option_t *opt, printer_func printer, void *arg)
 {
        while (opt->name != NULL) {
                if (opt->priority != OPRIO_DEFAULT
@@ -956,9 +1026,7 @@ print_option_list(opt, printer, arg)
  * print_options - print out what options are in effect.
  */
 void
-print_options(printer, arg)
-    void (*printer) __P((void *, char *, ...));
-    void *arg;
+print_options(printer_func printer, void *arg)
 {
        struct option_list *list;
        int i;
@@ -977,7 +1045,7 @@ print_options(printer, arg)
  * usage - print out a message telling how to use the program.
  */
 static void
-usage()
+usage(void)
 {
     if (phase == PHASE_INITIALIZE)
        fprintf(stderr, usage_string, VERSION, progname);
@@ -987,8 +1055,7 @@ usage()
  * showhelp - print out usage message and exit.
  */
 static int
-showhelp(argv)
-    char **argv;
+showhelp(char **argv)
 {
     if (phase == PHASE_INITIALIZE) {
        usage();
@@ -1001,11 +1068,10 @@ showhelp(argv)
  * showversion - print out the version number and exit.
  */
 static int
-showversion(argv)
-    char **argv;
+showversion(char **argv)
 {
     if (phase == PHASE_INITIALIZE) {
-       fprintf(stderr, "pppd version %s\n", VERSION);
+       fprintf(stdout, "pppd version %s\n", VERSION);
        exit(0);
     }
     return 0;
@@ -1017,18 +1083,12 @@ showversion(argv)
  * stderr if phase == PHASE_INITIALIZE.
  */
 void
-option_error __V((char *fmt, ...))
+option_error(char *fmt, ...)
 {
     va_list args;
     char buf[1024];
 
-#if defined(__STDC__)
     va_start(args, fmt);
-#else
-    char *fmt;
-    va_start(args);
-    fmt = va_arg(args, char *);
-#endif
     vslprintf(buf, sizeof(buf), fmt, args);
     va_end(args);
     if (phase == PHASE_INITIALIZE)
@@ -1041,8 +1101,7 @@ option_error __V((char *fmt, ...))
  * readable - check if a file is readable by the real user.
  */
 int
-readable(fd)
-    int fd;
+readable(int fd)
 {
     uid_t uid;
     int i;
@@ -1071,11 +1130,7 @@ readable(fd)
  * \<newline> is ignored.
  */
 int
-getword(f, word, newlinep, filename)
-    FILE *f;
-    char *word;
-    int *newlinep;
-    char *filename;
+getword(FILE *f, char *word, int *newlinep, char *filename)
 {
     int c, len, escape;
     int quoted, comment;
@@ -1087,6 +1142,7 @@ getword(f, word, newlinep, filename)
     len = 0;
     escape = 0;
     comment = 0;
+    quoted = 0;
 
     /*
      * First skip white-space and comments.
@@ -1144,15 +1200,6 @@ getword(f, word, newlinep, filename)
            break;
     }
 
-    /*
-     * Save the delimiter for quoted strings.
-     */
-    if (!escape && (c == '"' || c == '\'')) {
-        quoted = c;
-       c = getc(f);
-    } else
-        quoted = 0;
-
     /*
      * Process characters until the end of the word.
      */
@@ -1234,47 +1281,55 @@ getword(f, word, newlinep, filename)
            /*
             * Store the resulting character for the escape sequence.
             */
-           if (len < MAXWORDLEN-1)
+           if (len < MAXWORDLEN) {
                word[len] = value;
-           ++len;
+               ++len;
+           }
 
            if (!got)
                c = getc(f);
            continue;
-
        }
 
        /*
-        * Not escaped: see if we've reached the end of the word.
+        * Backslash starts a new escape sequence.
         */
-       if (quoted) {
-           if (c == quoted)
-               break;
-       } else {
-           if (isspace(c) || c == '#') {
-               ungetc (c, f);
-               break;
-           }
+       if (c == '\\') {
+           escape = 1;
+           c = getc(f);
+           continue;
        }
 
        /*
-        * Backslash starts an escape sequence.
+        * Not escaped: check for the start or end of a quoted
+        * section and see if we've reached the end of the word.
         */
-       if (c == '\\') {
-           escape = 1;
+       if (quoted) {
+           if (c == quoted) {
+               quoted = 0;
+               c = getc(f);
+               continue;
+           }
+       } else if (c == '"' || c == '\'') {
+           quoted = c;
            c = getc(f);
            continue;
+       } else if (isspace(c) || c == '#') {
+           ungetc (c, f);
+           break;
        }
 
        /*
         * An ordinary character: store it in the word and get another.
         */
-       if (len < MAXWORDLEN-1)
+       if (len < MAXWORDLEN) {
            word[len] = c;
-       ++len;
+           ++len;
+       }
 
        c = getc(f);
     }
+    word[MAXWORDLEN-1] = 0;    /* make sure word is null-terminated */
 
     /*
      * End of the word: check for errors.
@@ -1292,6 +1347,9 @@ getword(f, word, newlinep, filename)
         */
        if (len == 0)
            return 0;
+       if (quoted)
+           option_error("warning: quoted word runs to end of file (%.20s...)",
+                        filename, word);
     }
 
     /*
@@ -1314,10 +1372,7 @@ getword(f, word, newlinep, filename)
  * number_option - parse an unsigned numeric parameter for an option.
  */
 static int
-number_option(str, valp, base)
-    char *str;
-    u_int32_t *valp;
-    int base;
+number_option(char *str, u_int32_t *valp, int base)
 {
     char *ptr;
 
@@ -1337,9 +1392,7 @@ number_option(str, valp, base)
  * if there is an error.
  */
 int
-int_option(str, valp)
-    char *str;
-    int *valp;
+int_option(char *str, int *valp)
 {
     u_int32_t v;
 
@@ -1358,8 +1411,7 @@ int_option(str, valp)
  * readfile - take commands from a file.
  */
 static int
-readfile(argv)
-    char **argv;
+readfile(char **argv)
 {
     return options_from_file(*argv, 1, 1, privileged_option);
 }
@@ -1369,8 +1421,7 @@ readfile(argv)
  * Name may not contain /../, start with / or ../, or end in /..
  */
 static int
-callfile(argv)
-    char **argv;
+callfile(char **argv)
 {
     char *fname, *arg, *p;
     int l, ok;
@@ -1400,6 +1451,7 @@ callfile(argv)
     if ((fname = (char *) malloc(l)) == NULL)
        novm("call file name");
     slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
+    script_setenv("CALL_FILE", arg, 0);
 
     ok = options_from_file(fname, 1, 1, 1);
 
@@ -1412,32 +1464,40 @@ callfile(argv)
  * setpassfilter - Set the pass filter for packets
  */
 static int
-setpassfilter(argv)
-    char **argv;
+setpassfilter(char **argv)
 {
-    pc.linktype = DLT_PPP;
-    pc.snapshot = PPP_HDRLEN;
-    if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
-       return 1;
-    option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
-    return 0;
+    pcap_t *pc;
+    int ret = 1;
+
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
+    if (pcap_compile(pc, &pass_filter, *argv, 1, netmask) == -1) {
+       option_error("error in pass-filter expression: %s\n",
+                    pcap_geterr(pc));
+       ret = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
 }
 
 /*
  * setactivefilter - Set the active filter for packets
  */
 static int
-setactivefilter(argv)
-    char **argv;
+setactivefilter(char **argv)
 {
-    pc.linktype = DLT_PPP;
-    pc.snapshot = PPP_HDRLEN;
-    if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
-       return 1;
-    option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
-    return 0;
+    pcap_t *pc;
+    int ret = 1;
+
+    pc = pcap_open_dead(DLT_PPP_PPPD, 65535);
+    if (pcap_compile(pc, &active_filter, *argv, 1, netmask) == -1) {
+       option_error("error in active-filter expression: %s\n",
+                    pcap_geterr(pc));
+       ret = 0;
+    }
+    pcap_close(pc);
+
+    return ret;
 }
 #endif
 
@@ -1445,8 +1505,7 @@ setactivefilter(argv)
  * setdomain - Set domain name to append to hostname 
  */
 static int
-setdomain(argv)
-    char **argv;
+setdomain(char **argv)
 {
     gethostname(hostname, MAXNAMELEN);
     if (**argv != 0) {
@@ -1459,21 +1518,23 @@ setdomain(argv)
     return (1);
 }
 
-
 static int
-setlogfile(argv)
-    char **argv;
+setlogfile(char **argv)
 {
     int fd, err;
+    uid_t euid;
 
-    if (!privileged_option)
-       seteuid(getuid());
+    euid = geteuid();
+    if (!privileged_option && seteuid(getuid()) == -1) {
+       option_error("unable to drop permissions to open %s: %m", *argv);
+       return 0;
+    }
     fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
     if (fd < 0 && errno == EEXIST)
        fd = open(*argv, O_WRONLY | O_APPEND);
     err = errno;
-    if (!privileged_option)
-       seteuid(0);
+    if (!privileged_option && seteuid(euid) == -1)
+       fatal("unable to regain privileges: %m");
     if (fd < 0) {
        errno = err;
        option_error("Can't open log file %s: %m", *argv);
@@ -1487,10 +1548,10 @@ setlogfile(argv)
     log_default = 0;
     return 1;
 }
+
 #ifdef MAXOCTETS
 static int
-setmodir(argv)
-    char **argv;
+setmodir(char **argv)
 {
     if(*argv == NULL)
        return 0;
@@ -1499,7 +1560,7 @@ setmodir(argv)
     } else if (!strcmp(*argv,"out")) {
         maxoctets_dir = PPP_OCTETS_DIRECTION_OUT;
     } else if (!strcmp(*argv,"max")) {
-        maxoctets_dir = PPP_OCTETS_DIRECTION_MAX;
+        maxoctets_dir = PPP_OCTETS_DIRECTION_MAXOVERAL;
     } else {
         maxoctets_dir = PPP_OCTETS_DIRECTION_SUM;
     }
@@ -1509,13 +1570,12 @@ setmodir(argv)
 
 #ifdef PLUGIN
 static int
-loadplugin(argv)
-    char **argv;
+loadplugin(char **argv)
 {
     char *arg = *argv;
     void *handle;
     const char *err;
-    void (*init) __P((void));
+    void (*init)(void);
     char *path = arg;
     const char *vers;
 
@@ -1562,3 +1622,146 @@ loadplugin(argv)
     return 0;
 }
 #endif /* PLUGIN */
+
+/*
+ * Set an environment variable specified by the user.
+ */
+static int
+user_setenv(char **argv)
+{
+    char *arg = argv[0];
+    char *eqp;
+    struct userenv *uep, **insp;
+
+    if ((eqp = strchr(arg, '=')) == NULL) {
+       option_error("missing = in name=value: %s", arg);
+       return 0;
+    }
+    if (eqp == arg) {
+       option_error("missing variable name: %s", arg);
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       int nlen = strlen(uep->ue_name);
+       if (nlen == (eqp - arg) &&
+           strncmp(arg, uep->ue_name, nlen) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + (eqp-arg));
+       strncpy(uep->ue_name, arg, eqp-arg);
+       uep->ue_name[eqp-arg] = '\0';
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && !uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && !uep->ue_isset)
+           find_option("unset")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 1;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = strdup(eqp + 1);
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_setprint(option_t *opt, printer_func printer, void *arg)
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && !uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && !uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s=%s", uep->ue_name, uep->ue_value);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}
+
+static int
+user_unsetenv(char **argv)
+{
+    struct userenv *uep, **insp;
+    char *arg = argv[0];
+
+    if (strchr(arg, '=') != NULL) {
+       option_error("unexpected = in name: %s", arg);
+       return 0;
+    }
+    if (*arg == '\0') {
+       option_error("missing variable name for unset");
+       return 0;
+    }
+    for (uep = userenv_list; uep != NULL; uep = uep->ue_next) {
+       if (strcmp(arg, uep->ue_name) == 0)
+           break;
+    }
+    /* Ignore attempts by unprivileged users to override privileged sources */
+    if (uep != NULL && !privileged_option && uep->ue_priv)
+       return 1;
+    /* The name never changes, so allocate it with the structure */
+    if (uep == NULL) {
+       uep = malloc(sizeof (*uep) + strlen(arg));
+       strcpy(uep->ue_name, arg);
+       uep->ue_next = NULL;
+       insp = &userenv_list;
+       while (*insp != NULL)
+           insp = &(*insp)->ue_next;
+       *insp = uep;
+    } else {
+       struct userenv *uep2;
+       for (uep2 = userenv_list; uep2 != NULL; uep2 = uep2->ue_next) {
+           if (uep2 != uep && uep2->ue_isset)
+               break;
+       }
+       if (uep2 == NULL && uep->ue_isset)
+           find_option("set")->flags |= OPT_NOPRINT;
+       free(uep->ue_value);
+    }
+    uep->ue_isset = 0;
+    uep->ue_priv = privileged_option;
+    uep->ue_source = option_source;
+    uep->ue_value = NULL;
+    curopt->flags &= ~OPT_NOPRINT;
+    return 1;
+}
+
+static void
+user_unsetprint(option_t *opt, printer_func printer, void *arg)
+{
+    struct userenv *uep, *uepnext;
+
+    uepnext = userenv_list;
+    while (uepnext != NULL && uepnext->ue_isset)
+       uepnext = uepnext->ue_next;
+    while ((uep = uepnext) != NULL) {
+       uepnext = uep->ue_next;
+       while (uepnext != NULL && uepnext->ue_isset)
+           uepnext = uepnext->ue_next;
+       (*printer)(arg, "%s", uep->ue_name);
+       if (uepnext != NULL)
+           (*printer)(arg, "\t\t# (from %s)\n%s ", uep->ue_source, opt->name);
+       else
+           opt->source = uep->ue_source;
+    }
+}