2 * options.c - handles option processing for PPP.
4 * Copyright (c) 2000-2001 by Sun Microsystems, Inc.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation is hereby granted, provided that the above copyright
9 * notice appears in all copies.
11 * SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF
12 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
13 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
14 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
15 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
16 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES
18 * Copyright (c) 1989 Carnegie Mellon University.
19 * All rights reserved.
21 * Redistribution and use in source and binary forms are permitted
22 * provided that the above copyright notice and this paragraph are
23 * duplicated in all such forms and that any documentation,
24 * advertising materials, and other materials related to such
25 * distribution and use acknowledge that the software was developed
26 * by Carnegie Mellon University. The name of the
27 * University may not be used to endorse or promote products derived
28 * from this software without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
30 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
31 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
34 #define RCSID "$Id: options.c,v 1.74 2000/04/15 01:27:13 masputra Exp $"
46 #include <sys/types.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
55 #include <pcap-int.h> /* XXX: To get struct pcap */
56 #endif /* PPP_FILTER */
59 #include "pathnames.h"
60 #include "patchlevel.h"
65 #if defined(ultrix) || defined(NeXT)
66 char *strdup
__P((char *));
69 #if !defined(lint) && !defined(_lint)
70 static const char rcsid
[] = RCSID
;
74 * Option variables and default values.
77 int dflag
= 0; /* Tell libpcap we want debugging */
78 #endif /* PPP_FILTER */
79 int debug
= 0; /* Debug flag */
80 int kdebugflag
= 0; /* Tell kernel to print debug messages */
81 int default_device
= 1; /* Using /dev/tty or equivalent */
82 char devnam
[MAXPATHLEN
]; /* Device name */
83 int crtscts
= 0; /* Use hardware flow control */
84 bool modem
= 1; /* Use modem control lines */
85 int inspeed
= 0; /* Input/Output speed requested */
86 u_int32_t netmask
= 0; /* IP netmask to set on interface */
87 bool lockflag
= 0; /* Create lock file to lock the serial dev */
88 bool nodetach
= 0; /* Don't detach from controlling tty */
89 bool updetach
= 0; /* Detach once link is up */
90 char *initializer
= NULL
; /* Script to initialize physical link */
91 char *connect_script
= NULL
; /* Script to establish physical link */
92 char *disconnect_script
= NULL
; /* Script to disestablish physical link */
93 char *welcomer
= NULL
; /* Script to run after phys link estab. */
94 char *ptycommand
= NULL
; /* Command to run on other side of pty */
95 int maxconnect
= 0; /* Maximum connect time */
96 char user
[MAXNAMELEN
]; /* Username for PAP */
97 char passwd
[MAXSECRETLEN
]; /* Password for PAP */
98 bool persist
= 0; /* Reopen link after it goes down */
99 char our_name
[MAXNAMELEN
]; /* Our name for authentication purposes */
100 bool demand
= 0; /* do dial-on-demand */
101 char *ipparam
= NULL
; /* Extra parameter for ip up/down scripts */
102 int idle_time_limit
= 0; /* Disconnect if idle for this many seconds */
103 int holdoff
= 30; /* # seconds to pause before reconnecting */
104 bool holdoff_specified
; /* true if a holdoff value has been given */
105 bool notty
= 0; /* Stdin/out is not a tty */
106 char *pty_socket
= NULL
; /* Socket to connect to pty */
107 char *record_file
= NULL
; /* File to record chars sent/received */
109 bool sync_serial
= 0; /* Device is synchronous serial device */
110 int log_to_fd
= 1; /* send log messages to this fd too */
111 int maxfail
= 10; /* max # of unsuccessful connection attempts */
112 char linkname
[MAXPATHLEN
]; /* logical name for link */
113 bool tune_kernel
; /* may alter kernel settings */
114 int connect_delay
= 1000; /* wait this many ms after connect script */
115 int max_data_rate
; /* max bytes/sec through charshunt */
116 int req_unit
= -1; /* requested interface unit */
117 bool multilink
= 0; /* Enable multilink operation */
118 char *bundle_name
= NULL
; /* bundle name for multilink */
119 bool direct_tty
= 0; /* use standard input directly; not a tty */
121 /* Maximum depth of include files; prevents looping. */
122 #define MAXFILENESTING 10
124 struct option_info initializer_info
;
125 struct option_info connect_script_info
;
126 struct option_info disconnect_script_info
;
127 struct option_info welcomer_info
;
128 struct option_info devnam_info
;
129 struct option_info ptycommand_info
;
130 struct option_info ipsrc_info
;
131 struct option_info ipdst_info
;
132 struct option_info speed_info
;
135 struct bpf_program pass_filter
;/* Filter program for packets to pass */
136 struct bpf_program active_filter
; /* Filter program for link-active pkts */
137 pcap_t pc
; /* Fake struct pcap so we can compile expr */
138 #endif /* PPP_FILTER */
140 char *current_option
; /* the name of the option being parsed */
141 bool privileged_option
; /* set iff the current option came from root */
142 char *option_source
= NULL
; /* string saying where the option came from */
143 int option_line
= 0; /* line number in file */
144 bool log_to_file
; /* log_to_fd is a file opened by us */
145 bool log_to_specific_fd
; /* log_to_fd was specified by user option */
150 static int setdevname
__P((char *));
151 static int setipaddr
__P((char *));
152 static int setspeed
__P((char *));
153 static int noopt
__P((char **, option_t
*));
154 static int setdomain
__P((char **, option_t
*));
155 static int setnetmask
__P((char **, option_t
*));
156 static int setxonxoff
__P((char **, option_t
*));
157 static int readfile
__P((char **, option_t
*));
158 static int callfile
__P((char **, option_t
*));
159 static int showversion
__P((char **, option_t
*));
160 static int showhelp
__P((char **, option_t
*));
161 static int showalloptions
__P((char **, option_t
*));
162 static void usage
__P((void));
163 static int setlogfile
__P((char **, option_t
*));
165 static int loadplugin
__P((char **, option_t
*));
168 static int setpassfilter
__P((char **, option_t
*));
169 static int setactivefilter
__P((char **, option_t
*));
170 #endif /* PPP_FILTER */
171 static option_t
*find_option
__P((char *name
));
172 static int process_option
__P((option_t
*opt
, char **argv
, int sline
));
173 static int n_arguments
__P((option_t
*opt
));
174 static int number_option
__P((char *str
, u_int32_t
*valp
, int base
));
175 static u_int32_t opt_hash
__P((const void *key
));
176 static int opt_compare
__P((const void *p1
, const void *p2
));
178 typedef struct _opt_t
{
182 typedef struct _hashentry_t
{
183 struct _hashentry_t
*next
;
188 * A prime number describing the size of hash table.
190 #define OPTHASH_TBLSIZE 101
193 * Chained hash table containing pointers to available options.
195 static hashentry_t
*hash_tbl
[OPTHASH_TBLSIZE
] = { NULL
};
198 * Total number of entries in the hash table.
205 option_t general_options
[] = {
206 { "debug", o_int
, &debug
,
207 "Increase debugging level", OPT_INC
|OPT_NOARG
|1 },
208 { "-d", o_int
, &debug
,
209 "Increase debugging level", OPT_INC
|OPT_NOARG
|1 },
210 { "kdebug", o_int
, &kdebugflag
,
211 "Set kernel driver debug level" },
212 { "nodetach", o_bool
, &nodetach
,
213 "Don't detach from controlling tty", 1 },
214 { "-detach", o_bool
, &nodetach
,
215 "Don't detach from controlling tty", 1 },
216 { "updetach", o_bool
, &updetach
,
217 "Detach from controlling tty once link is up", 1 },
218 { "holdoff", o_int
, &holdoff
,
219 "Set time in seconds before retrying connection" },
220 { "idle", o_int
, &idle_time_limit
,
221 "Set time in seconds before disconnecting idle link" },
222 { "lock", o_bool
, &lockflag
,
223 "Lock serial device with UUCP-style lock file", 1 },
224 { "-all", o_special_noarg
, (void *)noopt
,
225 "Don't request/allow any LCP or IPCP options (useless)" },
226 { "init", o_string
, &initializer
,
227 "A program to initialize the device",
228 OPT_A2INFO
| OPT_PRIVFIX
, &initializer_info
},
229 { "connect", o_string
, &connect_script
,
230 "A program to set up a connection",
231 OPT_A2INFO
| OPT_PRIVFIX
, &connect_script_info
},
232 { "disconnect", o_string
, &disconnect_script
,
233 "Program to disconnect serial device",
234 OPT_A2INFO
| OPT_PRIVFIX
, &disconnect_script_info
},
235 { "welcome", o_string
, &welcomer
,
236 "Script to welcome client",
237 OPT_A2INFO
| OPT_PRIVFIX
, &welcomer_info
},
238 { "pty", o_string
, &ptycommand
,
239 "Script to run on pseudo-tty master side",
240 OPT_A2INFO
| OPT_PRIVFIX
| OPT_DEVNAM
, &ptycommand_info
},
241 { "notty", o_bool
, ¬ty
,
242 "Input/output is not a tty", OPT_DEVNAM
| 1 },
243 { "directtty", o_bool
, &direct_tty
,
244 "Use standard input as tty without checking", OPT_DEVNAM
| 1 },
245 { "socket", o_string
, &pty_socket
,
246 "Send and receive over socket, arg is host:port", OPT_DEVNAM
},
247 { "record", o_string
, &record_file
,
248 "Record characters sent/received to file" },
249 { "maxconnect", o_int
, &maxconnect
,
250 "Set connection time limit", OPT_LLIMIT
|OPT_NOINCR
|OPT_ZEROINF
},
251 { "crtscts", o_int
, &crtscts
,
252 "Set hardware (RTS/CTS) flow control", OPT_NOARG
|OPT_VAL(1) },
253 { "nocrtscts", o_int
, &crtscts
,
254 "Disable hardware flow control", OPT_NOARG
|OPT_VAL(-1) },
255 { "-crtscts", o_int
, &crtscts
,
256 "Disable hardware flow control", OPT_NOARG
|OPT_VAL(-1) },
257 { "cdtrcts", o_int
, &crtscts
,
258 "Set alternate hardware (DTR/CTS) flow control", OPT_NOARG
|OPT_VAL(2) },
259 { "nocdtrcts", o_int
, &crtscts
,
260 "Disable hardware flow control", OPT_NOARG
|OPT_VAL(-1) },
261 { "xonxoff", o_special_noarg
, (void *)setxonxoff
,
262 "Set software (XON/XOFF) flow control" },
263 { "domain", o_special
, (void *)setdomain
,
264 "Add given domain name to hostname" },
265 { "netmask", o_special
, (void *)setnetmask
,
267 { "modem", o_bool
, &modem
,
268 "Use modem control lines", 1 },
269 { "local", o_bool
, &modem
,
270 "Don't use modem control lines" },
271 { "file", o_special
, (void *)readfile
,
272 "Take options from a file", OPT_PREPASS
},
273 { "call", o_special
, (void *)callfile
,
274 "Take options from a privileged file", OPT_PREPASS
},
275 { "persist", o_bool
, &persist
,
276 "Keep on reopening connection after close", 1 },
277 { "nopersist", o_bool
, &persist
,
278 "Turn off persist option" },
279 { "demand", o_bool
, &demand
,
280 "Dial on demand", OPT_INITONLY
| 1, &persist
},
281 { "--version", o_special_noarg
, (void *)showversion
,
282 "Show version number" },
283 { "--help", o_special_noarg
, (void *)showhelp
,
284 "Show brief listing of options" },
285 { "-h", o_special_noarg
, (void *)showhelp
,
286 "Show brief listing of options" },
287 { "options", o_special_noarg
, (void *)showalloptions
,
288 "Show full listing of options" },
289 { "sync", o_bool
, &sync_serial
,
290 "Use synchronous HDLC serial encoding", 1 },
291 { "logfd", o_int
, &log_to_fd
,
292 "Send log messages to this file descriptor",
293 0, &log_to_specific_fd
},
294 { "logfile", o_special
, (void *)setlogfile
,
295 "Append log messages to this file" },
296 { "nolog", o_int
, &log_to_fd
,
297 "Don't send log messages to any file",
298 OPT_NOARG
| OPT_VAL(-1) },
299 { "nologfd", o_int
, &log_to_fd
,
300 "Don't send log messages to any file descriptor",
301 OPT_NOARG
| OPT_VAL(-1) },
302 { "linkname", o_string
, linkname
,
303 "Set logical name for link",
304 OPT_PRIV
|OPT_STATIC
, NULL
, MAXPATHLEN
},
305 { "maxfail", o_int
, &maxfail
,
306 "Number of unsuccessful connection attempts to allow" },
307 { "ktune", o_bool
, &tune_kernel
,
308 "Alter kernel settings as necessary", 1 },
309 { "noktune", o_bool
, &tune_kernel
,
310 "Don't alter kernel settings", 0 },
311 { "connect-delay", o_int
, &connect_delay
,
312 "Maximum wait time (msec) after connect script finishes" },
313 { "datarate", o_int
, &max_data_rate
,
314 "Max data rate in bytes/sec for pty, notty, or record" },
315 { "unit", o_int
, &req_unit
,
316 "PPP interface unit number to use if possible", OPT_LLIMIT
, 0, 0 },
317 #ifdef HAVE_MULTILINK
318 { "multilink", o_bool
, &multilink
,
319 "Enable multilink operation", 1 },
320 { "nomultilink", o_bool
, &multilink
,
321 "Disable multilink operation", 0 },
322 { "mp", o_bool
, &multilink
,
323 "Enable multilink operation", 1 },
324 { "nomp", o_bool
, &multilink
,
325 "Disable multilink operation", 0 },
326 { "bundle", o_string
, &bundle_name
,
327 "Bundle name for multilink" },
328 #endif /* HAVE_MULTILINK */
330 { "plugin", o_special
, (void *)loadplugin
,
331 "Load a plug-in module into pppd", OPT_PRIV
},
334 { "pdebug", o_int
, &dflag
,
335 "libpcap debugging" },
336 { "pass-filter", o_special
, setpassfilter
,
337 "set filter for packets to pass" },
338 { "active-filter", o_special
, setactivefilter
,
339 "set filter for active pkts" },
340 #endif /* PPP_FILTER */
345 * This string gets printed out when "options" is given on the command
346 * line. Following this string, all of the available options and
347 * their descriptions are printed out as well. Certain options which
348 * are not available as part of the option_t structure are placed in
349 * the "dummy" option structure.
351 static const char pre_allopt_string
[] = "\
352 pppd version %s.%d%s\n\
353 Usage: %s [ options ], where options are:\n\n\
356 /* Do not call add_options() on this structure */
357 static option_t dummy_options
[] = {
358 { "<device>", o_special_noarg
, NULL
,
359 "Communicate over the named device" },
360 { "<speed>", o_special_noarg
, NULL
,
361 "Set the baud rate to <speed>" },
362 { "[<loc>]:[<rem>]", o_special_noarg
, NULL
,
363 "Set the local and/or remote interface IP addresses" },
367 static const char post_allopt_string
[] = "\
370 \t<n>\tinteger type argument\n\
371 \t<s>\tstring type argument\n\
372 \t<r>\tspecial type argument\n\
373 \t(!)\tprivileged option available only when pppd is executed by root\n\
374 \t\tor when found in the privileged option files (/etc/ppp/options,\n\
375 \t\t/etc/ppp/options.ttyname, /etc/ppp/peers/name, or following\n\
376 \t\t\"--\" in /etc/ppp/pap-secrets or /etc/ppp/chap-secrets).\n\
377 \t(#)\tdisabled option\n\
379 Please see the pppd man page for details.\n";
382 * parse_args - parse a string of arguments from the command line. If prepass
383 * is true, we are scanning for the device name and only processing a few
384 * options, so error messages are suppressed. Returns 1 upon successful
385 * processing of options, and 0 otherwise.
388 parse_args(argc
, argv
)
396 privileged_option
= privileged
;
397 option_source
= "command line";
404 * First check to see if it's a known option name. If so, parse the
405 * argument(s) and set the option.
407 opt
= find_option(arg
);
409 int n
= n_arguments(opt
);
411 option_error("too few parameters for option '%s'", arg
);
414 current_option
= arg
;
415 if (!process_option(opt
, argv
, 0))
423 * Maybe a tty name, speed or IP address ?
425 if (((ret
= setdevname(arg
)) == 0) &&
426 ((ret
= setspeed(arg
)) == 0) &&
427 ((ret
= setipaddr(arg
)) == 0) && !prepass
) {
428 option_error("unrecognized option '%s'", arg
);
432 if (ret
< 0) /* error */
439 * options_from_file - read a string of options from a file, and
440 * interpret them. Returns 1 upon successful processing of options,
446 (char *filename
, bool must_exist
, bool check_prot
, bool priv
)
448 (filename
, must_exist
, check_prot
, priv
)
456 int i
, newline
, ret
, err
;
462 char args
[MAXARGS
][MAXWORDLEN
];
463 char cmd
[MAXWORDLEN
];
464 static bool firsterr
= 1;
465 static int nestlevel
= 0;
467 if (nestlevel
>= MAXFILENESTING
) {
468 option_error("file nesting too deep");
472 (void) seteuid(getuid());
474 f
= fopen(filename
, "r");
479 if (!must_exist
&& err
== ENOENT
)
482 option_error("Can't open options file %s: %m", filename
);
487 oldpriv
= privileged_option
;
488 privileged_option
= priv
;
489 oldsource
= option_source
;
491 * strdup() is used here because the pointer might refer to the
492 * caller's automatic (stack) storage, and the option_info array
493 * records the source file name.
495 option_source
= strdup(filename
);
496 oldline
= option_line
;
498 if (option_source
== NULL
)
499 option_source
= "file";
501 while (getword(f
, cmd
, &newline
, filename
)) {
504 * First see if it's a command.
506 opt
= find_option(cmd
);
508 int n
= n_arguments(opt
);
509 for (i
= 0; i
< n
; ++i
) {
510 if (!getword(f
, args
[i
], &newline
, filename
)) {
511 option_error("too few parameters for option '%s'", cmd
);
516 current_option
= cmd
;
517 if ((opt
->flags
& OPT_DEVEQUIV
) && devnam_fixed
) {
518 option_error("the '%s' option may not be used here", cmd
);
521 if (!process_option(opt
, argv
, sline
))
527 * Maybe a tty name, speed or IP address ?
529 if (((i
= setdevname(cmd
)) == 0) &&
530 ((i
= setspeed(cmd
)) == 0) &&
531 ((i
= setipaddr(cmd
)) == 0)) {
532 option_error("unrecognized option '%s'", cmd
);
535 if (i
< 0) /* error */
542 /* We assume here that we abort all processing on the first error. */
545 else if (!prepass
&& !ret
)
546 option_error("error in included file");
548 * Cannot free option_source because it might be referenced in one
549 * or more option_info structures now.
551 privileged_option
= oldpriv
;
552 option_source
= oldsource
;
553 option_line
= oldline
;
559 * options_from_user - see if the user has a ~/.ppprc file, and if so,
560 * interpret options from it. Returns 1 upon successful processing of
561 * options, and 0 otherwise.
566 char *user
, *path
, *file
;
571 pw
= getpwuid(getuid());
572 if (pw
== NULL
|| (user
= pw
->pw_dir
) == NULL
|| user
[0] == '\0')
574 file
= _PATH_USEROPT
;
575 pl
= strlen(user
) + strlen(file
) + 2;
578 novm("init file name");
579 (void) slprintf(path
, pl
, "%s/%s", user
, file
);
580 ret
= options_from_file(path
, 0, 1, privileged
);
586 * options_for_tty - see if an options file exists for the serial device, and
587 * if so, interpret options from it. Returns 1 upon successful processing of
588 * options, and 0 otherwise.
593 char *dev
, *path
, *p
;
598 if (strncmp(dev
, "/dev/", 5) == 0)
600 if (dev
[0] == '\0' || strcmp(dev
, "tty") == 0)
601 return (1); /* don't look for /etc/ppp/options.tty */
602 pl
= strlen(_PATH_TTYOPT
) + strlen(dev
) + 1;
605 novm("tty init file name");
606 (void) slprintf(path
, pl
, "%s%s", _PATH_TTYOPT
, dev
);
607 /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
608 for (p
= path
+ strlen(_PATH_TTYOPT
); *p
!= '\0'; ++p
)
611 ret
= options_from_file(path
, 0, 0, 1);
617 * options_from_list - process a string of options in a wordlist. Returns 1
618 * upon successful processing of options, and 0 otherwise.
623 (struct wordlist
*w
, bool priv
)
634 privileged_option
= priv
;
636 /* Caller is expected to set option_source and option_line. */
640 * First see if it's a command.
642 opt
= find_option(w
->word
);
644 int n
= n_arguments(opt
);
645 struct wordlist
*w0
= w
;
646 for (i
= 0; i
< n
; ++i
) {
649 option_error("too few parameters for option '%s'",
655 current_option
= w0
->word
;
656 if (!process_option(opt
, argv
, option_line
))
662 * Options from the {p,ch}ap-secrets files can't change the device
663 * name nor the speed. Therefore, calls to setdevname() and
664 * setspeed() were removed.
666 if ((i
= setipaddr(w
->word
)) == 0) {
667 option_error("unrecognized option '%s'", w
->word
);
670 if (i
< 0) /* error */
680 * find_option - scan the option lists for the various protocols looking for an
681 * entry with the given name. Returns a pointer to the matching option_t
682 * structure upon successful processing of options, and NULL otherwise.
690 bucket
= hash_tbl
[opt_hash(name
)];
691 for (; bucket
!= NULL
; bucket
= bucket
->next
) {
692 if (bucket
->opt
.p
->name
!= NULL
) {
693 if ((strcmp(bucket
->opt
.p
->name
, name
) == 0) &&
694 !(bucket
->opt
.p
->flags
& OPT_DISABLE
)) {
695 return (bucket
->opt
.p
);
703 * process_option - process one new-style option (something other than a
704 * port name, bit rate, or IP address). Returns 1 upon successful
705 * processing of options, and 0 otherwise.
708 process_option(opt
, argv
, sline
)
716 int (*parser
) __P((char **, option_t
*));
718 if ((opt
->flags
& OPT_PREPASS
) == 0 && prepass
)
720 if ((opt
->flags
& OPT_INITONLY
) && phase
!= PHASE_INITIALIZE
) {
721 option_error("it's too late to use the '%s' option", opt
->name
);
724 if ((opt
->flags
& OPT_PRIV
) && !privileged_option
) {
725 option_error("using the '%s' option requires root privilege",
729 if ((opt
->flags
& OPT_ENABLE
) && !privileged_option
&&
730 *(bool *)(opt
->addr2
) == 0) {
731 option_error("'%s' option is disabled", opt
->name
);
734 if ((opt
->flags
& OPT_PRIVFIX
) && !privileged_option
) {
735 struct option_info
*ip
= (struct option_info
*) opt
->addr2
;
736 if ((ip
!= NULL
) && ip
->priv
) {
737 option_error("'%s' option cannot be overridden", opt
->name
);
744 v
= opt
->flags
& OPT_VALUE
;
745 *(bool *)(opt
->addr
) = (v
!= 0);
746 if ((opt
->addr2
!= NULL
) && (opt
->flags
& OPT_A2COPY
))
747 *(bool *)(opt
->addr2
) = (v
!= 0);
752 if ((opt
->flags
& OPT_NOARG
) == 0) {
753 if (!int_option(*argv
, &iv
))
755 if ((((opt
->flags
& OPT_LLIMIT
) && (iv
< opt
->lower_limit
)) ||
756 ((opt
->flags
& OPT_ULIMIT
) && (iv
> opt
->upper_limit
))) &&
757 !((opt
->flags
& OPT_ZEROOK
) && (iv
== 0))) {
758 char *zok
= (opt
->flags
& OPT_ZEROOK
) ? " zero or" : "";
759 switch (opt
->flags
& OPT_LIMITS
) {
761 option_error("%s value must be%s >= %d",
762 opt
->name
, zok
, opt
->lower_limit
);
765 option_error("%s value must be%s <= %d",
766 opt
->name
, zok
, opt
->upper_limit
);
769 option_error("%s value must be%s between %d and %d",
770 opt
->name
, zok
, opt
->lower_limit
, opt
->upper_limit
);
776 a
= opt
->flags
& OPT_VALUE
;
778 a
-= 256; /* sign extend */
780 if (opt
->flags
& OPT_INC
)
781 iv
+= *(int *)(opt
->addr
);
782 if ((opt
->flags
& OPT_NOINCR
) && !privileged_option
) {
783 int oldv
= *(int *)(opt
->addr
);
785 if ((opt
->flags
& OPT_ZEROINF
) && (iv
== 0)) {
787 option_error("%s value cannot be set to infinity; limited to %d",
791 } else if (iv
> oldv
) {
792 option_error("%s value cannot be increased beyond %d",
797 *(int *)(opt
->addr
) = iv
;
798 if ((opt
->addr2
!= NULL
) && (opt
->flags
& OPT_A2COPY
))
799 *(int *)(opt
->addr2
) = iv
;
803 if (opt
->flags
& OPT_NOARG
) {
804 v
= opt
->flags
& OPT_VALUE
;
805 } else if (!number_option(*argv
, &v
, 16))
807 if (opt
->flags
& OPT_OR
)
808 v
|= *(u_int32_t
*)(opt
->addr
);
809 *(u_int32_t
*)(opt
->addr
) = v
;
810 if ((opt
->addr2
!= NULL
) && (opt
->flags
& OPT_A2COPY
))
811 *(u_int32_t
*)(opt
->addr2
) = v
;
815 if (opt
->flags
& OPT_STATIC
) {
816 (void) strlcpy((char *)(opt
->addr
), *argv
, opt
->upper_limit
);
817 if ((opt
->addr2
!= NULL
) && (opt
->flags
& OPT_A2COPY
)) {
818 (void) strlcpy((char *)(opt
->addr2
), *argv
, opt
->upper_limit
);
823 novm("option argument");
824 *(char **)(opt
->addr
) = sv
;
825 if (opt
->addr2
!= NULL
&& (opt
->flags
& OPT_A2COPY
))
826 *(char **)(opt
->addr2
) = sv
;
830 case o_special_noarg
:
832 parser
= (int (*) __P((char **, option_t
*))) opt
->addr
;
833 if (!(*parser
)(argv
, opt
))
838 if (opt
->addr2
!= NULL
) {
839 if (opt
->flags
& OPT_A2INFO
) {
840 struct option_info
*ip
= (struct option_info
*) opt
->addr2
;
841 ip
->priv
= privileged_option
;
842 ip
->source
= option_source
;
844 } else if ((opt
->flags
& (OPT_A2COPY
|OPT_ENABLE
)) == 0)
845 *(bool *)(opt
->addr2
) = 1;
852 * n_arguments - tell how many arguments an option takes. Returns 1 upon
853 * successful processing of options, and 0 otherwise.
859 return ((opt
->type
== o_bool
|| opt
->type
== o_special_noarg
||
860 (opt
->flags
& OPT_NOARG
)) ? 0 : 1);
864 * opt_hash - a hash function that works quite well for strings. Returns
865 * the hash key of the supplied string.
871 register const char *ptr
;
872 register u_int32_t val
;
876 while (*ptr
!= '\0') {
878 val
= (val
<< 4) + (*ptr
);
879 tmp
= val
& 0xf0000000;
886 return (val
% OPTHASH_TBLSIZE
);
890 * add_options - add a list of options to the chained hash table.
891 * Also detect duplicate options, and if found, disable the older
892 * definition and log it as an error.
898 register option_t
*sopt
;
899 register hashentry_t
*bucket
;
900 register u_int32_t loc
;
903 /* fill hash-table */
904 for (sopt
= opt
; sopt
->name
!= NULL
; ++sopt
, hash_tblcnt
++) {
906 /* first, allocate a hash entry */
907 he
= (hashentry_t
*)malloc(sizeof(*he
));
909 novm("option hash table entry");
915 * fill the chained hash table and take care of any collisions or
918 loc
= opt_hash(sopt
->name
);
919 bucket
= hash_tbl
[loc
];
920 if (bucket
!= NULL
) {
922 if (!(bucket
->opt
.p
->flags
& OPT_DISABLE
) &&
923 strcmp(sopt
->name
, bucket
->opt
.p
->name
) == 0) {
924 info("option '%s' redefined; old definition disabled",
926 bucket
->opt
.p
->flags
|= OPT_DISABLE
;
928 if (bucket
->next
== NULL
)
930 bucket
= bucket
->next
;
940 * remove_option - disable an option. Returns the option_t structure
941 * of the disabled option, or NULL if the option name is invalid or if
942 * the option has already been disabled.
950 if ((opt
= find_option(name
)) != NULL
) {
951 opt
->flags
|= OPT_DISABLE
;
957 * opt_compare - a compare function supplied to the quicksort routine.
958 * Returns an integer less than, equal to, or greater than zero to indicate
959 * if the first argument is considered less than, equal to, or greater
960 * than the second argument.
967 opt_t
*o1
= (opt_t
*)p1
;
968 opt_t
*o2
= (opt_t
*)p2
;
970 return (strcmp(o1
->p
->name
, o2
->p
->name
));
975 showalloptions(argv
, topt
)
979 #define MAXOPTSTRLEN 257
980 #define PRINTOPTIONS() { \
981 (void) slprintf(opt_str, sizeof(opt_str), "%s", opt->name); \
982 if ((opt->type == o_int || opt->type == o_uint32) && \
983 !(opt->flags & OPT_NOARG)) { \
984 (void) strlcat(opt_str, " <n>", sizeof(opt_str)); \
985 } else if (opt->type == o_string) { \
986 (void) strlcat(opt_str, " <s>", sizeof(opt_str)); \
987 } else if (opt->type == o_special) { \
988 (void) strlcat(opt_str, " <r>", sizeof(opt_str)); \
990 if (opt->flags & OPT_PRIV) { \
991 (void) strlcat(opt_str, " (!)", sizeof(opt_str)); \
992 } else if (opt->flags & OPT_DISABLE) { \
993 (void) strlcat(opt_str, " (#)", sizeof(opt_str)); \
995 (void) printf("%-26s%s\n", opt_str, opt->description); \
998 char opt_str
[MAXOPTSTRLEN
];
1000 hashentry_t
*bucket
;
1004 if (phase
!= PHASE_INITIALIZE
) {
1007 (void) printf(pre_allopt_string
, VERSION
, PATCHLEVEL
, IMPLEMENTATION
,
1009 for (opt
= dummy_options
; opt
->name
!= NULL
; ++opt
) {
1013 sopt
= malloc(sizeof(*sopt
) * hash_tblcnt
);
1015 novm("sorted option table");
1019 for (i
= 0; i
< OPTHASH_TBLSIZE
; i
++) {
1020 for (bucket
= hash_tbl
[i
]; bucket
!= NULL
; bucket
= bucket
->next
) {
1021 if (sofar
>= hash_tblcnt
) {
1022 fatal("options hash table corrupted; size mismatch");
1024 sopt
[sofar
++].p
= bucket
->opt
.p
;
1028 qsort((void *)sopt
, sofar
, sizeof(sopt
[0]), opt_compare
);
1029 for (i
= 0; i
< sofar
; i
++) {
1034 (void) printf(post_allopt_string
);
1043 * usage - print out a message telling how to use the program.
1044 * This string gets printed out when either "--help" or an invalid option
1050 static const char usage_string
[] = "\
1051 pppd version %s.%d%s\n\
1052 Usage: %s [ options ], where options are:\n\
1053 \t<device>\tCommunicate over the named device\n\
1054 \t<speed>\t\tSet the baud rate to <speed>\n\
1055 \t<loc>:<rem>\tSet the local and/or remote interface IP\n\
1056 \t\t\taddresses. Either one may be omitted.\n\
1057 \tnoauth\t\tDon't require authentication from peer\n\
1058 \tconnect <p>\tInvoke shell command <p> to set up the serial line\n\
1059 \tdefaultroute\tAdd default route through interface\n\
1060 Use \"%s options\" or \"man pppd\" for more options.\n\
1063 if (phase
== PHASE_INITIALIZE
)
1064 (void) fprintf(stderr
, usage_string
, VERSION
, PATCHLEVEL
,
1065 IMPLEMENTATION
, progname
, progname
);
1069 * showhelp - print out usage message and exit program upon success, or
1070 * return 0 otherwise.
1078 if (phase
== PHASE_INITIALIZE
) {
1086 * showversion - print out the version number and exit program upon success,
1087 * or return 0 otherwise.
1091 showversion(argv
, opt
)
1095 if (phase
== PHASE_INITIALIZE
) {
1096 (void) fprintf(stderr
, "pppd version %s.%d%s\n", VERSION
, PATCHLEVEL
,
1104 * option_error - print a message about an error in an option. The message is
1105 * logged, and also sent to stderr if phase == PHASE_INITIALIZE.
1108 option_error
__V((char *fmt
, ...))
1114 #if defined(__STDC__)
1115 va_start(args
, fmt
);
1119 fmt
= va_arg(args
, char *);
1126 if (option_source
== NULL
) {
1128 } else if (option_line
<= 0) {
1129 (void) strlcpy(buf
, option_source
, sizeof (buf
));
1132 i
= slprintf(buf
, sizeof(buf
), "%s:%d", option_source
, option_line
);
1135 (void) strlcat(buf
, ": ", sizeof (buf
));
1139 (void) vslprintf(buf
+ i
, sizeof (buf
) - i
, fmt
, args
);
1141 if ((phase
== PHASE_INITIALIZE
) && !detached
)
1142 (void) fprintf(stderr
, "%s: %s\n", progname
, buf
);
1143 syslog(LOG_ERR
, "%s", buf
);
1147 * getword - read a word from a file. Words are delimited by white-space or by
1148 * quotes (" or '). Quotes, white-space and \ may be escaped with \.
1149 * \<newline> is ignored. Returns 1 upon successful processing of options,
1153 getword(f
, word
, newlinep
, filename
)
1160 int quoted
, comment
;
1161 int value
, digit
, got
, n
;
1163 #define isoctal(c) ((c) >= '0' && (c) < '8')
1171 * First skip white-space and comments.
1179 * A newline means the end of a comment; backslash-newline
1180 * is ignored. Note that we cannot have escape && comment.
1193 * Ignore characters other than newline in a comment.
1199 * If this character is escaped, we have a word start.
1205 * If this is the escape character, look at the next character.
1213 * If this is the start of a comment, ignore the rest of the line.
1221 * A non-whitespace character is the start of a word.
1228 * Save the delimiter for quoted strings.
1230 if (!escape
&& (c
== '"' || c
== '\'')) {
1237 * Process characters until the end of the word.
1242 * This character is escaped: backslash-newline is ignored,
1243 * various other characters indicate particular values
1244 * as for C backslash-escapes.
1279 * \ddd octal sequence
1282 for (n
= 0; n
< 3 && isoctal(c
); ++n
) {
1283 value
= (value
<< 3) + (c
& 07);
1292 * \x<hex_string> sequence
1296 for (n
= 0; n
< 2 && isxdigit(c
); ++n
) {
1297 digit
= (islower(c
) ? toupper(c
) : c
) - '0';
1298 if (digit
> 10 || digit
< 0) /* allow non-ASCII */
1299 digit
+= '0' + 10 - 'A';
1300 value
= (value
<< 4) + digit
;
1308 * Otherwise the character stands for itself.
1315 * Store the resulting character for the escape sequence.
1317 if (len
< MAXWORDLEN
) {
1329 * Not escaped: see if we've reached the end of the word.
1335 if (isspace(c
) || c
== '#') {
1336 (void) ungetc (c
, f
);
1342 * Backslash starts an escape sequence.
1351 * An ordinary character: store it in the word and get another.
1353 if (len
< MAXWORDLEN
) {
1362 * End of the word: check for errors.
1368 option_error("Error reading %s: %m", filename
);
1372 * If len is zero, then we didn't find a word before the
1380 * Warn if the word was too long, and append a terminating null.
1382 if (len
>= MAXWORDLEN
) {
1383 option_error("warning: word in file %s too long (%.20s...)",
1385 len
= MAXWORDLEN
- 1;
1396 * number_option - parse an unsigned numeric parameter for an option.
1397 * Returns 1 upon successful processing of options, and 0 otherwise.
1400 number_option(str
, valp
, base
)
1407 *valp
= strtoul(str
, &ptr
, base
);
1408 if (ptr
== str
|| *ptr
!= '\0') {
1409 option_error("invalid numeric parameter '%s' for '%s' option",
1410 str
, current_option
);
1417 * save_source - store option source, line, and privilege into an
1418 * option_info structure.
1422 struct option_info
*info
;
1424 info
->priv
= privileged_option
;
1425 info
->source
= option_source
;
1426 info
->line
= option_line
;
1430 * set_source - set option source, line, and privilege from an
1431 * option_info structure.
1435 struct option_info
*info
;
1437 privileged_option
= info
->priv
;
1438 option_source
= info
->source
;
1439 option_line
= info
->line
;
1443 * name_source - return string containing option source and line. Can
1444 * be used as part of an option_error call.
1448 struct option_info
*info
;
1450 static char buf
[MAXPATHLEN
];
1452 if (info
->source
== NULL
)
1454 if (info
->line
<= 0)
1455 return info
->source
;
1456 (void) slprintf(buf
, sizeof (buf
), "%s:%d", info
->source
, info
->line
);
1457 return (const char *)buf
;
1461 * int_option - like number_option, but valp is int *, the base is assumed to
1462 * be 0, and *valp is not changed if there is an error. Returns 1 upon
1463 * successful processing of options, and 0 otherwise.
1466 int_option(str
, valp
)
1472 if (!number_option(str
, &v
, 0))
1480 * The following procedures parse options.
1484 * readfile - take commands from a file.
1492 return (options_from_file(*argv
, 1, 1, privileged_option
));
1496 * callfile - take commands from /etc/ppp/peers/<name>. Name may not contain
1497 * /../, start with / or ../, or end in /. Returns 1 upon successful
1498 * processing of options, and 0 otherwise.
1506 char *fname
, *arg
, *p
;
1511 if (arg
[0] == '/' || arg
[0] == '\0')
1514 for (p
= arg
; *p
!= '\0'; ) {
1515 if (p
[0] == '.' && p
[1] == '.' && (p
[2] == '/' || p
[2] == '\0')) {
1519 while (*p
!= '/' && *p
!= '\0')
1526 option_error("call option value may not contain .. or start with /");
1530 l
= strlen(arg
) + strlen(_PATH_PEERFILES
) + 1;
1531 if ((fname
= (char *) malloc(l
)) == NULL
)
1532 novm("call file name");
1533 (void) slprintf(fname
, l
, "%s%s", _PATH_PEERFILES
, arg
);
1535 ok
= options_from_file(fname
, 1, 1, 1);
1543 * setpdebug - set libpcap debugging level. Returns 1 upon successful
1544 * processing of options, and 0 otherwise.
1550 return (int_option(*argv
, &dflag
));
1554 * setpassfilter - set the pass filter for packets. Returns 1 upon successful
1555 * processing of options, and 0 otherwise.
1559 setpassfilter(argv
, opt
)
1563 pc
.linktype
= DLT_PPP
;
1564 pc
.snapshot
= PPP_HDRLEN
;
1566 if (pcap_compile(&pc
, &pass_filter
, *argv
, 1, netmask
) == 0)
1568 option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc
));
1573 * setactivefilter - set the active filter for packets. Returns 1 upon
1574 * successful processing of options, and 0 otherwise.
1578 setactivefilter(argv
, opt
)
1582 pc
.linktype
= DLT_PPP
;
1583 pc
.snapshot
= PPP_HDRLEN
;
1585 if (pcap_compile(&pc
, &active_filter
, *argv
, 1, netmask
) == 0)
1587 option_error("error in active-filter expression: %s\n", pcap_geterr(&pc
));
1590 #endif /* PPP_FILTER */
1593 * noopt - disable all options. Returns 1 upon successful processing of
1594 * options, and 0 otherwise.
1602 BZERO((char *) &lcp_wantoptions
[0], sizeof (struct lcp_options
));
1603 BZERO((char *) &lcp_allowoptions
[0], sizeof (struct lcp_options
));
1604 BZERO((char *) &ipcp_wantoptions
[0], sizeof (struct ipcp_options
));
1605 BZERO((char *) &ipcp_allowoptions
[0], sizeof (struct ipcp_options
));
1611 * setdomain - set domain name to append to hostname. Returns 1 upon
1612 * successful processing of options, and 0 otherwise.
1616 setdomain(argv
, opt
)
1620 if (!privileged_option
) {
1621 option_error("using the domain option requires root privilege");
1624 (void) gethostname(hostname
, MAXHOSTNAMELEN
+1);
1625 if (**argv
!= '\0') {
1627 (void) strncat(hostname
, ".", MAXHOSTNAMELEN
- strlen(hostname
));
1628 (void) strncat(hostname
, *argv
, MAXHOSTNAMELEN
- strlen(hostname
));
1630 hostname
[MAXHOSTNAMELEN
] = '\0';
1636 * setspeed - set the speed. Returns 1 upon successful processing of options,
1648 spd
= strtol(arg
, &ptr
, 0);
1649 if (ptr
== arg
|| *ptr
!= '\0' || spd
<= 0)
1652 save_source(&speed_info
);
1658 * setdevname - set the device name. Returns 1 upon successful processing of
1659 * options, 0 when the device does not exist, and -1 when an error is
1666 struct stat statbuf
;
1667 char dev
[MAXPATHLEN
];
1672 if (strncmp("/dev/", cp
, 5) != 0) {
1673 (void) strlcpy(dev
, "/dev/", sizeof(dev
));
1674 (void) strlcat(dev
, cp
, sizeof(dev
));
1679 * Check if there is a character device by this name.
1681 if (stat(cp
, &statbuf
) < 0) {
1682 if (errno
== ENOENT
) {
1685 option_error("Couldn't stat '%s': %m", cp
);
1688 if (!S_ISCHR(statbuf
.st_mode
)) {
1689 option_error("'%s' is not a character device", cp
);
1693 if (phase
!= PHASE_INITIALIZE
) {
1694 option_error("device name cannot be changed after initialization");
1696 } else if (devnam_fixed
) {
1697 option_error("per-tty options file may not specify device name");
1701 if (devnam_info
.priv
&& !privileged_option
) {
1702 option_error("device name %s from %s cannot be overridden",
1703 devnam
, name_source(&devnam_info
));
1707 (void) strlcpy(devnam
, cp
, sizeof(devnam
));
1710 save_source(&devnam_info
);
1717 * setipaddr - set the IP address. Returns 1 upon successful processing of
1718 * options, 0 when the argument does not contain a `:', and -1 for error.
1726 u_int32_t local
, remote
;
1727 ipcp_options
*wo
= &ipcp_wantoptions
[0];
1730 * IP address pair separated by ":".
1732 if ((colon
= strchr(arg
, ':')) == NULL
)
1738 * If colon first character, then no local addr.
1742 if ((local
= inet_addr(arg
)) == (u_int32_t
) -1) {
1743 if ((hp
= gethostbyname(arg
)) == NULL
) {
1744 option_error("unknown host: %s", arg
);
1747 BCOPY(hp
->h_addr
, &local
, sizeof(local
));
1750 if (bad_ip_adrs(local
)) {
1751 option_error("bad local IP address %I", local
);
1755 save_source(&ipsrc_info
);
1756 wo
->ouraddr
= local
;
1762 * If colon last character, then no remote addr.
1764 if (*++colon
!= '\0') {
1765 if ((remote
= inet_addr(colon
)) == (u_int32_t
) -1) {
1766 if ((hp
= gethostbyname(colon
)) == NULL
) {
1767 option_error("unknown host: %s", colon
);
1770 BCOPY(hp
->h_addr
, &remote
, sizeof(remote
));
1771 if (remote_name
[0] == '\0')
1772 (void) strlcpy(remote_name
, colon
, sizeof(remote_name
));
1775 if (bad_ip_adrs(remote
)) {
1776 option_error("bad remote IP address %I", remote
);
1780 save_source(&ipdst_info
);
1781 wo
->hisaddr
= remote
;
1790 * setnetmask - set the netmask to be used on the interface. Returns 1 upon
1791 * successful processing of options, and 0 otherwise.
1795 setnetmask(argv
, opt
)
1804 * Unfortunately, if we use inet_addr, we can't tell whether
1805 * a result of all 1s is an error or a valid 255.255.255.255.
1808 n
= parse_dotted_ip(p
, &mask
);
1812 if (n
== 0 || p
[n
] != 0 || (netmask
& ~mask
) != 0) {
1813 option_error("invalid netmask value '%s'", *argv
);
1822 * parse_dotted_ip - parse and convert the IP address string to make
1823 * sure it conforms to the dotted notation. Returns the length of
1824 * processed characters upon success, and 0 otherwise. If successful,
1825 * the converted IP address number is stored in vp, in the host byte
1829 parse_dotted_ip(cp
, vp
)
1833 register u_int32_t val
, base
, n
;
1836 u_char parts
[3], *pp
= parts
;
1838 if ((*cp
== '\0') || (vp
== NULL
))
1839 return (0); /* disallow null string in cp */
1843 * Collect number up to ``.''. Values are specified as for C:
1844 * 0x=hex, 0=octal, other=decimal.
1848 if (*++cp
== 'x' || *cp
== 'X')
1853 while ((c
= *cp
) != '\0') {
1855 if ((c
- '0') >= base
)
1857 val
= (val
* base
) + (c
- '0');
1861 if (base
== 16 && isxdigit(c
)) {
1862 val
= (val
<< 4) + (c
+ 10 - (islower(c
) ? 'a' : 'A'));
1872 * a.b.c (with c treated as 16-bits)
1873 * a.b (with b treated as 24 bits)
1875 if ((pp
>= parts
+ 3) || (val
> 0xff)) {
1878 *pp
++ = (u_char
)val
;
1883 * Check for trailing characters.
1885 if (*cp
!= '\0' && !isspace(*cp
)) {
1889 * Concoct the address according to the number of parts specified.
1893 case 0: /* a -- 32 bits */
1895 case 1: /* a.b -- 8.24 bits */
1898 val
|= parts
[0] << 24;
1900 case 2: /* a.b.c -- 8.8.16 bits */
1903 val
|= (parts
[0] << 24) | (parts
[1] << 16);
1905 case 3: /* a.b.c.d -- 8.8.8.8 bits */
1908 val
|= (parts
[0] << 24) | (parts
[1] << 16) | (parts
[2] << 8);
1918 * setxonxoff - modify the asyncmap to include escaping XON and XOFF
1919 * characters used for software flow control. Returns 1 upon successful
1920 * processing of options, and 0 otherwise.
1924 setxonxoff(argv
, opt
)
1928 int xonxoff
= 0x000A0000;
1930 lcp_wantoptions
[0].neg_asyncmap
= 1;
1931 lcp_wantoptions
[0].asyncmap
|= xonxoff
; /* escape ^S and ^Q */
1932 lcp_allowoptions
[0].asyncmap
|= xonxoff
;
1933 xmit_accm
[0][0] |= xonxoff
;
1934 xmit_accm
[0][4] |= xonxoff
; /* escape 0x91 and 0x93 as well */
1941 * setlogfile - open (or create) a file used for logging purposes. Returns 1
1942 * upon success, and 0 otherwise.
1946 setlogfile(argv
, opt
)
1952 if (!privileged_option
)
1953 (void) seteuid(getuid());
1954 fd
= open(*argv
, O_WRONLY
| O_APPEND
| O_CREAT
| O_EXCL
, 0644);
1955 if (fd
< 0 && errno
== EEXIST
)
1956 fd
= open(*argv
, O_WRONLY
| O_APPEND
);
1958 if (!privileged_option
)
1962 option_error("Can't open log file %s: %m", *argv
);
1965 if (log_to_file
&& log_to_fd
>= 0)
1966 (void) close(log_to_fd
);
1975 * loadplugin - load and initialize the plugin. Returns 1 upon successful
1976 * processing of the plugin, and 0 otherwise.
1980 loadplugin(argv
, opt
)
1987 void (*init
) __P((void));
1989 handle
= dlopen(arg
, RTLD_GLOBAL
| RTLD_NOW
);
1990 if (handle
== NULL
) {
1993 option_error("%s", err
);
1994 option_error("Couldn't load plugin %s", arg
);
1997 init
= (void (*)(void))dlsym(handle
, "plugin_init");
1999 option_error("%s has no initialization entry point", arg
);
2000 (void) dlclose(handle
);
2003 info("Plugin %s loaded.", arg
);