]> git.ozlabs.org Git - ppp.git/blob - pppd/options.c
Multilink support.
[ppp.git] / pppd / options.c
1 /*
2  * options.c - handles option processing for PPP.
3  *
4  * Copyright (c) 1989 Carnegie Mellon University.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by Carnegie Mellon University.  The name of the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  */
19
20 #define RCSID   "$Id: options.c,v 1.72 2000/04/04 07:06:52 paulus Exp $"
21
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <termios.h>
29 #include <syslog.h>
30 #include <string.h>
31 #include <netdb.h>
32 #include <pwd.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #ifdef PLUGIN
38 #include <dlfcn.h>
39 #endif
40 #ifdef PPP_FILTER
41 #include <pcap.h>
42 #include <pcap-int.h>   /* XXX: To get struct pcap */
43 #endif
44
45 #include "pppd.h"
46 #include "pathnames.h"
47 #include "patchlevel.h"
48 #include "fsm.h"
49 #include "lcp.h"
50 #include "ipcp.h"
51 #include "upap.h"
52 #include "chap.h"
53 #include "ccp.h"
54
55 #include <net/ppp-comp.h>
56
57 #if defined(ultrix) || defined(NeXT)
58 char *strdup __P((char *));
59 #endif
60
61 static const char rcsid[] = RCSID;
62
63 /*
64  * Option variables and default values.
65  */
66 #ifdef PPP_FILTER
67 int     dflag = 0;              /* Tell libpcap we want debugging */
68 #endif
69 int     debug = 0;              /* Debug flag */
70 int     kdebugflag = 0;         /* Tell kernel to print debug messages */
71 int     default_device = 1;     /* Using /dev/tty or equivalent */
72 char    devnam[MAXPATHLEN];     /* Device name */
73 int     crtscts = 0;            /* Use hardware flow control */
74 bool    modem = 1;              /* Use modem control lines */
75 int     inspeed = 0;            /* Input/Output speed requested */
76 u_int32_t netmask = 0;          /* IP netmask to set on interface */
77 bool    lockflag = 0;           /* Create lock file to lock the serial dev */
78 bool    nodetach = 0;           /* Don't detach from controlling tty */
79 bool    updetach = 0;           /* Detach once link is up */
80 char    *initializer = NULL;    /* Script to initialize physical link */
81 char    *connect_script = NULL; /* Script to establish physical link */
82 char    *disconnect_script = NULL; /* Script to disestablish physical link */
83 char    *welcomer = NULL;       /* Script to run after phys link estab. */
84 char    *ptycommand = NULL;     /* Command to run on other side of pty */
85 int     maxconnect = 0;         /* Maximum connect time */
86 char    user[MAXNAMELEN];       /* Username for PAP */
87 char    passwd[MAXSECRETLEN];   /* Password for PAP */
88 bool    persist = 0;            /* Reopen link after it goes down */
89 char    our_name[MAXNAMELEN];   /* Our name for authentication purposes */
90 bool    demand = 0;             /* do dial-on-demand */
91 char    *ipparam = NULL;        /* Extra parameter for ip up/down scripts */
92 int     idle_time_limit = 0;    /* Disconnect if idle for this many seconds */
93 int     holdoff = 30;           /* # seconds to pause before reconnecting */
94 bool    holdoff_specified;      /* true if a holdoff value has been given */
95 bool    notty = 0;              /* Stdin/out is not a tty */
96 char    *pty_socket = NULL;     /* Socket to connect to pty */
97 char    *record_file = NULL;    /* File to record chars sent/received */
98 int     using_pty = 0;
99 bool    sync_serial = 0;        /* Device is synchronous serial device */
100 int     log_to_fd = 1;          /* send log messages to this fd too */
101 int     maxfail = 10;           /* max # of unsuccessful connection attempts */
102 char    linkname[MAXPATHLEN];   /* logical name for link */
103 bool    tune_kernel;            /* may alter kernel settings */
104 int     connect_delay = 1000;   /* wait this many ms after connect script */
105 int     max_data_rate;          /* max bytes/sec through charshunt */
106 int     req_unit = -1;          /* requested interface unit */
107 bool    multilink = 0;          /* Enable multilink operation */
108 char    *bundle_name = NULL;    /* bundle name for multilink */
109
110 extern option_t auth_options[];
111 extern struct stat devstat;
112 extern int prepass;             /* Doing pre-pass to find device name */
113
114 struct option_info initializer_info;
115 struct option_info connect_script_info;
116 struct option_info disconnect_script_info;
117 struct option_info welcomer_info;
118 struct option_info devnam_info;
119 struct option_info ptycommand_info;
120
121 #ifdef PPP_FILTER
122 struct  bpf_program pass_filter;/* Filter program for packets to pass */
123 struct  bpf_program active_filter; /* Filter program for link-active pkts */
124 pcap_t  pc;                     /* Fake struct pcap so we can compile expr */
125 #endif
126
127 char *current_option;           /* the name of the option being parsed */
128 int  privileged_option;         /* set iff the current option came from root */
129 char *option_source;            /* string saying where the option came from */
130 bool log_to_file;               /* log_to_fd is a file opened by us */
131
132 /*
133  * Prototypes
134  */
135 static int setdevname __P((char *));
136 static int setipaddr __P((char *));
137 static int setspeed __P((char *));
138 static int noopt __P((char **));
139 static int setdomain __P((char **));
140 static int setnetmask __P((char **));
141 static int setxonxoff __P((char **));
142 static int readfile __P((char **));
143 static int callfile __P((char **));
144 static int showversion __P((char **));
145 static int showhelp __P((char **));
146 static void usage __P((void));
147 static int setlogfile __P((char **));
148 #ifdef PLUGIN
149 static int loadplugin __P((char **));
150 #endif
151
152 #ifdef PPP_FILTER
153 static int setpassfilter __P((char **));
154 static int setactivefilter __P((char **));
155 #endif
156
157 static option_t *find_option __P((char *name));
158 static int process_option __P((option_t *, char **));
159 static int n_arguments __P((option_t *));
160 static int number_option __P((char *, u_int32_t *, int));
161
162 /*
163  * Structure to store extra lists of options.
164  */
165 struct option_list {
166     option_t *options;
167     struct option_list *next;
168 };
169
170 static struct option_list *extra_options = NULL;
171
172 /*
173  * Valid arguments.
174  */
175 option_t general_options[] = {
176     { "debug", o_int, &debug,
177       "Increase debugging level", OPT_INC|OPT_NOARG|1 },
178     { "-d", o_int, &debug,
179       "Increase debugging level", OPT_INC|OPT_NOARG|1 },
180     { "kdebug", o_int, &kdebugflag,
181       "Set kernel driver debug level" },
182     { "nodetach", o_bool, &nodetach,
183       "Don't detach from controlling tty", 1 },
184     { "-detach", o_bool, &nodetach,
185       "Don't detach from controlling tty", 1 },
186     { "updetach", o_bool, &updetach,
187       "Detach from controlling tty once link is up", 1 },
188     { "holdoff", o_int, &holdoff,
189       "Set time in seconds before retrying connection" },
190     { "idle", o_int, &idle_time_limit,
191       "Set time in seconds before disconnecting idle link" },
192     { "lock", o_bool, &lockflag,
193       "Lock serial device with UUCP-style lock file", 1 },
194     { "-all", o_special_noarg, noopt,
195       "Don't request/allow any LCP or IPCP options (useless)" },
196     { "init", o_string, &initializer,
197       "A program to initialize the device",
198       OPT_A2INFO | OPT_PRIVFIX, &initializer_info },
199     { "connect", o_string, &connect_script,
200       "A program to set up a connection",
201       OPT_A2INFO | OPT_PRIVFIX, &connect_script_info },
202     { "disconnect", o_string, &disconnect_script,
203       "Program to disconnect serial device",
204       OPT_A2INFO | OPT_PRIVFIX, &disconnect_script_info },
205     { "welcome", o_string, &welcomer,
206       "Script to welcome client",
207       OPT_A2INFO | OPT_PRIVFIX, &welcomer_info },
208     { "pty", o_string, &ptycommand,
209       "Script to run on pseudo-tty master side",
210       OPT_A2INFO | OPT_PRIVFIX | OPT_DEVNAM, &ptycommand_info },
211     { "notty", o_bool, &notty,
212       "Input/output is not a tty", OPT_DEVNAM | 1 },
213     { "socket", o_string, &pty_socket,
214       "Send and receive over socket, arg is host:port", OPT_DEVNAM },
215     { "record", o_string, &record_file,
216       "Record characters sent/received to file" },
217     { "maxconnect", o_int, &maxconnect,
218       "Set connection time limit", OPT_LLIMIT|OPT_NOINCR|OPT_ZEROINF },
219     { "crtscts", o_int, &crtscts,
220       "Set hardware (RTS/CTS) flow control", OPT_NOARG|OPT_VAL(1) },
221     { "nocrtscts", o_int, &crtscts,
222       "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
223     { "-crtscts", o_int, &crtscts,
224       "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
225     { "cdtrcts", o_int, &crtscts,
226       "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG|OPT_VAL(2) },
227     { "nocdtrcts", o_int, &crtscts,
228       "Disable hardware flow control", OPT_NOARG|OPT_VAL(-1) },
229     { "xonxoff", o_special_noarg, setxonxoff,
230       "Set software (XON/XOFF) flow control" },
231     { "domain", o_special, setdomain,
232       "Add given domain name to hostname" },
233     { "mtu", o_int, &lcp_allowoptions[0].mru,
234       "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU },
235     { "netmask", o_special, setnetmask,
236       "set netmask" },
237     { "modem", o_bool, &modem,
238       "Use modem control lines", 1 },
239     { "local", o_bool, &modem,
240       "Don't use modem control lines" },
241     { "file", o_special, readfile,
242       "Take options from a file", OPT_PREPASS },
243     { "call", o_special, callfile,
244       "Take options from a privileged file", OPT_PREPASS },
245     { "persist", o_bool, &persist,
246       "Keep on reopening connection after close", 1 },
247     { "nopersist", o_bool, &persist,
248       "Turn off persist option" },
249     { "demand", o_bool, &demand,
250       "Dial on demand", OPT_INITONLY | 1, &persist },
251     { "--version", o_special_noarg, showversion,
252       "Show version number" },
253     { "--help", o_special_noarg, showhelp,
254       "Show brief listing of options" },
255     { "-h", o_special_noarg, showhelp,
256       "Show brief listing of options" },
257     { "sync", o_bool, &sync_serial,
258       "Use synchronous HDLC serial encoding", 1 },
259     { "logfd", o_int, &log_to_fd,
260       "Send log messages to this file descriptor" },
261     { "logfile", o_special, setlogfile,
262       "Append log messages to this file" },
263     { "nolog", o_int, &log_to_fd,
264       "Don't send log messages to any file",
265       OPT_NOARG | OPT_VAL(-1) },
266     { "nologfd", o_int, &log_to_fd,
267       "Don't send log messages to any file descriptor",
268       OPT_NOARG | OPT_VAL(-1) },
269     { "linkname", o_string, linkname,
270       "Set logical name for link",
271       OPT_PRIV|OPT_STATIC, NULL, MAXPATHLEN },
272     { "maxfail", o_int, &maxfail,
273       "Maximum number of unsuccessful connection attempts to allow" },
274     { "ktune", o_bool, &tune_kernel,
275       "Alter kernel settings as necessary", 1 },
276     { "noktune", o_bool, &tune_kernel,
277       "Don't alter kernel settings", 0 },
278     { "connect-delay", o_int, &connect_delay,
279       "Maximum time (in ms) to wait after connect script finishes" },
280     { "datarate", o_int, &max_data_rate,
281       "Maximum data rate in bytes/sec (with pty, notty or record option)" },
282     { "unit", o_int, &req_unit,
283       "PPP interface unit number to use if possible", OPT_LLIMIT, 0, 0 },
284 #ifdef HAVE_MULTILINK
285     { "multilink", o_bool, &multilink,
286       "Enable multilink operation", 1 },
287     { "nomultilink", o_bool, &multilink,
288       "Disable multilink operation", 0 },
289     { "mp", o_bool, &multilink,
290       "Enable multilink operation", 1 },
291     { "nomp", o_bool, &multilink,
292       "Disable multilink operation", 0 },
293     { "bundle", o_string, &bundle_name,
294       "Bundle name for multilink" },
295 #endif /* HAVE_MULTILINK */
296 #ifdef PLUGIN
297     { "plugin", o_special, loadplugin,
298       "Load a plug-in module into pppd", OPT_PRIV },
299 #endif
300
301 #ifdef PPP_FILTER
302     { "pdebug", o_int, &dflag,
303       "libpcap debugging" },
304     { "pass-filter", 1, setpassfilter,
305       "set filter for packets to pass" },
306     { "active-filter", 1, setactivefilter,
307       "set filter for active pkts" },
308 #endif
309
310     { NULL }
311 };
312
313 #ifndef IMPLEMENTATION
314 #define IMPLEMENTATION ""
315 #endif
316
317 static char *usage_string = "\
318 pppd version %s.%d%s\n\
319 Usage: %s [ options ], where options are:\n\
320         <device>        Communicate over the named device\n\
321         <speed>         Set the baud rate to <speed>\n\
322         <loc>:<rem>     Set the local and/or remote interface IP\n\
323                         addresses.  Either one may be omitted.\n\
324         asyncmap <n>    Set the desired async map to hex <n>\n\
325         auth            Require authentication from peer\n\
326         connect <p>     Invoke shell command <p> to set up the serial line\n\
327         crtscts         Use hardware RTS/CTS flow control\n\
328         defaultroute    Add default route through interface\n\
329         file <f>        Take options from file <f>\n\
330         modem           Use modem control lines\n\
331         mru <n>         Set MRU value to <n> for negotiation\n\
332 See pppd(8) for more options.\n\
333 ";
334
335 /*
336  * parse_args - parse a string of arguments from the command line.
337  * If prepass is true, we are scanning for the device name and only
338  * processing a few options, so error messages are suppressed.
339  */
340 int
341 parse_args(argc, argv)
342     int argc;
343     char **argv;
344 {
345     char *arg;
346     option_t *opt;
347     int ret;
348
349     privileged_option = privileged;
350     option_source = "command line";
351     while (argc > 0) {
352         arg = *argv++;
353         --argc;
354
355         /*
356          * First see if it's an option in the new option list.
357          */
358         opt = find_option(arg);
359         if (opt != NULL) {
360             int n = n_arguments(opt);
361             if (argc < n) {
362                 option_error("too few parameters for option %s", arg);
363                 return 0;
364             }
365             current_option = arg;
366             if (!process_option(opt, argv))
367                 return 0;
368             argc -= n;
369             argv += n;
370             continue;
371         }
372
373         /*
374          * Maybe a tty name, speed or IP address?
375          */
376         if ((ret = setdevname(arg)) == 0
377             && (ret = setspeed(arg)) == 0
378             && (ret = setipaddr(arg)) == 0
379             && !prepass) {
380             option_error("unrecognized option '%s'", arg);
381             usage();
382             return 0;
383         }
384         if (ret < 0)    /* error */
385             return 0;
386     }
387     return 1;
388 }
389
390 #if 0
391 /*
392  * scan_args - scan the command line arguments to get the tty name,
393  * if specified.  Also checks whether the notty or pty option was given.
394  */
395 void
396 scan_args(argc, argv)
397     int argc;
398     char **argv;
399 {
400     char *arg;
401     option_t *opt;
402
403     privileged_option = privileged;
404     while (argc > 0) {
405         arg = *argv++;
406         --argc;
407
408         if (strcmp(arg, "notty") == 0 || strcmp(arg, "pty") == 0)
409             using_pty = 1;
410
411         /* Skip options and their arguments */
412         opt = find_option(arg);
413         if (opt != NULL) {
414             int n = n_arguments(opt);
415             argc -= n;
416             argv += n;
417             continue;
418         }
419
420         /* Check if it's a tty name and copy it if so */
421         (void) setdevname(arg, 1);
422     }
423 }
424 #endif
425
426 /*
427  * options_from_file - Read a string of options from a file,
428  * and interpret them.
429  */
430 int
431 options_from_file(filename, must_exist, check_prot, priv)
432     char *filename;
433     int must_exist;
434     int check_prot;
435     int priv;
436 {
437     FILE *f;
438     int i, newline, ret, err;
439     option_t *opt;
440     int oldpriv;
441     char *oldsource;
442     char *argv[MAXARGS];
443     char args[MAXARGS][MAXWORDLEN];
444     char cmd[MAXWORDLEN];
445
446     if (check_prot)
447         seteuid(getuid());
448     f = fopen(filename, "r");
449     err = errno;
450     if (check_prot)
451         seteuid(0);
452     if (f == NULL) {
453         if (!must_exist && err == ENOENT)
454             return 1;
455         errno = err;
456         option_error("Can't open options file %s: %m", filename);
457         return 0;
458     }
459
460     oldpriv = privileged_option;
461     privileged_option = priv;
462     oldsource = option_source;
463     option_source = strdup(filename);
464     if (option_source == NULL)
465         option_source = "file";
466     ret = 0;
467     while (getword(f, cmd, &newline, filename)) {
468         /*
469          * First see if it's a command.
470          */
471         opt = find_option(cmd);
472         if (opt != NULL) {
473             int n = n_arguments(opt);
474             for (i = 0; i < n; ++i) {
475                 if (!getword(f, args[i], &newline, filename)) {
476                     option_error(
477                         "In file %s: too few parameters for option '%s'",
478                         filename, cmd);
479                     goto err;
480                 }
481                 argv[i] = args[i];
482             }
483             current_option = cmd;
484             if ((opt->flags & OPT_DEVEQUIV) && devnam_fixed) {
485                 option_error("the %s option may not be used in the %s file",
486                              cmd, filename);
487                 goto err;
488             }
489             if (!process_option(opt, argv))
490                 goto err;
491             continue;
492         }
493
494         /*
495          * Maybe a tty name, speed or IP address?
496          */
497         if ((i = setdevname(cmd)) == 0
498             && (i = setspeed(cmd)) == 0
499             && (i = setipaddr(cmd)) == 0) {
500             option_error("In file %s: unrecognized option '%s'",
501                          filename, cmd);
502             goto err;
503         }
504         if (i < 0)              /* error */
505             goto err;
506     }
507     ret = 1;
508
509 err:
510     fclose(f);
511     privileged_option = oldpriv;
512     option_source = oldsource;
513     return ret;
514 }
515
516 /*
517  * options_from_user - See if the use has a ~/.ppprc file,
518  * and if so, interpret options from it.
519  */
520 int
521 options_from_user()
522 {
523     char *user, *path, *file;
524     int ret;
525     struct passwd *pw;
526     size_t pl;
527
528     pw = getpwuid(getuid());
529     if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
530         return 1;
531     file = _PATH_USEROPT;
532     pl = strlen(user) + strlen(file) + 2;
533     path = malloc(pl);
534     if (path == NULL)
535         novm("init file name");
536     slprintf(path, pl, "%s/%s", user, file);
537     ret = options_from_file(path, 0, 1, privileged);
538     free(path);
539     return ret;
540 }
541
542 /*
543  * options_for_tty - See if an options file exists for the serial
544  * device, and if so, interpret options from it.
545  */
546 int
547 options_for_tty()
548 {
549     char *dev, *path, *p;
550     int ret;
551     size_t pl;
552
553     dev = devnam;
554     if (strncmp(dev, "/dev/", 5) == 0)
555         dev += 5;
556     if (dev[0] == 0 || strcmp(dev, "tty") == 0)
557         return 1;               /* don't look for /etc/ppp/options.tty */
558     pl = strlen(_PATH_TTYOPT) + strlen(dev) + 1;
559     path = malloc(pl);
560     if (path == NULL)
561         novm("tty init file name");
562     slprintf(path, pl, "%s%s", _PATH_TTYOPT, dev);
563     /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
564     for (p = path + strlen(_PATH_TTYOPT); *p != 0; ++p)
565         if (*p == '/')
566             *p = '.';
567     ret = options_from_file(path, 0, 0, 1);
568     free(path);
569     return ret;
570 }
571
572 /*
573  * options_from_list - process a string of options in a wordlist.
574  */
575 int
576 options_from_list(w, priv)
577     struct wordlist *w;
578     int priv;
579 {
580     char *argv[MAXARGS];
581     option_t *opt;
582     int i, ret = 0;
583
584     privileged_option = priv;
585     option_source = "secrets file";
586
587     while (w != NULL) {
588         /*
589          * First see if it's a command.
590          */
591         opt = find_option(w->word);
592         if (opt != NULL) {
593             int n = n_arguments(opt);
594             struct wordlist *w0 = w;
595             for (i = 0; i < n; ++i) {
596                 w = w->next;
597                 if (w == NULL) {
598                     option_error(
599                         "In secrets file: too few parameters for option '%s'",
600                         w0->word);
601                     goto err;
602                 }
603                 argv[i] = w->word;
604             }
605             current_option = w0->word;
606             if (!process_option(opt, argv))
607                 goto err;
608             continue;
609         }
610
611         /*
612          * Maybe a tty name, speed or IP address?
613          */
614         if ((i = setdevname(w->word)) == 0
615             && (i = setspeed(w->word)) == 0
616             && (i = setipaddr(w->word)) == 0) {
617             option_error("In secrets file: unrecognized option '%s'",
618                          w->word);
619             goto err;
620         }
621         if (i < 0)              /* error */
622             goto err;
623     }
624     ret = 1;
625
626 err:
627     return ret;
628 }
629
630 /*
631  * find_option - scan the option lists for the various protocols
632  * looking for an entry with the given name.
633  * This could be optimized by using a hash table.
634  */
635 static option_t *
636 find_option(name)
637     char *name;
638 {
639     option_t *opt;
640     struct option_list *list;
641     int i;
642
643     for (list = extra_options; list != NULL; list = list->next)
644         for (opt = list->options; opt->name != NULL; ++opt)
645             if (strcmp(name, opt->name) == 0)
646                 return opt;
647     for (opt = general_options; opt->name != NULL; ++opt)
648         if (strcmp(name, opt->name) == 0)
649             return opt;
650     for (opt = auth_options; opt->name != NULL; ++opt)
651         if (strcmp(name, opt->name) == 0)
652             return opt;
653     for (i = 0; protocols[i] != NULL; ++i)
654         if ((opt = protocols[i]->options) != NULL)
655             for (; opt->name != NULL; ++opt)
656                 if (strcmp(name, opt->name) == 0)
657                     return opt;
658     return NULL;
659 }
660
661 /*
662  * process_option - process one new-style option.
663  */
664 static int
665 process_option(opt, argv)
666     option_t *opt;
667     char **argv;
668 {
669     u_int32_t v;
670     int iv, a;
671     char *sv;
672     int (*parser) __P((char **));
673
674     if ((opt->flags & OPT_PREPASS) == 0 && prepass)
675         return 1;
676     if ((opt->flags & OPT_INITONLY) && phase != PHASE_INITIALIZE) {
677         option_error("it's too late to use the %s option", opt->name);
678         return 0;
679     }
680     if ((opt->flags & OPT_PRIV) && !privileged_option) {
681         option_error("using the %s option requires root privilege", opt->name);
682         return 0;
683     }
684     if ((opt->flags & OPT_ENABLE) && *(bool *)(opt->addr2) == 0) {
685         option_error("%s option is disabled", opt->name);
686         return 0;
687     }
688     if ((opt->flags & OPT_PRIVFIX) && !privileged_option) {
689         struct option_info *ip = (struct option_info *) opt->addr2;
690         if (ip && ip->priv) {
691             option_error("%s option cannot be overridden", opt->name);
692             return 0;
693         }
694     }
695
696     switch (opt->type) {
697     case o_bool:
698         v = opt->flags & OPT_VALUE;
699         *(bool *)(opt->addr) = v;
700         if (opt->addr2 && (opt->flags & OPT_A2COPY))
701             *(bool *)(opt->addr2) = v;
702         break;
703
704     case o_int:
705         iv = 0;
706         if ((opt->flags & OPT_NOARG) == 0) {
707             if (!int_option(*argv, &iv))
708                 return 0;
709             if ((((opt->flags & OPT_LLIMIT) && iv < opt->lower_limit)
710                  || ((opt->flags & OPT_ULIMIT) && iv > opt->upper_limit))
711                 && !((opt->flags & OPT_ZEROOK && iv == 0))) {
712                 char *zok = (opt->flags & OPT_ZEROOK)? " zero or": "";
713                 switch (opt->flags & OPT_LIMITS) {
714                 case OPT_LLIMIT:
715                     option_error("%s value must be%s >= %d",
716                                  opt->name, zok, opt->lower_limit);
717                     break;
718                 case OPT_ULIMIT:
719                     option_error("%s value must be%s <= %d",
720                                  opt->name, zok, opt->upper_limit);
721                     break;
722                 case OPT_LIMITS:
723                     option_error("%s value must be%s between %d and %d",
724                                 opt->name, opt->lower_limit, opt->upper_limit);
725                     break;
726                 }
727                 return 0;
728             }
729         }
730         a = opt->flags & OPT_VALUE;
731         if (a >= 128)
732             a -= 256;           /* sign extend */
733         iv += a;
734         if (opt->flags & OPT_INC)
735             iv += *(int *)(opt->addr);
736         if ((opt->flags & OPT_NOINCR) && !privileged_option) {
737             int oldv = *(int *)(opt->addr);
738             if ((opt->flags & OPT_ZEROINF) ?
739                 (oldv != 0 && (iv == 0 || iv > oldv)) : (iv > oldv)) {
740                 option_error("%s value cannot be increased", opt->name);
741                 return 0;
742             }
743         }
744         *(int *)(opt->addr) = iv;
745         if (opt->addr2 && (opt->flags & OPT_A2COPY))
746             *(int *)(opt->addr2) = iv;
747         break;
748
749     case o_uint32:
750         if (opt->flags & OPT_NOARG) {
751             v = opt->flags & OPT_VALUE;
752         } else if (!number_option(*argv, &v, 16))
753             return 0;
754         if (opt->flags & OPT_OR)
755             v |= *(u_int32_t *)(opt->addr);
756         *(u_int32_t *)(opt->addr) = v;
757         if (opt->addr2 && (opt->flags & OPT_A2COPY))
758             *(u_int32_t *)(opt->addr2) = v;
759         break;
760
761     case o_string:
762         if (opt->flags & OPT_STATIC) {
763             strlcpy((char *)(opt->addr), *argv, opt->upper_limit);
764         } else {
765             sv = strdup(*argv);
766             if (sv == NULL)
767                 novm("option argument");
768             *(char **)(opt->addr) = sv;
769         }
770         break;
771
772     case o_special_noarg:
773     case o_special:
774         parser = (int (*) __P((char **))) opt->addr;
775         if (!(*parser)(argv))
776             return 0;
777         break;
778     }
779
780     if (opt->addr2) {
781         if (opt->flags & OPT_A2INFO) {
782             struct option_info *ip = (struct option_info *) opt->addr2;
783             ip->priv = privileged_option;
784             ip->source = option_source;
785         } else if ((opt->flags & (OPT_A2COPY|OPT_ENABLE)) == 0)
786             *(bool *)(opt->addr2) = 1;
787     }
788
789     return 1;
790 }
791
792 /*
793  * n_arguments - tell how many arguments an option takes
794  */
795 static int
796 n_arguments(opt)
797     option_t *opt;
798 {
799     return (opt->type == o_bool || opt->type == o_special_noarg
800             || (opt->flags & OPT_NOARG))? 0: 1;
801 }
802
803 /*
804  * add_options - add a list of options to the set we grok.
805  */
806 void
807 add_options(opt)
808     option_t *opt;
809 {
810     struct option_list *list;
811
812     list = malloc(sizeof(*list));
813     if (list == 0)
814         novm("option list entry");
815     list->options = opt;
816     list->next = extra_options;
817     extra_options = list;
818 }
819
820 /*
821  * usage - print out a message telling how to use the program.
822  */
823 static void
824 usage()
825 {
826     if (phase == PHASE_INITIALIZE)
827         fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
828                 progname);
829 }
830
831 /*
832  * showhelp - print out usage message and exit.
833  */
834 static int
835 showhelp(argv)
836     char **argv;
837 {
838     if (phase == PHASE_INITIALIZE) {
839         usage();
840         exit(0);
841     }
842     return 0;
843 }
844
845 /*
846  * showversion - print out the version number and exit.
847  */
848 static int
849 showversion(argv)
850     char **argv;
851 {
852     if (phase == PHASE_INITIALIZE) {
853         fprintf(stderr, "pppd version %s.%d%s\n",
854                 VERSION, PATCHLEVEL, IMPLEMENTATION);
855         exit(0);
856     }
857     return 0;
858 }
859
860 /*
861  * option_error - print a message about an error in an option.
862  * The message is logged, and also sent to
863  * stderr if phase == PHASE_INITIALIZE.
864  */
865 void
866 option_error __V((char *fmt, ...))
867 {
868     va_list args;
869     char buf[256];
870
871 #if defined(__STDC__)
872     va_start(args, fmt);
873 #else
874     char *fmt;
875     va_start(args);
876     fmt = va_arg(args, char *);
877 #endif
878     if (prepass) {
879         va_end(args);
880         return;
881     }
882     vslprintf(buf, sizeof(buf), fmt, args);
883     va_end(args);
884     if (phase == PHASE_INITIALIZE)
885         fprintf(stderr, "%s: %s\n", progname, buf);
886     syslog(LOG_ERR, "%s", buf);
887 }
888
889 #if 0
890 /*
891  * readable - check if a file is readable by the real user.
892  */
893 int
894 readable(fd)
895     int fd;
896 {
897     uid_t uid;
898     int i;
899     struct stat sbuf;
900
901     uid = getuid();
902     if (uid == 0)
903         return 1;
904     if (fstat(fd, &sbuf) != 0)
905         return 0;
906     if (sbuf.st_uid == uid)
907         return sbuf.st_mode & S_IRUSR;
908     if (sbuf.st_gid == getgid())
909         return sbuf.st_mode & S_IRGRP;
910     for (i = 0; i < ngroups; ++i)
911         if (sbuf.st_gid == groups[i])
912             return sbuf.st_mode & S_IRGRP;
913     return sbuf.st_mode & S_IROTH;
914 }
915 #endif
916
917 /*
918  * Read a word from a file.
919  * Words are delimited by white-space or by quotes (" or ').
920  * Quotes, white-space and \ may be escaped with \.
921  * \<newline> is ignored.
922  */
923 int
924 getword(f, word, newlinep, filename)
925     FILE *f;
926     char *word;
927     int *newlinep;
928     char *filename;
929 {
930     int c, len, escape;
931     int quoted, comment;
932     int value, digit, got, n;
933
934 #define isoctal(c) ((c) >= '0' && (c) < '8')
935
936     *newlinep = 0;
937     len = 0;
938     escape = 0;
939     comment = 0;
940
941     /*
942      * First skip white-space and comments.
943      */
944     for (;;) {
945         c = getc(f);
946         if (c == EOF)
947             break;
948
949         /*
950          * A newline means the end of a comment; backslash-newline
951          * is ignored.  Note that we cannot have escape && comment.
952          */
953         if (c == '\n') {
954             if (!escape) {
955                 *newlinep = 1;
956                 comment = 0;
957             } else
958                 escape = 0;
959             continue;
960         }
961
962         /*
963          * Ignore characters other than newline in a comment.
964          */
965         if (comment)
966             continue;
967
968         /*
969          * If this character is escaped, we have a word start.
970          */
971         if (escape)
972             break;
973
974         /*
975          * If this is the escape character, look at the next character.
976          */
977         if (c == '\\') {
978             escape = 1;
979             continue;
980         }
981
982         /*
983          * If this is the start of a comment, ignore the rest of the line.
984          */
985         if (c == '#') {
986             comment = 1;
987             continue;
988         }
989
990         /*
991          * A non-whitespace character is the start of a word.
992          */
993         if (!isspace(c))
994             break;
995     }
996
997     /*
998      * Save the delimiter for quoted strings.
999      */
1000     if (!escape && (c == '"' || c == '\'')) {
1001         quoted = c;
1002         c = getc(f);
1003     } else
1004         quoted = 0;
1005
1006     /*
1007      * Process characters until the end of the word.
1008      */
1009     while (c != EOF) {
1010         if (escape) {
1011             /*
1012              * This character is escaped: backslash-newline is ignored,
1013              * various other characters indicate particular values
1014              * as for C backslash-escapes.
1015              */
1016             escape = 0;
1017             if (c == '\n') {
1018                 c = getc(f);
1019                 continue;
1020             }
1021
1022             got = 0;
1023             switch (c) {
1024             case 'a':
1025                 value = '\a';
1026                 break;
1027             case 'b':
1028                 value = '\b';
1029                 break;
1030             case 'f':
1031                 value = '\f';
1032                 break;
1033             case 'n':
1034                 value = '\n';
1035                 break;
1036             case 'r':
1037                 value = '\r';
1038                 break;
1039             case 's':
1040                 value = ' ';
1041                 break;
1042             case 't':
1043                 value = '\t';
1044                 break;
1045
1046             default:
1047                 if (isoctal(c)) {
1048                     /*
1049                      * \ddd octal sequence
1050                      */
1051                     value = 0;
1052                     for (n = 0; n < 3 && isoctal(c); ++n) {
1053                         value = (value << 3) + (c & 07);
1054                         c = getc(f);
1055                     }
1056                     got = 1;
1057                     break;
1058                 }
1059
1060                 if (c == 'x') {
1061                     /*
1062                      * \x<hex_string> sequence
1063                      */
1064                     value = 0;
1065                     c = getc(f);
1066                     for (n = 0; n < 2 && isxdigit(c); ++n) {
1067                         digit = toupper(c) - '0';
1068                         if (digit > 10)
1069                             digit += '0' + 10 - 'A';
1070                         value = (value << 4) + digit;
1071                         c = getc (f);
1072                     }
1073                     got = 1;
1074                     break;
1075                 }
1076
1077                 /*
1078                  * Otherwise the character stands for itself.
1079                  */
1080                 value = c;
1081                 break;
1082             }
1083
1084             /*
1085              * Store the resulting character for the escape sequence.
1086              */
1087             if (len < MAXWORDLEN-1)
1088                 word[len] = value;
1089             ++len;
1090
1091             if (!got)
1092                 c = getc(f);
1093             continue;
1094
1095         }
1096
1097         /*
1098          * Not escaped: see if we've reached the end of the word.
1099          */
1100         if (quoted) {
1101             if (c == quoted)
1102                 break;
1103         } else {
1104             if (isspace(c) || c == '#') {
1105                 ungetc (c, f);
1106                 break;
1107             }
1108         }
1109
1110         /*
1111          * Backslash starts an escape sequence.
1112          */
1113         if (c == '\\') {
1114             escape = 1;
1115             c = getc(f);
1116             continue;
1117         }
1118
1119         /*
1120          * An ordinary character: store it in the word and get another.
1121          */
1122         if (len < MAXWORDLEN-1)
1123             word[len] = c;
1124         ++len;
1125
1126         c = getc(f);
1127     }
1128
1129     /*
1130      * End of the word: check for errors.
1131      */
1132     if (c == EOF) {
1133         if (ferror(f)) {
1134             if (errno == 0)
1135                 errno = EIO;
1136             option_error("Error reading %s: %m", filename);
1137             die(1);
1138         }
1139         /*
1140          * If len is zero, then we didn't find a word before the
1141          * end of the file.
1142          */
1143         if (len == 0)
1144             return 0;
1145     }
1146
1147     /*
1148      * Warn if the word was too long, and append a terminating null.
1149      */
1150     if (len >= MAXWORDLEN) {
1151         option_error("warning: word in file %s too long (%.20s...)",
1152                      filename, word);
1153         len = MAXWORDLEN - 1;
1154     }
1155     word[len] = 0;
1156
1157     return 1;
1158
1159 #undef isoctal
1160
1161 }
1162
1163 /*
1164  * number_option - parse an unsigned numeric parameter for an option.
1165  */
1166 static int
1167 number_option(str, valp, base)
1168     char *str;
1169     u_int32_t *valp;
1170     int base;
1171 {
1172     char *ptr;
1173
1174     *valp = strtoul(str, &ptr, base);
1175     if (ptr == str) {
1176         option_error("invalid numeric parameter '%s' for %s option",
1177                      str, current_option);
1178         return 0;
1179     }
1180     return 1;
1181 }
1182
1183
1184 /*
1185  * int_option - like number_option, but valp is int *,
1186  * the base is assumed to be 0, and *valp is not changed
1187  * if there is an error.
1188  */
1189 int
1190 int_option(str, valp)
1191     char *str;
1192     int *valp;
1193 {
1194     u_int32_t v;
1195
1196     if (!number_option(str, &v, 0))
1197         return 0;
1198     *valp = (int) v;
1199     return 1;
1200 }
1201
1202
1203 /*
1204  * The following procedures parse options.
1205  */
1206
1207 /*
1208  * readfile - take commands from a file.
1209  */
1210 static int
1211 readfile(argv)
1212     char **argv;
1213 {
1214     return options_from_file(*argv, 1, 1, privileged_option);
1215 }
1216
1217 /*
1218  * callfile - take commands from /etc/ppp/peers/<name>.
1219  * Name may not contain /../, start with / or ../, or end in /..
1220  */
1221 static int
1222 callfile(argv)
1223     char **argv;
1224 {
1225     char *fname, *arg, *p;
1226     int l, ok;
1227
1228     arg = *argv;
1229     ok = 1;
1230     if (arg[0] == '/' || arg[0] == 0)
1231         ok = 0;
1232     else {
1233         for (p = arg; *p != 0; ) {
1234             if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
1235                 ok = 0;
1236                 break;
1237             }
1238             while (*p != '/' && *p != 0)
1239                 ++p;
1240             if (*p == '/')
1241                 ++p;
1242         }
1243     }
1244     if (!ok) {
1245         option_error("call option value may not contain .. or start with /");
1246         return 0;
1247     }
1248
1249     l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
1250     if ((fname = (char *) malloc(l)) == NULL)
1251         novm("call file name");
1252     slprintf(fname, l, "%s%s", _PATH_PEERFILES, arg);
1253
1254     ok = options_from_file(fname, 1, 1, 1);
1255
1256     free(fname);
1257     return ok;
1258 }
1259
1260 #ifdef PPP_FILTER
1261 /*
1262  * setpdebug - Set libpcap debugging level.
1263  */
1264 static int
1265 setpdebug(argv)
1266     char **argv;
1267 {
1268     return int_option(*argv, &dflag);
1269 }
1270
1271 /*
1272  * setpassfilter - Set the pass filter for packets
1273  */
1274 static int
1275 setpassfilter(argv)
1276     char **argv;
1277 {
1278     pc.linktype = DLT_PPP;
1279     pc.snapshot = PPP_HDRLEN;
1280  
1281     if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
1282         return 1;
1283     option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
1284     return 0;
1285 }
1286
1287 /*
1288  * setactivefilter - Set the active filter for packets
1289  */
1290 static int
1291 setactivefilter(argv)
1292     char **argv;
1293 {
1294     pc.linktype = DLT_PPP;
1295     pc.snapshot = PPP_HDRLEN;
1296  
1297     if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
1298         return 1;
1299     option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
1300     return 0;
1301 }
1302 #endif
1303
1304 /*
1305  * noopt - Disable all options.
1306  */
1307 static int
1308 noopt(argv)
1309     char **argv;
1310 {
1311     BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
1312     BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
1313     BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
1314     BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
1315
1316     return (1);
1317 }
1318
1319 /*
1320  * setdomain - Set domain name to append to hostname 
1321  */
1322 static int
1323 setdomain(argv)
1324     char **argv;
1325 {
1326     if (!privileged_option) {
1327         option_error("using the domain option requires root privilege");
1328         return 0;
1329     }
1330     gethostname(hostname, MAXNAMELEN);
1331     if (**argv != 0) {
1332         if (**argv != '.')
1333             strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
1334         strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
1335     }
1336     hostname[MAXNAMELEN-1] = 0;
1337     return (1);
1338 }
1339
1340
1341 /*
1342  * setspeed - Set the speed.
1343  */
1344 static int
1345 setspeed(arg)
1346     char *arg;
1347 {
1348     char *ptr;
1349     int spd;
1350
1351     if (prepass)
1352         return 1;
1353     spd = strtol(arg, &ptr, 0);
1354     if (ptr == arg || *ptr != 0 || spd == 0)
1355         return 0;
1356     inspeed = spd;
1357     return 1;
1358 }
1359
1360
1361 /*
1362  * setdevname - Set the device name.
1363  */
1364 static int
1365 setdevname(cp)
1366     char *cp;
1367 {
1368     struct stat statbuf;
1369     char dev[MAXPATHLEN];
1370
1371     if (*cp == 0)
1372         return 0;
1373
1374     if (strncmp("/dev/", cp, 5) != 0) {
1375         strlcpy(dev, "/dev/", sizeof(dev));
1376         strlcat(dev, cp, sizeof(dev));
1377         cp = dev;
1378     }
1379
1380     /*
1381      * Check if there is a character device by this name.
1382      */
1383     if (stat(cp, &statbuf) < 0) {
1384         if (errno == ENOENT)
1385             return 0;
1386         option_error("Couldn't stat %s: %m", cp);
1387         return -1;
1388     }
1389     if (!S_ISCHR(statbuf.st_mode)) {
1390         option_error("%s is not a character device", cp);
1391         return -1;
1392     }
1393
1394     if (phase != PHASE_INITIALIZE) {
1395         option_error("device name cannot be changed after initialization");
1396         return -1;
1397     } else if (devnam_fixed) {
1398         option_error("per-tty options file may not specify device name");
1399         return -1;
1400     }
1401
1402     if (devnam_info.priv && !privileged_option) {
1403         option_error("device name cannot be overridden");
1404         return -1;
1405     }
1406
1407     strlcpy(devnam, cp, sizeof(devnam));
1408     devstat = statbuf;
1409     default_device = 0;
1410     devnam_info.priv = privileged_option;
1411     devnam_info.source = option_source;
1412   
1413     return 1;
1414 }
1415
1416
1417 /*
1418  * setipaddr - Set the IP address
1419  */
1420 static int
1421 setipaddr(arg)
1422     char *arg;
1423 {
1424     struct hostent *hp;
1425     char *colon;
1426     u_int32_t local, remote;
1427     ipcp_options *wo = &ipcp_wantoptions[0];
1428   
1429     /*
1430      * IP address pair separated by ":".
1431      */
1432     if ((colon = strchr(arg, ':')) == NULL)
1433         return 0;
1434     if (prepass)
1435         return 1;
1436   
1437     /*
1438      * If colon first character, then no local addr.
1439      */
1440     if (colon != arg) {
1441         *colon = '\0';
1442         if ((local = inet_addr(arg)) == (u_int32_t) -1) {
1443             if ((hp = gethostbyname(arg)) == NULL) {
1444                 option_error("unknown host: %s", arg);
1445                 return -1;
1446             } else {
1447                 local = *(u_int32_t *)hp->h_addr;
1448             }
1449         }
1450         if (bad_ip_adrs(local)) {
1451             option_error("bad local IP address %s", ip_ntoa(local));
1452             return -1;
1453         }
1454         if (local != 0)
1455             wo->ouraddr = local;
1456         *colon = ':';
1457     }
1458   
1459     /*
1460      * If colon last character, then no remote addr.
1461      */
1462     if (*++colon != '\0') {
1463         if ((remote = inet_addr(colon)) == (u_int32_t) -1) {
1464             if ((hp = gethostbyname(colon)) == NULL) {
1465                 option_error("unknown host: %s", colon);
1466                 return -1;
1467             } else {
1468                 remote = *(u_int32_t *)hp->h_addr;
1469                 if (remote_name[0] == 0)
1470                     strlcpy(remote_name, colon, sizeof(remote_name));
1471             }
1472         }
1473         if (bad_ip_adrs(remote)) {
1474             option_error("bad remote IP address %s", ip_ntoa(remote));
1475             return -1;
1476         }
1477         if (remote != 0)
1478             wo->hisaddr = remote;
1479     }
1480
1481     return 1;
1482 }
1483
1484
1485 /*
1486  * setnetmask - set the netmask to be used on the interface.
1487  */
1488 static int
1489 setnetmask(argv)
1490     char **argv;
1491 {
1492     u_int32_t mask;
1493     int n;
1494     char *p;
1495
1496     /*
1497      * Unfortunately, if we use inet_addr, we can't tell whether
1498      * a result of all 1s is an error or a valid 255.255.255.255.
1499      */
1500     p = *argv;
1501     n = parse_dotted_ip(p, &mask);
1502
1503     mask = htonl(mask);
1504
1505     if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) {
1506         option_error("invalid netmask value '%s'", *argv);
1507         return 0;
1508     }
1509
1510     netmask = mask;
1511     return (1);
1512 }
1513
1514 int
1515 parse_dotted_ip(p, vp)
1516     char *p;
1517     u_int32_t *vp;
1518 {
1519     int n;
1520     u_int32_t v, b;
1521     char *endp, *p0 = p;
1522
1523     v = 0;
1524     for (n = 3;; --n) {
1525         b = strtoul(p, &endp, 0);
1526         if (endp == p)
1527             return 0;
1528         if (b > 255) {
1529             if (n < 3)
1530                 return 0;
1531             /* accept e.g. 0xffffff00 */
1532             *vp = b;
1533             return endp - p0;
1534         }
1535         v |= b << (n * 8);
1536         p = endp;
1537         if (n == 0)
1538             break;
1539         if (*p != '.')
1540             return 0;
1541         ++p;
1542     }
1543     *vp = v;
1544     return p - p0;
1545 }
1546
1547 static int
1548 setxonxoff(argv)
1549     char **argv;
1550 {
1551     lcp_wantoptions[0].asyncmap |= 0x000A0000;  /* escape ^S and ^Q */
1552     lcp_wantoptions[0].neg_asyncmap = 1;
1553
1554     crtscts = -2;
1555     return (1);
1556 }
1557
1558 static int
1559 setlogfile(argv)
1560     char **argv;
1561 {
1562     int fd, err;
1563
1564     if (!privileged_option)
1565         seteuid(getuid());
1566     fd = open(*argv, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0644);
1567     if (fd < 0 && errno == EEXIST)
1568         fd = open(*argv, O_WRONLY | O_APPEND);
1569     err = errno;
1570     if (!privileged_option)
1571         seteuid(0);
1572     if (fd < 0) {
1573         errno = err;
1574         option_error("Can't open log file %s: %m", *argv);
1575         return 0;
1576     }
1577     if (log_to_file && log_to_fd >= 0)
1578         close(log_to_fd);
1579     log_to_fd = fd;
1580     log_to_file = 1;
1581     return 1;
1582 }
1583
1584 #ifdef PLUGIN
1585 static int
1586 loadplugin(argv)
1587     char **argv;
1588 {
1589     char *arg = *argv;
1590     void *handle;
1591     const char *err;
1592     void (*init) __P((void));
1593
1594     handle = dlopen(arg, RTLD_GLOBAL | RTLD_NOW);
1595     if (handle == 0) {
1596         err = dlerror();
1597         if (err != 0)
1598             option_error("%s", err);
1599         option_error("Couldn't load plugin %s", arg);
1600         return 0;
1601     }
1602     init = dlsym(handle, "plugin_init");
1603     if (init == 0) {
1604         option_error("%s has no initialization entry point", arg);
1605         dlclose(handle);
1606         return 0;
1607     }
1608     info("Plugin %s loaded.", arg);
1609     (*init)();
1610     return 1;
1611 }
1612 #endif /* PLUGIN */