Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / bsd / dhcpcd / dist / if-options.c
blobc4d8d09680a436b059a978f4a2fb8e7728f70b18
1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2009 Roy Marples <roy@marples.name>
4 * All rights reserved
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.
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.
28 #include <sys/types.h>
30 #include <arpa/inet.h>
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 <syslog.h>
40 #include <unistd.h>
41 #include <time.h>
43 #include "config.h"
44 #include "common.h"
45 #include "if-options.h"
46 #include "net.h"
48 /* These options only make sense in the config file, so don't use any
49 valid short options for them */
50 #define O_BASE MAX('z', 'Z') + 1
51 #define O_ARPING O_BASE + 1
52 #define O_FALLBACK O_BASE + 2
54 const struct option cf_options[] = {
55 {"background", no_argument, NULL, 'b'},
56 {"script", required_argument, NULL, 'c'},
57 {"debug", no_argument, NULL, 'd'},
58 {"env", required_argument, NULL, 'e'},
59 {"config", required_argument, NULL, 'f'},
60 {"reconfigure", no_argument, NULL, 'g'},
61 {"hostname", optional_argument, NULL, 'h'},
62 {"vendorclassid", optional_argument, NULL, 'i'},
63 {"release", no_argument, NULL, 'k'},
64 {"leasetime", required_argument, NULL, 'l'},
65 {"metric", required_argument, NULL, 'm'},
66 {"rebind", no_argument, NULL, 'n'},
67 {"option", required_argument, NULL, 'o'},
68 {"persistent", no_argument, NULL, 'p'},
69 {"quiet", no_argument, NULL, 'q'},
70 {"request", optional_argument, NULL, 'r'},
71 {"inform", optional_argument, NULL, 's'},
72 {"timeout", required_argument, NULL, 't'},
73 {"userclass", required_argument, NULL, 'u'},
74 {"vendor", required_argument, NULL, 'v'},
75 {"waitip", no_argument, NULL, 'w'},
76 {"exit", no_argument, NULL, 'x'},
77 {"allowinterfaces", required_argument, NULL, 'z'},
78 {"reboot", required_argument, NULL, 'y'},
79 {"noarp", no_argument, NULL, 'A'},
80 {"nobackground", no_argument, NULL, 'B'},
81 {"nohook", required_argument, NULL, 'C'},
82 {"duid", no_argument, NULL, 'D'},
83 {"lastlease", no_argument, NULL, 'E'},
84 {"fqdn", optional_argument, NULL, 'F'},
85 {"nogateway", no_argument, NULL, 'G'},
86 {"clientid", optional_argument, NULL, 'I'},
87 {"nolink", no_argument, NULL, 'K'},
88 {"noipv4ll", no_argument, NULL, 'L'},
89 {"destination", required_argument, NULL, 'N'},
90 {"nooption", optional_argument, NULL, 'O'},
91 {"require", required_argument, NULL, 'Q'},
92 {"static", required_argument, NULL, 'S'},
93 {"test", no_argument, NULL, 'T'},
94 {"variables", no_argument, NULL, 'V'},
95 {"whitelist", required_argument, NULL, 'W'},
96 {"blacklist", required_argument, NULL, 'X'},
97 {"denyinterfaces", required_argument, NULL, 'Z'},
98 {"arping", required_argument, NULL, O_ARPING},
99 {"fallback", required_argument, NULL, O_FALLBACK},
100 {NULL, 0, NULL, '\0'}
103 static int
104 atoint(const char *s)
106 char *t;
107 long n;
109 errno = 0;
110 n = strtol(s, &t, 0);
111 if ((errno != 0 && n == 0) || s == t ||
112 (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
114 syslog(LOG_ERR, "`%s' out of range", s);
115 return -1;
118 return (int)n;
121 static char *
122 add_environ(struct if_options *ifo, const char *value, int uniq)
124 char **newlist;
125 char **lst = ifo->environ;
126 size_t i = 0, l, lv;
127 char *match = NULL, *p;
129 match = xstrdup(value);
130 p = strchr(match, '=');
131 if (p)
132 *p++ = '\0';
133 l = strlen(match);
135 while (lst && lst[i]) {
136 if (match && strncmp(lst[i], match, l) == 0) {
137 if (uniq) {
138 free(lst[i]);
139 lst[i] = xstrdup(value);
140 } else {
141 /* Append a space and the value to it */
142 l = strlen(lst[i]);
143 lv = strlen(p);
144 lst[i] = xrealloc(lst[i], l + lv + 2);
145 lst[i][l] = ' ';
146 memcpy(lst[i] + l + 1, p, lv);
147 lst[i][l + lv + 1] = '\0';
149 free(match);
150 return lst[i];
152 i++;
155 newlist = xrealloc(lst, sizeof(char *) * (i + 2));
156 newlist[i] = xstrdup(value);
157 newlist[i + 1] = NULL;
158 ifo->environ = newlist;
159 free(match);
160 return newlist[i];
163 #define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
164 static ssize_t
165 parse_string_hwaddr(char *sbuf, ssize_t slen, const char *str, int clid)
167 ssize_t l;
168 const char *p;
169 int i, punt_last = 0;
170 char c[4];
172 /* If surrounded by quotes then it's a string */
173 if (*str == '"') {
174 str++;
175 l = strlen(str);
176 p = str + l - 1;
177 if (*p == '"')
178 punt_last = 1;
179 } else {
180 l = hwaddr_aton(NULL, str);
181 if (l > 1) {
182 if (l > slen) {
183 errno = ENOBUFS;
184 return -1;
186 hwaddr_aton((uint8_t *)sbuf, str);
187 return l;
191 /* Process escapes */
192 l = 0;
193 /* If processing a string on the clientid, first byte should be
194 * 0 to indicate a non hardware type */
195 if (clid && *str) {
196 *sbuf++ = 0;
197 l++;
199 c[3] = '\0';
200 while (*str) {
201 if (++l > slen) {
202 errno = ENOBUFS;
203 return -1;
205 if (*str == '\\') {
206 str++;
207 switch(*str) {
208 case '\0':
209 break;
210 case 'b':
211 *sbuf++ = '\b';
212 str++;
213 break;
214 case 'n':
215 *sbuf++ = '\n';
216 str++;
217 break;
218 case 'r':
219 *sbuf++ = '\r';
220 str++;
221 break;
222 case 't':
223 *sbuf++ = '\t';
224 str++;
225 break;
226 case 'x':
227 /* Grab a hex code */
228 c[1] = '\0';
229 for (i = 0; i < 2; i++) {
230 if (isxdigit((unsigned char)*str) == 0)
231 break;
232 c[i] = *str++;
234 if (c[1] != '\0') {
235 c[2] = '\0';
236 *sbuf++ = strtol(c, NULL, 16);
237 } else
238 l--;
239 break;
240 case '0':
241 /* Grab an octal code */
242 c[2] = '\0';
243 for (i = 0; i < 3; i++) {
244 if (*str < '0' || *str > '7')
245 break;
246 c[i] = *str++;
248 if (c[2] != '\0') {
249 i = strtol(c, NULL, 8);
250 if (i > 255)
251 i = 255;
252 *sbuf ++= i;
253 } else
254 l--;
255 break;
256 default:
257 *sbuf++ = *str++;
259 } else
260 *sbuf++ = *str++;
262 if (punt_last) {
263 *--sbuf = '\0';
264 l--;
266 return l;
269 static char **
270 splitv(int *argc, char **argv, const char *arg)
272 char **v = argv;
273 char *o = xstrdup(arg), *p, *t;
275 p = o;
276 while ((t = strsep(&p, ", "))) {
277 (*argc)++;
278 v = xrealloc(v, sizeof(char *) * ((*argc)));
279 v[(*argc) - 1] = xstrdup(t);
281 free(o);
282 return v;
285 static int
286 parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
288 char *p;
289 int i;
291 if (arg == NULL || *arg == '\0') {
292 if (addr != NULL)
293 addr->s_addr = 0;
294 if (net != NULL)
295 net->s_addr = 0;
296 return 0;
298 if ((p = strchr(arg, '/')) != NULL) {
299 *p++ = '\0';
300 if (net != NULL &&
301 (sscanf(p, "%d", &i) != 1 ||
302 inet_cidrtoaddr(i, net) != 0))
304 syslog(LOG_ERR, "`%s' is not a valid CIDR", p);
305 return -1;
309 if (addr != NULL && inet_aton(arg, addr) == 0) {
310 syslog(LOG_ERR, "`%s' is not a valid IP address", arg);
311 return -1;
313 if (p != NULL)
314 *--p = '/';
315 else if (net != NULL)
316 net->s_addr = get_netmask(addr->s_addr);
317 return 0;
320 static int
321 parse_option(struct if_options *ifo, int opt, const char *arg)
323 int i;
324 char *p = NULL, *np;
325 ssize_t s;
326 struct in_addr addr, addr2;
327 struct rt *rt;
329 switch(opt) {
330 case 'f': /* FALLTHROUGH */
331 case 'g': /* FALLTHROUGH */
332 case 'n': /* FALLTHROUGH */
333 case 'x': /* FALLTHROUGH */
334 case 'T': /* We need to handle non interface options */
335 break;
336 case 'b':
337 ifo->options |= DHCPCD_BACKGROUND;
338 break;
339 case 'c':
340 strlcpy(ifo->script, arg, sizeof(ifo->script));
341 break;
342 case 'd':
343 ifo->options |= DHCPCD_DEBUG;
344 break;
345 case 'e':
346 add_environ(ifo, arg, 1);
347 break;
348 case 'h':
349 if (arg) {
350 s = parse_string(ifo->hostname,
351 HOSTNAME_MAX_LEN, arg);
352 if (s == -1) {
353 syslog(LOG_ERR, "hostname: %m");
354 return -1;
356 if (s != 0 && ifo->hostname[0] == '.') {
357 syslog(LOG_ERR,
358 "hostname cannot begin with .");
359 return -1;
361 ifo->hostname[s] = '\0';
363 if (ifo->hostname[0] == '\0')
364 ifo->options &= ~DHCPCD_HOSTNAME;
365 else
366 ifo->options |= DHCPCD_HOSTNAME;
367 break;
368 case 'i':
369 if (arg)
370 s = parse_string((char *)ifo->vendorclassid + 1,
371 VENDORCLASSID_MAX_LEN, arg);
372 else
373 s = 0;
374 if (s == -1) {
375 syslog(LOG_ERR, "vendorclassid: %m");
376 return -1;
378 *ifo->vendorclassid = (uint8_t)s;
379 break;
380 case 'k':
381 ifo->options |= DHCPCD_RELEASE;
382 break;
383 case 'l':
384 if (*arg == '-') {
385 syslog(LOG_ERR,
386 "leasetime must be a positive value");
387 return -1;
389 errno = 0;
390 ifo->leasetime = (uint32_t)strtol(arg, NULL, 0);
391 if (errno == EINVAL || errno == ERANGE) {
392 syslog(LOG_ERR, "`%s' out of range", arg);
393 return -1;
395 break;
396 case 'm':
397 ifo->metric = atoint(arg);
398 if (ifo->metric < 0) {
399 syslog(LOG_ERR, "metric must be a positive value");
400 return -1;
402 break;
403 case 'o':
404 if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
405 syslog(LOG_ERR, "unknown option `%s'", arg);
406 return -1;
408 break;
409 case 'p':
410 ifo->options |= DHCPCD_PERSISTENT;
411 break;
412 case 'q':
413 ifo->options |= DHCPCD_QUIET;
414 break;
415 case 'r':
416 ifo->options |= DHCPCD_REQUEST;
417 if (parse_addr(&ifo->req_addr, NULL, arg) != 0)
418 return -1;
419 ifo->req_mask.s_addr = 0;
420 break;
421 case 's':
422 ifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;
423 ifo->options &= ~(DHCPCD_ARP | DHCPCD_STATIC);
424 if (arg && *arg != '\0') {
425 if (parse_addr(&ifo->req_addr, &ifo->req_mask,
426 arg) != 0)
427 return -1;
428 } else {
429 ifo->req_addr.s_addr = 0;
430 ifo->req_mask.s_addr = 0;
432 break;
433 case 't':
434 ifo->timeout = atoint(arg);
435 if (ifo->timeout < 0) {
436 syslog(LOG_ERR, "timeout must be a positive value");
437 return -1;
439 break;
440 case 'u':
441 s = USERCLASS_MAX_LEN - ifo->userclass[0] - 1;
442 s = parse_string((char *)ifo->userclass +
443 ifo->userclass[0] + 2,
444 s, arg);
445 if (s == -1) {
446 syslog(LOG_ERR, "userclass: %m");
447 return -1;
449 if (s != 0) {
450 ifo->userclass[ifo->userclass[0] + 1] = s;
451 ifo->userclass[0] += s + 1;
453 break;
454 case 'v':
455 p = strchr(arg, ',');
456 if (!p || !p[1]) {
457 syslog(LOG_ERR, "invalid vendor format");
458 return -1;
461 /* If vendor starts with , then it is not encapsulated */
462 if (p == arg) {
463 arg++;
464 s = parse_string((char *)ifo->vendor + 1,
465 VENDOR_MAX_LEN, arg);
466 if (s == -1) {
467 syslog(LOG_ERR, "vendor: %m");
468 return -1;
470 ifo->vendor[0] = (uint8_t)s;
471 ifo->options |= DHCPCD_VENDORRAW;
472 break;
475 /* Encapsulated vendor options */
476 if (ifo->options & DHCPCD_VENDORRAW) {
477 ifo->options &= ~DHCPCD_VENDORRAW;
478 ifo->vendor[0] = 0;
481 *p = '\0';
482 i = atoint(arg);
483 arg = p + 1;
484 if (i < 1 || i > 254) {
485 syslog(LOG_ERR, "vendor option should be between"
486 " 1 and 254 inclusive");
487 return -1;
489 s = VENDOR_MAX_LEN - ifo->vendor[0] - 2;
490 if (inet_aton(arg, &addr) == 1) {
491 if (s < 6) {
492 s = -1;
493 errno = ENOBUFS;
494 } else
495 memcpy(ifo->vendor + ifo->vendor[0] + 3,
496 &addr.s_addr, sizeof(addr.s_addr));
497 } else {
498 s = parse_string((char *)ifo->vendor +
499 ifo->vendor[0] + 3, s, arg);
501 if (s == -1) {
502 syslog(LOG_ERR, "vendor: %m");
503 return -1;
505 if (s != 0) {
506 ifo->vendor[ifo->vendor[0] + 1] = i;
507 ifo->vendor[ifo->vendor[0] + 2] = s;
508 ifo->vendor[0] += s + 2;
510 break;
511 case 'w':
512 ifo->options |= DHCPCD_WAITIP;
513 break;
514 case 'y':
515 ifo->reboot = atoint(arg);
516 if (ifo->reboot < 0) {
517 syslog(LOG_ERR, "reboot must be a positive value");
518 return -1;
520 break;
521 case 'z':
522 /* We only set this if we haven't got any interfaces */
523 if (!ifaces)
524 ifav = splitv(&ifac, ifav, arg);
525 break;
526 case 'A':
527 ifo->options &= ~DHCPCD_ARP;
528 /* IPv4LL requires ARP */
529 ifo->options &= ~DHCPCD_IPV4LL;
530 break;
531 case 'B':
532 ifo->options &= ~DHCPCD_DAEMONISE;
533 break;
534 case 'C':
535 /* Commas to spaces for shell */
536 while ((p = strchr(arg, ',')))
537 *p = ' ';
538 s = strlen("skip_hooks=") + strlen(arg) + 1;
539 p = xmalloc(sizeof(char) * s);
540 snprintf(p, s, "skip_hooks=%s", arg);
541 add_environ(ifo, p, 0);
542 free(p);
543 break;
544 case 'D':
545 ifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;
546 break;
547 case 'E':
548 ifo->options |= DHCPCD_LASTLEASE;
549 break;
550 case 'F':
551 if (!arg) {
552 ifo->fqdn = FQDN_BOTH;
553 break;
555 if (strcmp(arg, "none") == 0)
556 ifo->fqdn = FQDN_NONE;
557 else if (strcmp(arg, "ptr") == 0)
558 ifo->fqdn = FQDN_PTR;
559 else if (strcmp(arg, "both") == 0)
560 ifo->fqdn = FQDN_BOTH;
561 else if (strcmp(arg, "disable") == 0)
562 ifo->fqdn = FQDN_DISABLE;
563 else {
564 syslog(LOG_ERR, "invalid value `%s' for FQDN", arg);
565 return -1;
567 break;
568 case 'G':
569 ifo->options &= ~DHCPCD_GATEWAY;
570 break;
571 case 'I':
572 /* Strings have a type of 0 */;
573 ifo->clientid[1] = 0;
574 if (arg)
575 s = parse_string_hwaddr((char *)ifo->clientid + 1,
576 CLIENTID_MAX_LEN, arg, 1);
577 else
578 s = 0;
579 if (s == -1) {
580 syslog(LOG_ERR, "clientid: %m");
581 return -1;
583 ifo->options |= DHCPCD_CLIENTID;
584 ifo->clientid[0] = (uint8_t)s;
585 break;
586 case 'K':
587 ifo->options &= ~DHCPCD_LINK;
588 break;
589 case 'L':
590 ifo->options &= ~DHCPCD_IPV4LL;
591 break;
592 case 'N':
593 if (make_option_mask(ifo->dstmask, arg, 2) != 0) {
594 if (errno == EINVAL)
595 syslog(LOG_ERR, "option `%s' does not take"
596 " an IPv4 address", arg);
597 else
598 syslog(LOG_ERR, "unknown otpion `%s'", arg);
599 return -1;
601 break;
602 case 'O':
603 if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
604 make_option_mask(ifo->requiremask, arg, -1) != 0 ||
605 make_option_mask(ifo->nomask, arg, 1) != 0)
607 syslog(LOG_ERR, "unknown option `%s'", arg);
608 return -1;
610 break;
611 case 'Q':
612 if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
613 make_option_mask(ifo->requestmask, arg, 1) != 0)
615 syslog(LOG_ERR, "unknown option `%s'", arg);
616 return -1;
618 break;
619 case 'S':
620 p = strchr(arg, '=');
621 if (p == NULL) {
622 syslog(LOG_ERR, "static assignment required");
623 return -1;
625 p++;
626 if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
627 if (parse_addr(&ifo->req_addr, &ifo->req_mask, p) != 0)
628 return -1;
630 ifo->options |= DHCPCD_STATIC;
631 ifo->options &= ~DHCPCD_INFORM;
632 } else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
633 strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 ||
634 strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 ||
635 strncmp(arg, "ms_classless_static_routes=", strlen("ms_classless_static_routes=")) == 0)
637 np = strchr(p, ' ');
638 if (np == NULL) {
639 syslog(LOG_ERR, "all routes need a gateway");
640 return -1;
642 *np++ = '\0';
643 while (*np == ' ')
644 np++;
645 if (ifo->routes == NULL) {
646 rt = ifo->routes = xmalloc(sizeof(*rt));
647 } else {
648 rt = ifo->routes;
649 while (rt->next)
650 rt = rt->next;
651 rt->next = xmalloc(sizeof(*rt));
652 rt = rt->next;
654 rt->next = NULL;
655 if (parse_addr(&rt->dest, &rt->net, p) == -1 ||
656 parse_addr(&rt->gate, NULL, np) == -1)
657 return -1;
658 } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
659 if (ifo->routes == NULL) {
660 rt = ifo->routes = xzalloc(sizeof(*rt));
661 } else {
662 rt = ifo->routes;
663 while (rt->next)
664 rt = rt->next;
665 rt->next = xmalloc(sizeof(*rt));
666 rt = rt->next;
668 rt->dest.s_addr = INADDR_ANY;
669 rt->net.s_addr = INADDR_ANY;
670 rt->next = NULL;
671 if (parse_addr(&rt->gate, NULL, p) == -1)
672 return -1;
673 } else {
674 s = 0;
675 if (ifo->config != NULL) {
676 while (ifo->config[s] != NULL) {
677 if (strncmp(ifo->config[s], arg,
678 p - arg) == 0)
680 free(ifo->config[s]);
681 ifo->config[s] = xstrdup(arg);
682 return 1;
684 s++;
687 ifo->config = xrealloc(ifo->config,
688 sizeof(char *) * (s + 2));
689 ifo->config[s] = xstrdup(arg);
690 ifo->config[s + 1] = NULL;
692 break;
693 case 'W':
694 if (parse_addr(&addr, &addr2, arg) != 0)
695 return -1;
696 if (strchr(arg, '/') == NULL)
697 addr2.s_addr = INADDR_BROADCAST;
698 ifo->whitelist = xrealloc(ifo->whitelist,
699 sizeof(in_addr_t) * (ifo->whitelist_len + 2));
700 ifo->whitelist[ifo->whitelist_len++] = addr.s_addr;
701 ifo->whitelist[ifo->whitelist_len++] = addr2.s_addr;
702 break;
703 case 'X':
704 if (parse_addr(&addr, &addr2, arg) != 0)
705 return -1;
706 if (strchr(arg, '/') == NULL)
707 addr2.s_addr = INADDR_BROADCAST;
708 ifo->blacklist = xrealloc(ifo->blacklist,
709 sizeof(in_addr_t) * (ifo->blacklist_len + 2));
710 ifo->blacklist[ifo->blacklist_len++] = addr.s_addr;
711 ifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;
712 break;
713 case 'Z':
714 /* We only set this if we haven't got any interfaces */
715 if (!ifaces)
716 ifdv = splitv(&ifdc, ifdv, arg);
717 break;
718 case O_ARPING:
719 if (parse_addr(&addr, NULL, arg) != 0)
720 return -1;
721 ifo->arping = xrealloc(ifo->arping,
722 sizeof(in_addr_t) * (ifo->arping_len + 1));
723 ifo->arping[ifo->arping_len++] = addr.s_addr;
724 break;
725 case O_FALLBACK:
726 free(ifo->fallback);
727 ifo->fallback = xstrdup(arg);
728 break;
729 default:
730 return 0;
733 return 1;
736 static int
737 parse_config_line(struct if_options *ifo, const char *opt, char *line)
739 unsigned int i;
741 for (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {
742 if (!cf_options[i].name ||
743 strcmp(cf_options[i].name, opt) != 0)
744 continue;
746 if (cf_options[i].has_arg == required_argument && !line) {
747 fprintf(stderr,
748 PACKAGE ": option requires an argument -- %s\n",
749 opt);
750 return -1;
753 return parse_option(ifo, cf_options[i].val, line);
756 fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
757 return -1;
760 struct if_options *
761 read_config(const char *file,
762 const char *ifname, const char *ssid, const char *profile)
764 struct if_options *ifo;
765 FILE *f;
766 char *line, *option, *p;
767 int skip = 0, have_profile = 0;
769 /* Seed our default options */
770 ifo = xzalloc(sizeof(*ifo));
771 ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
772 ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
773 ifo->timeout = DEFAULT_TIMEOUT;
774 ifo->reboot = DEFAULT_REBOOT;
775 ifo->metric = -1;
776 strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
777 gethostname(ifo->hostname, HOSTNAME_MAX_LEN);
778 /* Ensure that the hostname is NULL terminated */
779 ifo->hostname[HOSTNAME_MAX_LEN] = '\0';
780 if (strcmp(ifo->hostname, "(none)") == 0 ||
781 strcmp(ifo->hostname, "localhost") == 0)
782 ifo->hostname[0] = '\0';
783 ifo->vendorclassid[0] = snprintf((char *)ifo->vendorclassid + 1,
784 VENDORCLASSID_MAX_LEN,
785 "%s %s", PACKAGE, VERSION);
787 /* Parse our options file */
788 f = fopen(file ? file : CONFIG, "r");
789 if (f == NULL) {
790 if (file != NULL)
791 syslog(LOG_ERR, "fopen `%s': %m", file);
792 return ifo;
795 while ((line = get_line(f))) {
796 option = strsep(&line, " \t");
797 /* Trim trailing whitespace */
798 if (line && *line) {
799 p = line + strlen(line) - 1;
800 while (p != line &&
801 (*p == ' ' || *p == '\t') &&
802 *(p - 1) != '\\')
803 *p-- = '\0';
805 /* Start of an interface block, skip if not ours */
806 if (strcmp(option, "interface") == 0) {
807 if (ifname && line && strcmp(line, ifname) == 0)
808 skip = 0;
809 else
810 skip = 1;
811 continue;
813 /* Start of an ssid block, skip if not ours */
814 if (strcmp(option, "ssid") == 0) {
815 if (ssid && line && strcmp(line, ssid) == 0)
816 skip = 0;
817 else
818 skip = 1;
819 continue;
821 /* Start of a profile block, skip if not ours */
822 if (strcmp(option, "profile") == 0) {
823 if (profile && line && strcmp(line, profile) == 0) {
824 skip = 0;
825 have_profile = 1;
826 } else
827 skip = 1;
828 continue;
830 if (skip)
831 continue;
832 if (parse_config_line(ifo, option, line) != 1)
833 break;
835 fclose(f);
837 if (profile && !have_profile) {
838 free_options(ifo);
839 errno = ENOENT;
840 ifo = NULL;
843 /* Terminate the encapsulated options */
844 if (ifo && ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
845 ifo->vendor[0]++;
846 ifo->vendor[ifo->vendor[0]] = DHO_END;
848 return ifo;
852 add_options(struct if_options *ifo, int argc, char **argv)
854 int oi, opt, r = 1;
856 optind = 0;
857 while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
859 r = parse_option(ifo, opt, optarg);
860 if (r != 1)
861 break;
863 /* Terminate the encapsulated options */
864 if (r == 1 && ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {
865 ifo->vendor[0]++;
866 ifo->vendor[ifo->vendor[0]] = DHO_END;
868 return r;
871 void
872 free_options(struct if_options *ifo)
874 size_t i;
876 if (ifo) {
877 if (ifo->environ) {
878 i = 0;
879 while (ifo->environ[i])
880 free(ifo->environ[i++]);
881 free(ifo->environ);
883 if (ifo->config) {
884 i = 0;
885 while (ifo->config[i])
886 free(ifo->config[i++]);
887 free(ifo->config);
889 free_routes(ifo->routes);
890 free(ifo->arping);
891 free(ifo->blacklist);
892 free(ifo->fallback);
893 free(ifo);