]>
Commit | Line | Data |
---|---|---|
fd05b7dc RM |
1 | /* |
2 | * dhcpcd - DHCP client daemon | |
3 | * Copyright 2006-2008 Roy Marples <roy@marples.name> | |
4 | * All rights reserved | |
5 | ||
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
25 | * SUCH DAMAGE. | |
26 | */ | |
27 | ||
28 | #include <sys/types.h> | |
29 | ||
30 | #include <arpa/inet.h> | |
31 | ||
32 | #include <ctype.h> | |
33 | #include <errno.h> | |
34 | #include <getopt.h> | |
35 | #include <paths.h> | |
36 | #include <stdio.h> | |
37 | #include <stdlib.h> | |
38 | #include <string.h> | |
39 | #include <unistd.h> | |
40 | #include <time.h> | |
41 | ||
42 | #include "common.h" | |
43 | #include "config.h" | |
44 | #include "dhcpf.h" | |
45 | #include "if-options.h" | |
46 | #include "logger.h" | |
47 | #include "net.h" | |
48 | ||
49 | /* Don't set any optional arguments here so we retain POSIX | |
50 | * compatibility with getopt */ | |
51 | #define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:" | |
52 | ||
53 | const struct option cf_options[] = { | |
54 | {"background", no_argument, NULL, 'b'}, | |
55 | {"script", required_argument, NULL, 'c'}, | |
56 | {"debug", no_argument, NULL, 'd'}, | |
57 | {"config", required_argument, NULL, 'f'}, | |
58 | {"hostname", optional_argument, NULL, 'h'}, | |
59 | {"vendorclassid", optional_argument, NULL, 'i'}, | |
60 | {"release", no_argument, NULL, 'k'}, | |
61 | {"leasetime", required_argument, NULL, 'l'}, | |
62 | {"metric", required_argument, NULL, 'm'}, | |
63 | {"rebind", no_argument, NULL, 'n'}, | |
64 | {"option", required_argument, NULL, 'o'}, | |
65 | {"persistent", no_argument, NULL, 'p'}, | |
66 | {"quiet", no_argument, NULL, 'q'}, | |
67 | {"request", optional_argument, NULL, 'r'}, | |
68 | {"inform", optional_argument, NULL, 's'}, | |
69 | {"timeout", required_argument, NULL, 't'}, | |
70 | {"userclass", required_argument, NULL, 'u'}, | |
71 | {"vendor", required_argument, NULL, 'v'}, | |
72 | {"exit", no_argument, NULL, 'x'}, | |
73 | {"noarp", no_argument, NULL, 'A'}, | |
74 | {"nobackground", no_argument, NULL, 'B'}, | |
75 | {"nohook", required_argument, NULL, 'C'}, | |
76 | {"duid", no_argument, NULL, 'D'}, | |
77 | {"lastlease", no_argument, NULL, 'E'}, | |
78 | {"fqdn", optional_argument, NULL, 'F'}, | |
79 | {"nogateway", no_argument, NULL, 'G'}, | |
80 | {"clientid", optional_argument, NULL, 'I'}, | |
81 | {"nolink", no_argument, NULL, 'K'}, | |
82 | {"noipv4ll", no_argument, NULL, 'L'}, | |
83 | {"nooption", optional_argument, NULL, 'O'}, | |
84 | {"require", required_argument, NULL, 'Q'}, | |
85 | {"test", no_argument, NULL, 'T'}, | |
86 | {"variables", no_argument, NULL, 'V'}, | |
87 | {"blacklist", required_argument, NULL, 'X'}, | |
88 | {NULL, 0, NULL, '\0'} | |
89 | }; | |
90 | ||
91 | static int | |
92 | atoint(const char *s) | |
93 | { | |
94 | char *t; | |
95 | long n; | |
96 | ||
97 | errno = 0; | |
98 | n = strtol(s, &t, 0); | |
99 | if ((errno != 0 && n == 0) || s == t || | |
100 | (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN))) | |
101 | { | |
102 | logger(LOG_ERR, "`%s' out of range", s); | |
103 | return -1; | |
104 | } | |
105 | ||
106 | return (int)n; | |
107 | } | |
108 | ||
109 | static char * | |
110 | add_environ(struct if_options *ifo, const char *value, int uniq) | |
111 | { | |
112 | char **newlist; | |
113 | char **lst = ifo->environ; | |
114 | size_t i = 0, l, lv; | |
115 | char *match = NULL, *p; | |
116 | ||
117 | match = xstrdup(value); | |
118 | p = strchr(match, '='); | |
119 | if (p) | |
120 | *p++ = '\0'; | |
121 | l = strlen(match); | |
122 | ||
123 | while (lst && lst[i]) { | |
124 | if (match && strncmp(lst[i], match, l) == 0) { | |
125 | if (uniq) { | |
126 | free(lst[i]); | |
127 | lst[i] = xstrdup(value); | |
128 | } else { | |
129 | /* Append a space and the value to it */ | |
130 | l = strlen(lst[i]); | |
131 | lv = strlen(p); | |
132 | lst[i] = xrealloc(lst[i], l + lv + 2); | |
133 | lst[i][l] = ' '; | |
134 | memcpy(lst[i] + l + 1, p, lv); | |
135 | lst[i][l + lv + 1] = '\0'; | |
136 | } | |
137 | free(match); | |
138 | return lst[i]; | |
139 | } | |
140 | i++; | |
141 | } | |
142 | ||
143 | newlist = xrealloc(lst, sizeof(char *) * (i + 2)); | |
144 | newlist[i] = xstrdup(value); | |
145 | newlist[i + 1] = NULL; | |
146 | ifo->environ = newlist; | |
147 | free(match); | |
148 | return newlist[i]; | |
149 | } | |
150 | ||
151 | #define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0) | |
152 | static ssize_t | |
153 | parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid) | |
154 | { | |
155 | ssize_t l; | |
156 | const char *p; | |
157 | int i, punt_last = 0; | |
158 | char c[4]; | |
159 | ||
160 | /* If surrounded by quotes then it's a string */ | |
161 | if (*str == '"') { | |
162 | str++; | |
163 | l = strlen(str); | |
164 | p = str + l - 1; | |
165 | if (*p == '"') | |
166 | punt_last = 1; | |
167 | } else { | |
168 | l = hwaddr_aton(NULL, str); | |
169 | if (l > 1) { | |
170 | if (l > slen) { | |
171 | errno = ENOBUFS; | |
172 | return -1; | |
173 | } | |
174 | hwaddr_aton((uint8_t *)sbuf, str); | |
175 | return l; | |
176 | } | |
177 | } | |
178 | ||
179 | /* Process escapes */ | |
180 | l = 0; | |
181 | /* If processing a string on the clientid, first byte should be | |
182 | * 0 to indicate a non hardware type */ | |
183 | if (clid) { | |
184 | *sbuf++ = 0; | |
185 | l++; | |
186 | } | |
187 | c[3] = '\0'; | |
188 | while (*str) { | |
189 | if (++l > slen) { | |
190 | errno = ENOBUFS; | |
191 | return -1; | |
192 | } | |
193 | if (*str == '\\') { | |
194 | str++; | |
195 | switch(*str++) { | |
196 | case '\0': | |
197 | break; | |
198 | case 'b': | |
199 | *sbuf++ = '\b'; | |
200 | break; | |
201 | case 'n': | |
202 | *sbuf++ = '\n'; | |
203 | break; | |
204 | case 'r': | |
205 | *sbuf++ = '\r'; | |
206 | break; | |
207 | case 't': | |
208 | *sbuf++ = '\t'; | |
209 | break; | |
210 | case 'x': | |
211 | /* Grab a hex code */ | |
212 | c[1] = '\0'; | |
213 | for (i = 0; i < 2; i++) { | |
214 | if (isxdigit((unsigned char)*str) == 0) | |
215 | break; | |
216 | c[i] = *str++; | |
217 | } | |
218 | if (c[1] != '\0') { | |
219 | c[2] = '\0'; | |
220 | *sbuf++ = strtol(c, NULL, 16); | |
221 | } else | |
222 | l--; | |
223 | break; | |
224 | case '0': | |
225 | /* Grab an octal code */ | |
226 | c[2] = '\0'; | |
227 | for (i = 0; i < 3; i++) { | |
228 | if (*str < '0' || *str > '7') | |
229 | break; | |
230 | c[i] = *str++; | |
231 | } | |
232 | if (c[2] != '\0') { | |
233 | i = strtol(c, NULL, 8); | |
234 | if (i > 255) | |
235 | i = 255; | |
236 | *sbuf ++= i; | |
237 | } else | |
238 | l--; | |
239 | break; | |
240 | default: | |
241 | *sbuf++ = *str++; | |
242 | } | |
243 | } else | |
244 | *sbuf++ = *str++; | |
245 | } | |
246 | if (punt_last) | |
247 | *--sbuf = '\0'; | |
248 | return l; | |
249 | } | |
250 | ||
251 | static int | |
252 | parse_option(struct if_options *ifo, int opt, const char *arg) | |
253 | { | |
254 | int i; | |
255 | char *p; | |
256 | ssize_t s; | |
257 | struct in_addr addr; | |
258 | ||
259 | switch(opt) { | |
da166178 RM |
260 | case 'b': /* FALLTHROUGH */ |
261 | case 'd': /* FALLTHROUGH */ | |
262 | case 'k': /* FALLTHROUGH */ | |
263 | case 'n': /* FALLTHROUGH */ | |
264 | case 'x': /* FALLTHROUGH */ | |
e7eeaf88 RM |
265 | case 'B': /* FALLTHROUGH */ |
266 | case 'T': /* We need to handle non interface options */ | |
fd05b7dc RM |
267 | break; |
268 | case 'c': | |
269 | strlcpy(ifo->script, arg, sizeof(ifo->script)); | |
270 | break; | |
fd05b7dc RM |
271 | case 'h': |
272 | if (arg) | |
273 | s = parse_string(ifo->hostname + 1, | |
274 | HOSTNAME_MAX_LEN, arg); | |
275 | else | |
276 | s = 0; | |
277 | if (s == -1) { | |
f6de5860 | 278 | logger(LOG_ERR, "hostname: %m"); |
fd05b7dc RM |
279 | return -1; |
280 | } | |
281 | if (s != 0 && ifo->hostname[1] == '.') { | |
282 | logger(LOG_ERR, "hostname cannot begin with a ."); | |
283 | return -1; | |
284 | } | |
285 | ifo->hostname[0] = (uint8_t)s; | |
286 | break; | |
287 | case 'i': | |
288 | if (arg) | |
289 | s = parse_string((char *)ifo->vendorclassid + 1, | |
290 | VENDORCLASSID_MAX_LEN, arg); | |
291 | else | |
292 | s = 0; | |
293 | if (s == -1) { | |
f6de5860 | 294 | logger(LOG_ERR, "vendorclassid: %m"); |
fd05b7dc RM |
295 | return -1; |
296 | } | |
297 | *ifo->vendorclassid = (uint8_t)s; | |
298 | break; | |
299 | case 'l': | |
300 | if (*arg == '-') { | |
301 | logger(LOG_ERR, | |
302 | "leasetime must be a positive value"); | |
303 | return -1; | |
304 | } | |
305 | errno = 0; | |
306 | ifo->leasetime = (uint32_t)strtol(arg, NULL, 0); | |
307 | if (errno == EINVAL || errno == ERANGE) { | |
308 | logger(LOG_ERR, "`%s' out of range", arg); | |
309 | return -1; | |
310 | } | |
311 | break; | |
312 | case 'm': | |
313 | ifo->metric = atoint(arg); | |
314 | if (ifo->metric < 0) { | |
315 | logger(LOG_ERR, "metric must be a positive value"); | |
316 | return -1; | |
317 | } | |
318 | break; | |
319 | case 'o': | |
320 | if (make_option_mask(ifo->requestmask, arg, 1) != 0) { | |
321 | logger(LOG_ERR, "unknown option `%s'", arg); | |
322 | return -1; | |
323 | } | |
324 | break; | |
325 | case 'p': | |
326 | ifo->options |= DHCPCD_PERSISTENT; | |
327 | break; | |
328 | case 'q': | |
329 | setloglevel(LOG_WARNING); | |
330 | break; | |
331 | case 's': | |
332 | ifo->options |= DHCPCD_INFORM; | |
333 | ifo->options |= DHCPCD_PERSISTENT; | |
334 | ifo->options &= ~DHCPCD_ARP; | |
335 | if (!arg || *arg == '\0') { | |
336 | ifo->request_address.s_addr = 0; | |
337 | break; | |
338 | } else { | |
339 | if ((p = strchr(arg, '/'))) { | |
340 | /* nullify the slash, so the -r option | |
341 | * can read the address */ | |
342 | *p++ = '\0'; | |
343 | if (sscanf(p, "%d", &i) != 1 || | |
344 | inet_cidrtoaddr(i, &ifo->request_netmask) != 0) | |
345 | { | |
346 | logger(LOG_ERR, | |
347 | "`%s' is not a valid CIDR", | |
348 | p); | |
349 | return -1; | |
350 | } | |
351 | } | |
352 | } | |
353 | /* FALLTHROUGH */ | |
354 | case 'r': | |
355 | if (!(ifo->options & DHCPCD_INFORM)) | |
356 | ifo->options |= DHCPCD_REQUEST; | |
357 | if (arg && !inet_aton(arg, &ifo->request_address)) { | |
358 | logger(LOG_ERR, "`%s' is not a valid IP address", | |
359 | arg); | |
360 | return -1; | |
361 | } | |
362 | break; | |
363 | case 't': | |
364 | ifo->timeout = atoint(arg); | |
365 | if (ifo->timeout < 0) { | |
366 | logger (LOG_ERR, "timeout must be a positive value"); | |
367 | return -1; | |
368 | } | |
369 | break; | |
370 | case 'u': | |
371 | s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1; | |
372 | s = parse_string((char *)ifo->userclass + ifo->userclass[0] + 2, | |
373 | s, arg); | |
374 | if (s == -1) { | |
f6de5860 | 375 | logger(LOG_ERR, "userclass: %m"); |
fd05b7dc RM |
376 | return -1; |
377 | } | |
378 | if (s != 0) { | |
379 | ifo->userclass[ifo->userclass[0] + 1] = s; | |
380 | ifo->userclass[0] += s + 1; | |
381 | } | |
382 | break; | |
383 | case 'v': | |
384 | p = strchr(arg, ','); | |
385 | if (!p || !p[1]) { | |
386 | logger(LOG_ERR, "invalid vendor format"); | |
387 | return -1; | |
388 | } | |
389 | *p = '\0'; | |
390 | i = atoint(arg); | |
391 | arg = p + 1; | |
392 | if (i < 1 || i > 254) { | |
393 | logger(LOG_ERR, "vendor option should be between" | |
394 | " 1 and 254 inclusive"); | |
395 | return -1; | |
396 | } | |
397 | s = VENDOR_MAX_LEN - ifo->vendor[0] - 2; | |
398 | if (inet_aton(arg, &addr) == 1) { | |
399 | if (s < 6) { | |
400 | s = -1; | |
401 | errno = ENOBUFS; | |
402 | } else | |
403 | memcpy(ifo->vendor + ifo->vendor[0] + 3, | |
404 | &addr.s_addr, sizeof(addr.s_addr)); | |
405 | } else { | |
406 | s = parse_string((char *)ifo->vendor + ifo->vendor[0] + 3, | |
407 | s, arg); | |
408 | } | |
409 | if (s == -1) { | |
f6de5860 | 410 | logger(LOG_ERR, "vendor: %m"); |
fd05b7dc RM |
411 | return -1; |
412 | } | |
413 | if (s != 0) { | |
414 | ifo->vendor[ifo->vendor[0] + 1] = i; | |
415 | ifo->vendor[ifo->vendor[0] + 2] = s; | |
416 | ifo->vendor[0] += s + 2; | |
417 | } | |
418 | break; | |
fd05b7dc RM |
419 | case 'A': |
420 | ifo->options &= ~DHCPCD_ARP; | |
421 | /* IPv4LL requires ARP */ | |
422 | ifo->options &= ~DHCPCD_IPV4LL; | |
423 | break; | |
fd05b7dc RM |
424 | case 'C': |
425 | /* Commas to spaces for shell */ | |
426 | while ((p = strchr(arg, ','))) | |
427 | *p = ' '; | |
428 | s = strlen("skip_hooks=") + strlen(arg) + 1; | |
429 | p = xmalloc(sizeof(char) * s); | |
430 | snprintf(p, s, "skip_hooks=%s", arg); | |
431 | add_environ(ifo, p, 0); | |
432 | free(p); | |
433 | break; | |
434 | case 'D': | |
435 | ifo->options |= DHCPCD_DUID; | |
436 | break; | |
437 | case 'E': | |
438 | ifo->options |= DHCPCD_LASTLEASE; | |
439 | break; | |
440 | case 'F': | |
441 | if (!arg) { | |
442 | ifo->fqdn = FQDN_BOTH; | |
443 | break; | |
444 | } | |
445 | if (strcmp(arg, "none") == 0) | |
446 | ifo->fqdn = FQDN_NONE; | |
447 | else if (strcmp(arg, "ptr") == 0) | |
448 | ifo->fqdn = FQDN_PTR; | |
449 | else if (strcmp(arg, "both") == 0) | |
450 | ifo->fqdn = FQDN_BOTH; | |
451 | else if (strcmp(arg, "disable") == 0) | |
452 | ifo->fqdn = FQDN_DISABLE; | |
453 | else { | |
454 | logger(LOG_ERR, "invalid value `%s' for FQDN", arg); | |
455 | return -1; | |
456 | } | |
457 | break; | |
458 | case 'G': | |
459 | ifo->options &= ~DHCPCD_GATEWAY; | |
460 | break; | |
461 | case 'I': | |
462 | /* Strings have a type of 0 */; | |
463 | ifo->clientid[1] = 0; | |
464 | if (arg) | |
465 | s = parse_string_hwaddr((char *)ifo->clientid + 1, | |
466 | CLIENTID_MAX_LEN, arg, 1); | |
467 | else | |
468 | s = 0; | |
469 | if (s == -1) { | |
f6de5860 | 470 | logger(LOG_ERR, "clientid: %m"); |
fd05b7dc RM |
471 | return -1; |
472 | } | |
473 | ifo->clientid[0] = (uint8_t)s; | |
474 | if (s == 0) { | |
475 | ifo->options &= ~DHCPCD_DUID; | |
476 | ifo->options &= ~DHCPCD_CLIENTID; | |
477 | } | |
478 | break; | |
479 | case 'K': | |
480 | ifo->options &= ~DHCPCD_LINK; | |
481 | break; | |
482 | case 'L': | |
483 | ifo->options &= ~DHCPCD_IPV4LL; | |
484 | break; | |
485 | case 'O': | |
486 | if (make_option_mask(ifo->requestmask, arg, -1) != 0 || | |
487 | make_option_mask(ifo->requiremask, arg, -1) != 0 || | |
488 | make_option_mask(ifo->nomask, arg, 1) != 0) | |
489 | { | |
490 | logger(LOG_ERR, "unknown option `%s'", arg); | |
491 | return -1; | |
492 | } | |
493 | break; | |
494 | case 'Q': | |
495 | if (make_option_mask(ifo->requiremask, arg, 1) != 0 || | |
496 | make_option_mask(ifo->requestmask, arg, 1) != 0) | |
497 | { | |
498 | logger(LOG_ERR, "unknown option `%s'", arg); | |
499 | return -1; | |
500 | } | |
501 | break; | |
502 | case 'X': | |
503 | if (!inet_aton(arg, &addr)) { | |
504 | logger(LOG_ERR, "`%s' is not a valid IP address", | |
505 | arg); | |
506 | return -1; | |
507 | } | |
508 | ifo->blacklist = xrealloc(ifo->blacklist, | |
509 | sizeof(in_addr_t) * (ifo->blacklist_len + 1)); | |
510 | ifo->blacklist[ifo->blacklist_len] = addr.s_addr; | |
511 | ifo->blacklist_len++; | |
512 | break; | |
513 | default: | |
514 | return 0; | |
515 | } | |
516 | ||
517 | return 1; | |
518 | } | |
519 | ||
520 | static int | |
521 | parse_config_line(struct if_options *ifo, const char *opt, char *line) | |
522 | { | |
523 | unsigned int i; | |
524 | ||
525 | for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) { | |
526 | if (!cf_options[i].name || | |
527 | strcmp(cf_options[i].name, opt) != 0) | |
528 | continue; | |
529 | ||
530 | if (cf_options[i].has_arg == required_argument && !line) { | |
531 | fprintf(stderr, | |
532 | PACKAGE ": option requires an argument -- %s\n", | |
533 | opt); | |
534 | return -1; | |
535 | } | |
536 | ||
537 | return parse_option(ifo, cf_options[i].val, line); | |
538 | } | |
539 | ||
540 | fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt); | |
541 | return -1; | |
542 | } | |
543 | ||
544 | struct if_options * | |
545 | read_config(const char *file, const char *ifname) | |
546 | { | |
547 | struct if_options *ifo; | |
548 | FILE *f; | |
549 | size_t len = 0; | |
550 | char *line, *option, *p, *buffer = NULL; | |
551 | int skip = 0; | |
552 | ||
553 | /* Seed our default options */ | |
554 | ifo = xzalloc(sizeof(*ifo)); | |
555 | ifo->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE; | |
556 | ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK; | |
557 | ifo->timeout = DEFAULT_TIMEOUT; | |
f43e5853 | 558 | ifo->metric = -1; |
fd05b7dc RM |
559 | gethostname(ifo->hostname + 1, sizeof(ifo->hostname)); |
560 | if (strcmp(ifo->hostname + 1, "(none)") == 0 || | |
561 | strcmp(ifo->hostname + 1, "localhost") == 0) | |
562 | ifo->hostname[1] = '\0'; | |
563 | *ifo->hostname = strlen(ifo->hostname + 1); | |
564 | strlcpy(ifo->script, SCRIPT, sizeof(ifo->script)); | |
565 | ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1, | |
566 | VENDORCLASSID_MAX_LEN, | |
567 | "%s %s", PACKAGE, VERSION); | |
568 | ||
569 | /* Parse our options file */ | |
570 | f = fopen(file, "r"); | |
571 | if (!f) | |
572 | return ifo; | |
573 | ||
574 | while ((get_line(&buffer, &len, f))) { | |
575 | line = buffer; | |
576 | while ((option = strsep(&line, " \t"))) | |
577 | if (*option != '\0') | |
578 | break; | |
579 | if (!option || *option == '\0' || *option == '#') | |
580 | continue; | |
581 | /* Trim leading whitespace */ | |
582 | if (line) { | |
583 | while (*line != '\0' && (*line == ' ' || *line == '\t')) | |
584 | line++; | |
585 | } | |
586 | /* Trim trailing whitespace */ | |
587 | if (line && *line) { | |
588 | p = line + strlen(line) - 1; | |
589 | while (p != line && | |
590 | (*p == ' ' || *p == '\t') && | |
591 | *(p - 1) != '\\') | |
592 | *p-- = '\0'; | |
593 | } | |
594 | /* Start of an interface block, skip if not ours */ | |
595 | if (strcmp(option, "interface") == 0) { | |
596 | if (ifname && line && strcmp(line, ifname) == 0) | |
597 | skip = 0; | |
598 | else | |
599 | skip = 1; | |
600 | continue; | |
601 | } | |
602 | if (skip) | |
603 | continue; | |
604 | if (parse_config_line(ifo, option, line) != 1) { | |
605 | break; | |
606 | } | |
607 | } | |
608 | free(buffer); | |
609 | fclose(f); | |
610 | ||
611 | /* Terminate the encapsulated options */ | |
612 | if (ifo->vendor[0]) { | |
613 | ifo->vendor[0]++; | |
614 | ifo->vendor[ifo->vendor[0]] = DHO_END; | |
615 | } | |
616 | return ifo; | |
617 | } | |
618 | ||
619 | int | |
620 | add_options(struct if_options *ifo, int argc, char **argv) | |
621 | { | |
622 | int oi, opt, r = 1; | |
623 | ||
624 | optind = 0; | |
625 | while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1) | |
626 | { | |
627 | r = parse_option(ifo, opt, optarg); | |
628 | if (r != 1) | |
629 | break; | |
630 | } | |
631 | /* Terminate the encapsulated options */ | |
632 | if (r == 1 && ifo->vendor[0]) { | |
633 | ifo->vendor[0]++; | |
634 | ifo->vendor[ifo->vendor[0]] = DHO_END; | |
635 | } | |
636 | return r; | |
637 | } | |
638 | ||
639 | void | |
640 | free_options(struct if_options *ifo) | |
641 | { | |
642 | size_t i; | |
643 | ||
f43e5853 RM |
644 | if (ifo) { |
645 | if (ifo->environ) { | |
646 | i = 0; | |
647 | while (ifo->environ[i]) | |
648 | free(ifo->environ[i++]); | |
649 | free(ifo->environ); | |
650 | } | |
651 | free(ifo->blacklist); | |
652 | free(ifo); | |
fd05b7dc | 653 | } |
fd05b7dc | 654 | } |