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