aboutsummaryrefslogtreecommitdiffstats
path: root/dhcpcd.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:22 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:29:22 -0800
commitf7c5421560640d23fc10803b9d59a9ff1d83e467 (patch)
treed6ae69d0d3f4a4d760a3254ec326bca4a8afacfe /dhcpcd.c
parentd97c47cad830d00c9da685cc4ea157d6185f6c97 (diff)
downloadexternal_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.zip
external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.gz
external_dhcpcd-f7c5421560640d23fc10803b9d59a9ff1d83e467.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'dhcpcd.c')
-rw-r--r--dhcpcd.c1038
1 files changed, 1038 insertions, 0 deletions
diff --git a/dhcpcd.c b/dhcpcd.c
new file mode 100644
index 0000000..e674bd2
--- /dev/null
+++ b/dhcpcd.c
@@ -0,0 +1,1038 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright 2006-2008 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+const char copyright[] = "Copyright (c) 2006-2008 Roy Marples";
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "config.h"
+#include "client.h"
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "net.h"
+#include "logger.h"
+
+#ifdef ANDROID
+#include <linux/capability.h>
+#include <linux/prctl.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+#endif
+
+/* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+#define OPTS "bc:df:h:i:kl:m:no:pqr:s:t:u:v:xABC:DEF:GI:KLO:Q:TVX:"
+
+static int doversion = 0;
+static int dohelp = 0;
+static const struct option longopts[] = {
+ {"background", no_argument, NULL, 'b'},
+ {"script", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"config", required_argument, NULL, 'f'},
+ {"hostname", optional_argument, NULL, 'h'},
+ {"vendorclassid", optional_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"rebind", no_argument, NULL, 'n'},
+ {"option", required_argument, NULL, 'o'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"request", optional_argument, NULL, 'r'},
+ {"inform", optional_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"vendor", required_argument, NULL, 'v'},
+ {"exit", no_argument, NULL, 'x'},
+ {"noarp", no_argument, NULL, 'A'},
+ {"nobackground", no_argument, NULL, 'B'},
+ {"nohook", required_argument, NULL, 'C'},
+ {"duid", no_argument, NULL, 'D'},
+ {"lastlease", no_argument, NULL, 'E'},
+ {"fqdn", optional_argument, NULL, 'F'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"clientid", optional_argument, NULL, 'I'},
+ {"nolink", no_argument, NULL, 'K'},
+ {"noipv4ll", no_argument, NULL, 'L'},
+ {"nooption", optional_argument, NULL, 'O'},
+ {"require", required_argument, NULL, 'Q'},
+ {"test", no_argument, NULL, 'T'},
+ {"variables", no_argument, NULL, 'V'},
+ {"blacklist", required_argument, NULL, 'X'},
+ {"help", no_argument, &dohelp, 1},
+ {"version", no_argument, &doversion, 1},
+#ifdef CMDLINE_COMPAT
+ {"classid", optional_argument, NULL, 'i'},
+ {"renew", no_argument, NULL, 'n'},
+ {"nohostname", no_argument, NULL, 'H'},
+ {"nomtu", no_argument, NULL, 'M'},
+ {"nontp", no_argument, NULL, 'N'},
+ {"nodns", no_argument, NULL, 'R'},
+ {"msscr", no_argument, NULL, 'S'},
+ {"nonis", no_argument, NULL, 'Y'},
+#endif
+ {NULL, 0, NULL, '\0'}
+};
+
+#ifdef CMDLINE_COMPAT
+# define EXTRA_OPTS "HMNRSY"
+#endif
+
+#ifndef EXTRA_OPTS
+# define EXTRA_OPTS
+#endif
+
+static int
+atoint(const char *s)
+{
+ char *t;
+ long n;
+
+ errno = 0;
+ n = strtol(s, &t, 0);
+ if ((errno != 0 && n == 0) || s == t ||
+ (errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)))
+ {
+ logger(LOG_ERR, "`%s' out of range", s);
+ return -1;
+ }
+
+ return (int)n;
+}
+
+static pid_t
+read_pid(const char *pidfile)
+{
+ FILE *fp;
+ pid_t pid = 0;
+
+ if ((fp = fopen(pidfile, "r")) == NULL) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ fscanf(fp, "%d", &pid);
+ fclose(fp);
+
+ return pid;
+}
+
+static void
+usage(void)
+{
+ printf("usage: "PACKAGE" [-dknpqxADEGHKLOTV] [-c script] [-f file ] [-h hostname]\n"
+ " [-i classID ] [-l leasetime] [-m metric] [-o option] [-r ipaddr]\n"
+ " [-s ipaddr] [-t timeout] [-u userclass] [-F none|ptr|both]\n"
+ " [-I clientID] [-C hookscript] [-Q option] [-X ipaddr] <interface>\n");
+}
+
+static char *
+add_environ(struct options *options, const char *value, int uniq)
+{
+ char **newlist;
+ char **lst = options->environ;
+ size_t i = 0, l, lv;
+ char *match = NULL, *p;
+
+ match = xstrdup(value);
+ p = strchr(match, '=');
+ if (p)
+ *p++ = '\0';
+ l = strlen(match);
+
+ while (lst && lst[i]) {
+ if (match && strncmp(lst[i], match, l) == 0) {
+ if (uniq) {
+ free(lst[i]);
+ lst[i] = xstrdup(value);
+ } else {
+ /* Append a space and the value to it */
+ l = strlen(lst[i]);
+ lv = strlen(p);
+ lst[i] = xrealloc(lst[i], l + lv + 2);
+ lst[i][l] = ' ';
+ memcpy(lst[i] + l + 1, p, lv);
+ lst[i][l + lv + 1] = '\0';
+ }
+ free(match);
+ return lst[i];
+ }
+ i++;
+ }
+
+ newlist = xrealloc(lst, sizeof(char *) * (i + 2));
+ newlist[i] = xstrdup(value);
+ newlist[i + 1] = NULL;
+ options->environ = newlist;
+ free(match);
+ return newlist[i];
+}
+
+#define parse_string(buf, len, arg) parse_string_hwaddr(buf, len, arg, 0)
+static ssize_t
+parse_string_hwaddr(char *sbuf, ssize_t slen, char *str, int clid)
+{
+ ssize_t l;
+ char *p;
+ int i;
+ char c[4];
+
+ /* If surrounded by quotes then it's a string */
+ if (*str == '"') {
+ str++;
+ l = strlen(str);
+ p = str + l - 1;
+ if (*p == '"')
+ *p = '\0';
+ } else {
+ l = hwaddr_aton(NULL, str);
+ if (l > 1) {
+ if (l > slen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ hwaddr_aton((uint8_t *)sbuf, str);
+ return l;
+ }
+ }
+
+ /* Process escapes */
+ l = 0;
+ /* If processing a string on the clientid, first byte should be
+ * 0 to indicate a non hardware type */
+ if (clid) {
+ *sbuf++ = 0;
+ l++;
+ }
+ c[3] = '\0';
+ while (*str) {
+ if (++l > slen) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (*str == '\\') {
+ str++;
+ switch(*str++) {
+ case '\0':
+ break;
+ case 'b':
+ *sbuf++ = '\b';
+ break;
+ case 'n':
+ *sbuf++ = '\n';
+ break;
+ case 'r':
+ *sbuf++ = '\r';
+ break;
+ case 't':
+ *sbuf++ = '\t';
+ break;
+ case 'x':
+ /* Grab a hex code */
+ c[1] = '\0';
+ for (i = 0; i < 2; i++) {
+ if (isxdigit((unsigned char)*str) == 0)
+ break;
+ c[i] = *str++;
+ }
+ if (c[1] != '\0') {
+ c[2] = '\0';
+ *sbuf++ = strtol(c, NULL, 16);
+ } else
+ l--;
+ break;
+ case '0':
+ /* Grab an octal code */
+ c[2] = '\0';
+ for (i = 0; i < 3; i++) {
+ if (*str < '0' || *str > '7')
+ break;
+ c[i] = *str++;
+ }
+ if (c[2] != '\0') {
+ i = strtol(c, NULL, 8);
+ if (i > 255)
+ i = 255;
+ *sbuf ++= i;
+ } else
+ l--;
+ break;
+ default:
+ *sbuf++ = *str++;
+ }
+ } else
+ *sbuf++ = *str++;
+ }
+ return l;
+}
+
+static int
+parse_option(int opt, char *oarg, struct options *options)
+{
+ int i;
+ char *p;
+ ssize_t s;
+ struct in_addr addr;
+
+ switch(opt) {
+ case 'b':
+ options->options |= DHCPCD_BACKGROUND;
+ break;
+ case 'c':
+ strlcpy(options->script, oarg, sizeof(options->script));
+ break;
+ case 'h':
+ if (oarg)
+ s = parse_string(options->hostname + 1,
+ HOSTNAME_MAX_LEN, oarg);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "hostname: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0 && options->hostname[1] == '.') {
+ logger(LOG_ERR, "hostname cannot begin with a .");
+ return -1;
+ }
+ options->hostname[0] = (uint8_t)s;
+ break;
+ case 'i':
+ if (oarg)
+ s = parse_string((char *)options->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN, oarg);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "vendorclassid: %s", strerror(errno));
+ return -1;
+ }
+ *options->vendorclassid = (uint8_t)s;
+ break;
+ case 'l':
+ if (*oarg == '-') {
+ logger(LOG_ERR,
+ "leasetime must be a positive value");
+ return -1;
+ }
+ errno = 0;
+ options->leasetime = (uint32_t)strtol(oarg, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE) {
+ logger(LOG_ERR, "`%s' out of range", oarg);
+ return -1;
+ }
+ break;
+ case 'm':
+ options->metric = atoint(oarg);
+ if (options->metric < 0) {
+ logger(LOG_ERR, "metric must be a positive value");
+ return -1;
+ }
+ break;
+ case 'o':
+ if (make_option_mask(options->requestmask, &oarg, 1) != 0) {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'p':
+ options->options |= DHCPCD_PERSISTENT;
+ break;
+ case 'q':
+ setloglevel(LOG_WARNING);
+ break;
+ case 's':
+ options->options |= DHCPCD_INFORM;
+ options->options |= DHCPCD_PERSISTENT;
+ options->options &= ~DHCPCD_ARP;
+ if (!oarg || *oarg == '\0') {
+ options->request_address.s_addr = 0;
+ break;
+ } else {
+ if ((p = strchr(oarg, '/'))) {
+ /* nullify the slash, so the -r option
+ * can read the address */
+ *p++ = '\0';
+ if (sscanf(p, "%d", &i) != 1 ||
+ inet_cidrtoaddr(i, &options->request_netmask) != 0)
+ {
+ logger(LOG_ERR,
+ "`%s' is not a valid CIDR",
+ p);
+ return -1;
+ }
+ }
+ }
+ /* FALLTHROUGH */
+ case 'r':
+ if (!(options->options & DHCPCD_INFORM))
+ options->options |= DHCPCD_REQUEST;
+ if (oarg && !inet_aton(oarg, &options->request_address)) {
+ logger(LOG_ERR, "`%s' is not a valid IP address",
+ oarg);
+ return -1;
+ }
+ break;
+ case 't':
+ options->timeout = atoint(oarg);
+ if (options->timeout < 0) {
+ logger (LOG_ERR, "timeout must be a positive value");
+ return -1;
+ }
+ break;
+ case 'u':
+ s = USERCLASS_MAX_LEN - options->userclass[0] - 1;
+ s = parse_string((char *)options->userclass + options->userclass[0] + 2,
+ s, oarg);
+ if (s == -1) {
+ logger(LOG_ERR, "userclass: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0) {
+ options->userclass[options->userclass[0] + 1] = s;
+ options->userclass[0] += s + 1;
+ }
+ break;
+ case 'v':
+ p = strchr(oarg, ',');
+ if (!p || !p[1]) {
+ logger(LOG_ERR, "invalid vendor format");
+ return -1;
+ }
+ *p = '\0';
+ i = atoint(oarg);
+ oarg = p + 1;
+ if (i < 1 || i > 254) {
+ logger(LOG_ERR, "vendor option should be between"
+ " 1 and 254 inclusive");
+ return -1;
+ }
+ s = VENDOR_MAX_LEN - options->vendor[0] - 2;
+ if (inet_aton(oarg, &addr) == 1) {
+ if (s < 6) {
+ s = -1;
+ errno = ENOBUFS;
+ } else
+ memcpy(options->vendor + options->vendor[0] + 3,
+ &addr.s_addr, sizeof(addr.s_addr));
+ } else {
+ s = parse_string((char *)options->vendor + options->vendor[0] + 3,
+ s, oarg);
+ }
+ if (s == -1) {
+ logger(LOG_ERR, "vendor: %s", strerror(errno));
+ return -1;
+ }
+ if (s != 0) {
+ options->vendor[options->vendor[0] + 1] = i;
+ options->vendor[options->vendor[0] + 2] = s;
+ options->vendor[0] += s + 2;
+ }
+ break;
+ case 'A':
+ options->options &= ~DHCPCD_ARP;
+ /* IPv4LL requires ARP */
+ options->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'B':
+ options->options &= ~DHCPCD_DAEMONISE;
+ break;
+ case 'C':
+ /* Commas to spaces for shell */
+ while ((p = strchr(oarg, ',')))
+ *p = ' ';
+ s = strlen("skip_hooks=") + strlen(oarg) + 1;
+ p = xmalloc(sizeof(char) * s);
+ snprintf(p, s, "skip_hooks=%s", oarg);
+ add_environ(options, p, 0);
+ free(p);
+ break;
+ case 'D':
+ options->options |= DHCPCD_DUID;
+ break;
+ case 'E':
+ options->options |= DHCPCD_LASTLEASE;
+ break;
+ case 'F':
+ if (!oarg) {
+ options->fqdn = FQDN_BOTH;
+ break;
+ }
+ if (strcmp(oarg, "none") == 0)
+ options->fqdn = FQDN_NONE;
+ else if (strcmp(oarg, "ptr") == 0)
+ options->fqdn = FQDN_PTR;
+ else if (strcmp(oarg, "both") == 0)
+ options->fqdn = FQDN_BOTH;
+ else if (strcmp(oarg, "disable") == 0)
+ options->fqdn = FQDN_DISABLE;
+ else {
+ logger(LOG_ERR, "invalid value `%s' for FQDN",
+ oarg);
+ return -1;
+ }
+ break;
+ case 'G':
+ options->options &= ~DHCPCD_GATEWAY;
+ break;
+ case 'I':
+ /* Strings have a type of 0 */;
+ options->clientid[1] = 0;
+ if (oarg)
+ s = parse_string_hwaddr((char *)options->clientid + 1,
+ CLIENTID_MAX_LEN, oarg, 1);
+ else
+ s = 0;
+ if (s == -1) {
+ logger(LOG_ERR, "clientid: %s", strerror(errno));
+ return -1;
+ }
+ options->clientid[0] = (uint8_t)s;
+ if (s == 0) {
+ options->options &= ~DHCPCD_DUID;
+ options->options &= ~DHCPCD_CLIENTID;
+ }
+ break;
+ case 'K':
+ options->options &= ~DHCPCD_LINK;
+ break;
+ case 'L':
+ options->options &= ~DHCPCD_IPV4LL;
+ break;
+ case 'O':
+ if (make_option_mask(options->requestmask, &oarg, -1) != 0 ||
+ make_option_mask(options->requiremask, &oarg, -1) != 0 ||
+ make_option_mask(options->nomask, &oarg, 1) != 0)
+ {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'Q':
+ if (make_option_mask(options->requiremask, &oarg, 1) != 0 ||
+ make_option_mask(options->requestmask, &oarg, 1) != 0)
+ {
+ logger(LOG_ERR, "unknown option `%s'", oarg);
+ return -1;
+ }
+ break;
+ case 'X':
+ if (!inet_aton(oarg, &addr)) {
+ logger(LOG_ERR, "`%s' is not a valid IP address",
+ oarg);
+ return -1;
+ }
+ options->blacklist = xrealloc(options->blacklist,
+ sizeof(in_addr_t) * (options->blacklist_len + 1));
+ options->blacklist[options->blacklist_len] = addr.s_addr;
+ options->blacklist_len++;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+parse_config_line(const char *opt, char *line, struct options *options)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(longopts) / sizeof(longopts[0]); i++) {
+ if (!longopts[i].name ||
+ strcmp(longopts[i].name, opt) != 0)
+ continue;
+
+ if (longopts[i].has_arg == required_argument && !line) {
+ fprintf(stderr,
+ PACKAGE ": option requires an argument -- %s\n",
+ opt);
+ return -1;
+ }
+
+ return parse_option(longopts[i].val, line, options);
+ }
+
+ fprintf(stderr, PACKAGE ": unknown option -- %s\n", opt);
+ return -1;
+}
+
+#ifdef ANDROID
+void switchUser() {
+ gid_t groups[] = { AID_INET, AID_SHELL };
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+ prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+ setgid(AID_DHCP);
+ setuid(AID_DHCP);
+
+ struct __user_cap_header_struct header;
+ struct __user_cap_data_struct cap;
+ header.version = _LINUX_CAPABILITY_VERSION;
+ header.pid = 0;
+ cap.effective = cap.permitted =
+ (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) |
+ (1 << CAP_NET_BROADCAST) | (1 << CAP_NET_BIND_SERVICE);
+ cap.inheritable = 0;
+ capset(&header, &cap);
+}
+#endif /* ANDROID */
+
+int
+main(int argc, char **argv)
+{
+ struct options *options;
+ int opt;
+ int option_index = 0;
+ char *prefix;
+ pid_t pid;
+ int debug = 0;
+ int i, r;
+ int pid_fd = -1;
+ int sig = 0;
+ int retval = EXIT_FAILURE;
+ char *line, *option, *p, *buffer = NULL;
+ size_t len = 0;
+ FILE *f;
+ char *cf = NULL;
+ char *intf = NULL;
+ struct timespec ts;
+
+#ifdef ANDROID
+ switchUser();
+#endif
+ closefrom(3);
+ /* Saves calling fflush(stream) in the logger */
+ setlinebuf(stdout);
+ openlog(PACKAGE, LOG_PID, LOG_LOCAL0);
+ setlogprefix(PACKAGE ": ");
+
+ options = xzalloc(sizeof(*options));
+ options->options |= DHCPCD_CLIENTID | DHCPCD_GATEWAY | DHCPCD_DAEMONISE;
+ options->options |= DHCPCD_ARP | DHCPCD_IPV4LL | DHCPCD_LINK;
+ options->timeout = DEFAULT_TIMEOUT;
+ strlcpy(options->script, SCRIPT, sizeof(options->script));
+
+ options->vendorclassid[0] = snprintf((char *)options->vendorclassid + 1,
+ VENDORCLASSID_MAX_LEN,
+ "%s %s", PACKAGE, VERSION);
+
+#ifdef CMDLINE_COMPAT
+ add_option_mask(options->requestmask, DHO_DNSSERVER);
+ add_option_mask(options->requestmask, DHO_DNSDOMAIN);
+ add_option_mask(options->requestmask, DHO_DNSSEARCH);
+ add_option_mask(options->requestmask, DHO_NISSERVER);
+ add_option_mask(options->requestmask, DHO_NISDOMAIN);
+ add_option_mask(options->requestmask, DHO_NTPSERVER);
+
+ /* If the duid file exists, then enable duid by default
+ * This means we don't break existing clients that easily :) */
+ if ((f = fopen(DUID, "r"))) {
+ options->options |= DHCPCD_DUID;
+ fclose(f);
+ }
+#endif
+
+ gethostname(options->hostname + 1, sizeof(options->hostname));
+ if (strcmp(options->hostname + 1, "(none)") == 0 ||
+ strcmp(options->hostname + 1, "localhost") == 0)
+ options->hostname[1] = '\0';
+ *options->hostname = strlen(options->hostname + 1);
+
+ while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+ longopts, &option_index)) != -1)
+ {
+ switch (opt) {
+ case 0:
+ if (longopts[option_index].flag)
+ break;
+ logger(LOG_ERR, "option `%s' should set a flag",
+ longopts[option_index].name);
+ goto abort;
+ case 'f':
+ cf = optarg;
+ break;
+ case 'V':
+ print_options();
+ goto abort;
+ case '?':
+ usage();
+ goto abort;
+ }
+ }
+
+ if (doversion)
+ printf(""PACKAGE" "VERSION"\n%s\n", copyright);
+
+ if (dohelp)
+ usage();
+
+ if (optind < argc) {
+ if (strlen(argv[optind]) >= IF_NAMESIZE) {
+ logger(LOG_ERR,
+ "`%s' too long for an interface name (max=%d)",
+ argv[optind], IF_NAMESIZE);
+ goto abort;
+ }
+ strlcpy(options->interface, argv[optind],
+ sizeof(options->interface));
+ } else {
+ /* If only version was requested then exit now */
+ if (doversion || dohelp) {
+ retval = 0;
+ goto abort;
+ }
+
+ logger(LOG_ERR, "no interface specified");
+ goto abort;
+ }
+
+ /* Parse our options file */
+ f = fopen(cf ? cf : CONFIG, "r");
+ if (f) {
+ r = 1;
+ while ((get_line(&buffer, &len, f))) {
+ line = buffer;
+ while ((option = strsep(&line, " \t")))
+ if (*option != '\0')
+ break;
+ if (!option || *option == '\0' || *option == '#')
+ continue;
+ /* Trim leading whitespace */
+ if (line) {
+ while (*line != '\0' && (*line == ' ' || *line == '\t'))
+ line++;
+ }
+ /* Trim trailing whitespace */
+ if (line && *line) {
+ p = line + strlen(line) - 1;
+ while (p != line &&
+ (*p == ' ' || *p == '\t') &&
+ *(p - 1) != '\\')
+ *p-- = '\0';
+ }
+ if (strcmp(option, "interface") == 0) {
+ free(intf);
+ intf = xstrdup(line);
+ continue;
+ }
+ /* If we're in an interface block don't use these
+ * options unless it's for us */
+ if (intf && strcmp(intf, options->interface) != 0)
+ continue;
+ r = parse_config_line(option, line, options);
+ if (r != 1)
+ break;
+ }
+ free(buffer);
+ free(intf);
+ fclose(f);
+ if (r == 0)
+ usage();
+ if (r != 1)
+ goto abort;
+ } else {
+ if (errno != ENOENT || cf) {
+ logger(LOG_ERR, "fopen `%s': %s", cf ? cf : CONFIG,
+ strerror(errno));
+ goto abort;
+ }
+ }
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS,
+ longopts, &option_index)) != -1)
+ {
+ switch (opt) {
+ case 'd':
+ debug++;
+ switch (debug) {
+ case 1:
+ setloglevel(LOG_DEBUG);
+ break;
+ case 2:
+ options->options &= ~DHCPCD_DAEMONISE;
+ break;
+ }
+ break;
+ case 'f':
+ break;
+ case 'k':
+ sig = SIGHUP;
+ break;
+ case 'n':
+ sig = SIGALRM;
+ break;
+ case 'x':
+ sig = SIGTERM;
+ break;
+ case 'T':
+ options->options |= DHCPCD_TEST | DHCPCD_PERSISTENT;
+ break;
+#ifdef CMDLINE_COMPAT
+ case 'H': /* FALLTHROUGH */
+ case 'M':
+ del_option_mask(options->requestmask, DHO_MTU);
+ break;
+ case 'N':
+ del_option_mask(options->requestmask, DHO_NTPSERVER);
+ break;
+ case 'R':
+ del_option_mask(options->requestmask, DHO_DNSSERVER);
+ del_option_mask(options->requestmask, DHO_DNSDOMAIN);
+ del_option_mask(options->requestmask, DHO_DNSSEARCH);
+ break;
+ case 'S':
+ add_option_mask(options->requestmask, DHO_MSCSR);
+ break;
+ case 'Y':
+ del_option_mask(options->requestmask, DHO_NISSERVER);
+ del_option_mask(options->requestmask, DHO_NISDOMAIN);
+ break;
+#endif
+ default:
+ i = parse_option(opt, optarg, options);
+ if (i == 1)
+ break;
+ if (i == 0)
+ usage();
+ goto abort;
+ }
+ }
+
+#ifdef THERE_IS_NO_FORK
+ options->options &= ~DHCPCD_DAEMONISE;
+#endif
+
+#ifndef ANDROID
+ /* android runs us as user "dhcp" */
+ if (geteuid())
+ logger(LOG_WARNING, PACKAGE " will not work correctly unless"
+ " run as root");
+#endif
+
+ if (options->options & DHCPCD_TEST) {
+ if (options->options & DHCPCD_REQUEST ||
+ options->options & DHCPCD_INFORM) {
+ logger(LOG_ERR,
+ "cannot test with --inform or --request");
+ goto abort;
+ }
+
+ if (options->options & DHCPCD_LASTLEASE) {
+ logger(LOG_ERR, "cannot test with --lastlease");
+ goto abort;
+ }
+
+ if (sig != 0) {
+ logger(LOG_ERR,
+ "cannot test with --release or --renew");
+ goto abort;
+ }
+ }
+
+ prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3));
+ snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface);
+ setlogprefix(prefix);
+ snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE,
+ options->interface);
+ free(prefix);
+
+ if (options->request_address.s_addr == 0 &&
+ (options->options & DHCPCD_INFORM ||
+ options->options & DHCPCD_REQUEST))
+ {
+ errno = 0;
+ if (get_address(options->interface,
+ &options->request_address,
+ &options->request_netmask) != 1)
+ {
+ if (errno)
+ logger(LOG_ERR, "get_address: %s",
+ strerror(errno));
+ else
+ logger(LOG_ERR, "no existing address");
+ goto abort;
+ }
+ }
+ if (IN_LINKLOCAL(ntohl(options->request_address.s_addr))) {
+ logger(LOG_ERR,
+ "you are not allowed to request a link local address");
+ goto abort;
+ }
+
+ chdir("/");
+ umask(022);
+
+ if (sig != 0 && !(options->options & DHCPCD_DAEMONISED)) {
+#ifdef ANDROID
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
+
+ i = -1;
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+ goto abort;
+ }
+ property_get(pidpropname, pidpropval, NULL);
+ if (strlen(pidpropval) == 0) {
+ goto abort;
+ }
+ pid = atoi(pidpropval);
+#else
+ i = -1;
+ pid = read_pid(options->pidfile);
+#endif
+ if (pid != 0)
+ logger(LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+
+ if (!pid || (i = kill(pid, sig))) {
+ if (sig != SIGALRM)
+ logger(LOG_ERR, ""PACKAGE" not running");
+ unlink(options->pidfile);
+ }
+ if (i == 0) {
+ if (sig == SIGALRM) {
+ retval = EXIT_SUCCESS;
+ goto abort;
+ }
+ /* Spin until it exits */
+ logger(LOG_INFO, "waiting for pid %d to exit", pid);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000; /* 10th of a second */
+ for(i = 0; i < 100; i++) {
+ nanosleep(&ts, NULL);
+ if (read_pid(options->pidfile) == 0) {
+ retval = EXIT_SUCCESS;
+ break;
+ }
+ }
+ if (retval != EXIT_SUCCESS)
+ logger(LOG_ERR, "pid %d failed to exit", pid);
+ goto abort;
+ }
+ if (sig != SIGALRM)
+ goto abort;
+ }
+
+ if (!(options->options & DHCPCD_TEST) &&
+ !(options->options & DHCPCD_DAEMONISED))
+ {
+#ifdef ANDROID
+ char pidpropname[PROPERTY_KEY_MAX];
+ char pidpropval[PROPERTY_VALUE_MAX];
+#endif
+#ifndef ANDROID
+ if ((pid = read_pid(options->pidfile)) > 0 &&
+ kill(pid, 0) == 0)
+ {
+ logger(LOG_ERR, ""PACKAGE
+ " already running on pid %d (%s)",
+ pid, options->pidfile);
+ goto abort;
+ }
+#endif
+ pid_fd = open(options->pidfile,
+ O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pid_fd == -1) {
+ logger(LOG_ERR, "open `%s': %s",
+ options->pidfile, strerror(errno));
+ goto abort;
+ }
+
+ /* Lock the file so that only one instance of dhcpcd runs
+ * on an interface */
+ if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) {
+ logger(LOG_ERR, "flock `%s': %s",
+ options->pidfile, strerror(errno));
+ goto abort;
+ }
+
+ if (set_cloexec(pid_fd) == -1)
+ goto abort;
+#ifdef ANDROID
+ if (snprintf(pidpropname,
+ sizeof(pidpropname),
+ "dhcp.%s.pid", options->interface) >= PROPERTY_KEY_MAX) {
+ goto abort;
+ }
+ if (snprintf(pidpropval, sizeof(pidpropval), "%d", getpid()) >= PROPERTY_VALUE_MAX) {
+ goto abort;
+ }
+ property_set(pidpropname, pidpropval);
+#else
+ writepid(pid_fd, getpid());
+#endif
+ logger(LOG_INFO, PACKAGE " " VERSION " starting");
+ }
+
+ /* Terminate the encapsulated options */
+ if (options->vendor[0]) {
+ options->vendor[0]++;
+ options->vendor[options->vendor[0]] = DHO_END;
+ }
+
+ if (dhcp_run(options, &pid_fd) == 0)
+ retval = EXIT_SUCCESS;
+
+abort:
+ /* If we didn't daemonise then we need to punt the pidfile now */
+ if (pid_fd > -1) {
+ close(pid_fd);
+ unlink(options->pidfile);
+ }
+ if (options->environ) {
+ len = 0;
+ while (options->environ[len])
+ free(options->environ[len++]);
+ free(options->environ);
+ }
+ free(options->blacklist);
+ free(options);
+ exit(retval);
+ /* NOTREACHED */
+}