summaryrefslogtreecommitdiffstats
path: root/src/customdhcpcd/dhcpcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/customdhcpcd/dhcpcd.c')
-rw-r--r--src/customdhcpcd/dhcpcd.c671
1 files changed, 671 insertions, 0 deletions
diff --git a/src/customdhcpcd/dhcpcd.c b/src/customdhcpcd/dhcpcd.c
new file mode 100644
index 0000000..d0ad5e7
--- /dev/null
+++ b/src/customdhcpcd/dhcpcd.c
@@ -0,0 +1,671 @@
+ /* 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/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "config.h"
+#include "client.h"
+#include "dhcpcd.h"
+#include "dhcp.h"
+#include "interface.h"
+#include "logger.h"
+#include "socket.h"
+#include "version.h"
+
+#include "logwriter.h"
+
+static int doversion = 0;
+static int dohelp = 0;
+#define EXTRA_OPTS
+static const struct option longopts[] = {
+ {"arp", no_argument, NULL, 'a'},
+ {"script", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'd'},
+ {"hostname", optional_argument, NULL, 'h'},
+ {"classid", optional_argument, NULL, 'i'},
+ {"release", no_argument, NULL, 'k'},
+ {"leasetime", required_argument, NULL, 'l'},
+ {"metric", required_argument, NULL, 'm'},
+ {"renew", no_argument, NULL, 'n'},
+ {"persistent", no_argument, NULL, 'p'},
+ {"qtsocketaddress", required_argument, NULL, 'q'},
+ {"inform", optional_argument, NULL, 's'},
+ {"request", optional_argument, NULL, 'r'},
+ {"timeout", required_argument, NULL, 't'},
+ {"userclass", required_argument, NULL, 'u'},
+ {"exit", no_argument, NULL, 'x'},
+ {"lastlease", no_argument, NULL, 'E'},
+ {"fqdn", required_argument, NULL, 'F'},
+ {"nogateway", no_argument, NULL, 'G'},
+ {"sethostname", no_argument, NULL, 'H'},
+ {"clientid", optional_argument, NULL, 'I'},
+ {"noipv4ll", no_argument, NULL, 'L'},
+ {"nomtu", no_argument, NULL, 'M'},
+ {"nontp", no_argument, NULL, 'N'},
+ {"nodns", no_argument, NULL, 'R'},
+ {"msscr", no_argument, NULL, 'S'},
+ {"test", no_argument, NULL, 'T'},
+ {"nonis", no_argument, NULL, 'Y'},
+ {"help", no_argument, &dohelp, 1},
+ {"version", no_argument, &doversion, 1},
+#ifdef THERE_IS_NO_FORK
+ {"daemonised", no_argument, NULL, 'f'},
+ {"skiproutes", required_argument, NULL, 'g'},
+#endif
+ {NULL, 0, NULL, 0}
+};
+
+#ifdef THERE_IS_NO_FORK
+char dhcpcd[PATH_MAX];
+char **dhcpcd_argv = NULL;
+int dhcpcd_argc = 0;
+char *dhcpcd_skiproutes = NULL;
+#undef EXTRA_OPTS
+#define EXTRA_OPTS "fg:"
+#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" [-adknpEGHMNRSTY] [-c script] [-h hostname] [-i classID]\n"
+ " [-l leasetime] [-m metric] [-r ipaddress] [-s ipaddress]\n"
+ " [-t timeout] [-u userclass] [-F none | ptr | both]\n"
+ " [-I clientID] [-q qtsocketaddress] <interface>\n");
+}
+
+
+int main (int argc, char **argv)
+{
+ options_t *options;
+ int userclasses = 0;
+ int opt;
+ int option_index = 0;
+ char *prefix;
+ pid_t pid;
+ int debug = 0;
+ int i;
+ int pidfd = -1;
+ int sig = 0;
+ int retval = EXIT_FAILURE;
+
+ /* Close any un-needed fd's */
+ for (i = getdtablesize() - 1; i >= 3; --i)
+ close (i);
+
+ openlog (PACKAGE, LOG_PID, LOG_LOCAL0);
+
+ options = xzalloc (sizeof (*options));
+ options->script = (char *) DEFAULT_SCRIPT;
+ snprintf (options->classid, CLASS_ID_MAX_LEN, "%s %s",
+ PACKAGE, VERSION);
+
+ options->doarp = true;
+ options->dodns = true;
+ options->domtu = true;
+ options->donis = true;
+ options->dontp = true;
+ options->dogateway = true;
+ options->daemonise = true;
+ options->doinform = false;
+ options->doipv4ll = true;
+ options->doduid = true;
+ options->timeout = DEFAULT_TIMEOUT;
+ /* added by Niklas Goby, additional field, storing the socket address path for
+ * communicating with Qt "server"
+ * defined in dhcpcd.h */
+ strcpy(options->qtsocketaddress, DEFAULT_QTSOCKETADDRESS);
+
+ gethostname (options->hostname, sizeof (options->hostname));
+ if (strcmp (options->hostname, "(none)") == 0 ||
+ strcmp (options->hostname, "localhost") == 0)
+ memset (options->hostname, 0, sizeof (options->hostname));
+
+
+ /* Don't set any optional arguments here so we retain POSIX
+ * compatibility with getopt */
+ while ((opt = getopt_long(argc, argv, EXTRA_OPTS
+ "c:dh:i:kl:m:npq:r:s:t:u:xAEF:GHI:LMNRSTY",
+ 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 'c':
+ options->script = optarg;
+ break;
+ case 'd':
+ debug++;
+ switch (debug) {
+ case 1:
+ setloglevel (LOG_DEBUG);
+ break;
+ case 2:
+ options->daemonise = false;
+ break;
+ }
+ break;
+ #ifdef THERE_IS_NO_FORK
+ case 'f':
+ options->daemonised = true;
+ close_fds ();
+ break;
+ case 'g':
+ dhcpcd_skiproutes = xstrdup (optarg);
+ break;
+ #endif
+ case 'h':
+ if (! optarg)
+ *options->hostname = '\0';
+ else if (strlen (optarg) > MAXHOSTNAMELEN) {
+ logger (LOG_ERR,
+ "`%s' too long for HostName string, max is %d",
+ optarg, MAXHOSTNAMELEN);
+ goto abort;
+ } else
+ strlcpy (options->hostname, optarg,
+ sizeof (options->hostname));
+ break;
+ case 'i':
+ if (! optarg) {
+ *options->classid = '\0';
+ } else if (strlen (optarg) > CLASS_ID_MAX_LEN) {
+ logger (LOG_ERR,
+ "`%s' too long for ClassID string, max is %d",
+ optarg, CLASS_ID_MAX_LEN);
+ goto abort;
+ } else
+ strlcpy (options->classid, optarg,
+ sizeof (options->classid));
+ break;
+ case 'k':
+ sig = SIGHUP;
+ break;
+ case 'l':
+ if (*optarg == '-') {
+ logger (LOG_ERR,
+ "leasetime must be a positive value");
+ goto abort;
+ }
+ errno = 0;
+ options->leasetime = (uint32_t) strtol (optarg, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE) {
+ logger (LOG_ERR, "`%s' out of range", optarg);
+ goto abort;
+ }
+ break;
+ case 'm':
+ options->metric = atoint (optarg);
+ if (options->metric < 0) {
+ logger (LOG_ERR,
+ "metric must be a positive value");
+ goto abort;
+ }
+ break;
+ case 'n':
+ sig = SIGALRM;
+ break;
+ case 'p':
+ options->persistent = true;
+ break;
+ case 'q':
+ if (strlen(optarg) > QTSOCKETADDRESSLENGTH) {
+ logger(LOG_ERR, "`%s' too long for an socket address path (max=%d)",
+ optarg, QTSOCKETADDRESSLENGTH);
+ goto abort;
+ }
+ strlcpy(options->qtsocketaddress, optarg, sizeof(options->qtsocketaddress));
+ break;
+ case 's':
+ options->doinform = true;
+ options->doarp = false;
+ if (! optarg || strlen (optarg) == 0) {
+ options->request_address.s_addr = 0;
+ break;
+ } else {
+ char *slash = strchr (optarg, '/');
+ if (slash) {
+ int cidr;
+ /* nullify the slash, so the -r option can read the
+ * address */
+ *slash++ = '\0';
+ if (sscanf (slash, "%d", &cidr) != 1 ||
+ inet_cidrtoaddr (cidr, &options->request_netmask) != 0) {
+ logger (LOG_ERR, "`%s' is not a valid CIDR", slash);
+ goto abort;
+ }
+ }
+ }
+ /* FALLTHROUGH */
+ case 'r':
+ if (! options->doinform)
+ options->dorequest = true;
+ if (strlen (optarg) > 0 &&
+ ! inet_aton (optarg, &options->request_address))
+ {
+ logger (LOG_ERR, "`%s' is not a valid IP address", optarg);
+ goto abort;
+ }
+ break;
+ case 't':
+ options->timeout = atoint (optarg);
+ if (options->timeout < 0) {
+ logger (LOG_ERR, "timeout must be a positive value");
+ goto abort;
+ }
+ break;
+ case 'u':
+ {
+ int offset = 0;
+ for (i = 0; i < userclasses; i++)
+ offset += (int) options->userclass[offset] + 1;
+ if (offset + 1 + strlen (optarg) > USERCLASS_MAX_LEN) {
+ logger (LOG_ERR, "userclass overrun, max is %d",
+ USERCLASS_MAX_LEN);
+ goto abort;
+ }
+ userclasses++;
+ memcpy (options->userclass + offset + 1 , optarg, strlen (optarg));
+ options->userclass[offset] = strlen (optarg);
+ options->userclass_len += (strlen (optarg)) + 1;
+ }
+ break;
+ case 'x':
+ sig = SIGTERM;
+ break;
+ case 'A':
+ #ifndef ENABLE_ARP
+ logger (LOG_ERR,
+ "arp not compiled into dhcpcd");
+ goto abort;
+ #endif
+ options->doarp = false;
+ break;
+ case 'E':
+ #ifndef ENABLE_INFO
+ logger (LOG_ERR,
+ "info not compiled into dhcpcd");
+ goto abort;
+ #endif
+ options->dolastlease = true;
+ break;
+ case 'F':
+ if (strncmp (optarg, "none", strlen (optarg)) == 0)
+ options->fqdn = FQDN_NONE;
+ else if (strncmp (optarg, "ptr", strlen (optarg)) == 0)
+ options->fqdn = FQDN_PTR;
+ else if (strncmp (optarg, "both", strlen (optarg)) == 0)
+ options->fqdn = FQDN_BOTH;
+ else {
+ logger (LOG_ERR, "invalid value `%s' for FQDN", optarg);
+ goto abort;
+ }
+ break;
+ case 'G':
+ options->dogateway = false;
+ break;
+ case 'H':
+ options->dohostname++;
+ break;
+ case 'I':
+ if (optarg) {
+ if (strlen (optarg) > CLIENT_ID_MAX_LEN) {
+ logger (LOG_ERR, "`%s' is too long for ClientID, max is %d",
+ optarg, CLIENT_ID_MAX_LEN);
+ goto abort;
+ }
+ if (strlcpy (options->clientid, optarg,
+ sizeof (options->clientid)) == 0)
+ /* empty string disabled duid */
+ options->doduid = false;
+
+ } else {
+ memset (options->clientid, 0, sizeof (options->clientid));
+ options->doduid = false;
+ }
+ break;
+ case 'L':
+ options->doipv4ll = false;
+ break;
+ case 'M':
+ options->domtu = false;
+ break;
+ case 'N':
+ options->dontp = false;
+ break;
+ case 'R':
+ options->dodns = false;
+ break;
+ case 'S':
+ options->domscsr++;
+ break;
+ case 'T':
+ #ifndef ENABLE_INFO
+ logger (LOG_ERR, "info support not compiled into dhcpcd");
+ goto abort;
+ #endif
+ options->test = true;
+ options->persistent = true;
+ break;
+ case 'Y':
+ options->donis = false;
+ break;
+ case '?':
+ usage ();
+ goto abort;
+ default:
+ usage ();
+ goto abort;
+ }
+ }
+ if (doversion) {
+ printf (""PACKAGE" "VERSION"\n");
+ printf ("Compile time options:"
+ #ifdef ENABLE_ARP
+ " ARP"
+ #endif
+ #ifdef ENABLE_DUID
+ " DUID"
+ #endif
+ #ifdef ENABLE_INFO
+ " INFO"
+ #endif
+ #ifdef ENABLE_INFO_COMPAT
+ " INFO_COMPAT"
+ #endif
+ #ifdef ENABLE_IPV4LL
+ " IPV4LL"
+ #endif
+ #ifdef ENABLE_NIS
+ " NIS"
+ #endif
+ #ifdef ENABLE_NTP
+ " NTP"
+ #endif
+ #ifdef SERVICE
+ " " SERVICE
+ #endif
+ #ifdef ENABLE_RESOLVCONF
+ " RESOLVCONF"
+ #endif
+ #ifdef THERE_IS_NO_FORK
+ " THERE_IS_NO_FORK"
+ #endif
+ "\n");
+ }
+
+ if (dohelp)
+ usage ();
+
+
+#ifdef THERE_IS_NO_FORK
+ dhcpcd_argv = argv;
+ dhcpcd_argc = argc;
+ if (! realpath (argv[0], dhcpcd)) {
+ logger (LOG_ERR, "unable to resolve the path `%s': %s",
+ argv[0], strerror (errno));
+ goto abort;
+ }
+#endif
+
+ /* initializations for the ipc connection to qt*/
+ setSocketName(options->qtsocketaddress);
+ if (initQtLoggerSocket() < 0) {
+ logger(LOG_ERR, "initialization Qt Logger failed: %s ", strerror (errno));
+ goto abort;
+ }
+
+
+ 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));
+ setInterfaceName(options->interface);
+ } else {
+ /* If only version was requested then exit now */
+ if (doversion || dohelp) {
+ retval = 0;
+ goto abort;
+ }
+
+ logger(LOG_ERR, "no interface specified");
+ setInterfaceName("no_if");
+ goto abort;
+ }
+
+ if (strchr(options->hostname, '.')) {
+ if (options->fqdn == FQDN_DISABLE)
+ options->fqdn = FQDN_BOTH;
+ } else
+ options->fqdn = FQDN_DISABLE;
+
+ if (options->request_address.s_addr == 0 && options->doinform) {
+ if ((options->request_address.s_addr = get_address(options->interface))
+ != 0)
+ options->keep_address = true;
+ }
+
+ if (IN_LINKLOCAL (ntohl (options->request_address.s_addr))) {
+ logger (LOG_ERR,
+ "you are not allowed to request a link local address");
+ logToQt(LOG_ERR, -1, "you are not allowed to request a link local address");
+ goto abort;
+ }
+
+ if (geteuid ()) {
+ logger (LOG_WARNING, PACKAGE " will not work correctly unless"
+ " run as root");
+ }
+
+ 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);
+
+ chdir ("/");
+ umask (022);
+
+ if (mkdir (INFODIR, S_IRUSR | S_IWUSR |S_IXUSR | S_IRGRP | S_IXGRP
+ | S_IROTH | S_IXOTH) && errno != EEXIST)
+ {
+ logger (LOG_ERR,
+ "mkdir(\"%s\",0): %s\n", INFODIR, strerror (errno));
+ goto abort;
+ }
+
+ if (mkdir (ETCDIR, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
+ | S_IROTH | S_IXOTH) && errno != EEXIST)
+ {
+ logger (LOG_ERR,
+ "mkdir(\"%s\",0): %s\n", ETCDIR, strerror (errno));
+ goto abort;
+ }
+
+ if (options->test) {
+ if (options->dorequest || options->doinform) {
+ logger (LOG_ERR,
+ "cannot test with --inform or --request");
+ goto abort;
+ }
+
+ if (options->dolastlease) {
+ logger (LOG_ERR, "cannot test with --lastlease");
+ goto abort;
+ }
+
+ if (sig != 0) {
+ logger (LOG_ERR,
+ "cannot test with --release or --renew");
+ goto abort;
+ }
+ }
+
+ if (sig != 0) {
+ int killed = -1;
+ pid = read_pid (options->pidfile);
+ if (pid != 0)
+ logger (LOG_INFO, "sending signal %d to pid %d",
+ sig, pid);
+
+ if (! pid || (killed = kill (pid, sig)))
+ logger (sig == SIGALRM ? LOG_INFO : LOG_ERR,
+ ""PACKAGE" not running");
+
+ if (pid != 0 && (sig != SIGALRM || killed != 0))
+ unlink (options->pidfile);
+
+ if (killed == 0) {
+ retval = EXIT_SUCCESS;
+ goto abort;
+ }
+
+ if (sig != SIGALRM)
+ goto abort;
+ }
+
+ if (! options->test && ! options->daemonised) {
+ 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;
+ }
+
+ pidfd = open (options->pidfile,
+ O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (pidfd == -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 (pidfd, LOCK_EX | LOCK_NB) == -1) {
+ logger (LOG_ERR, "flock `%s': %s",
+ options->pidfile, strerror (errno));
+ goto abort;
+ }
+
+ /* dhcpcd.sh should not interhit this fd */
+ if ((i = fcntl (pidfd, F_GETFD, 0)) == -1 ||
+ fcntl (pidfd, F_SETFD, i | FD_CLOEXEC) == -1)
+ logger (LOG_ERR, "fcntl: %s", strerror (errno));
+
+ writepid (pidfd, getpid ());
+ logger (LOG_INFO, PACKAGE " " VERSION " starting");
+ }
+
+ /* Seed random */
+ srandomdev ();
+
+ /* Massage our filters per platform */
+ setup_packet_filters ();
+
+ /* dhcp_run : defined in client.c */
+ if (dhcp_run (options, &pidfd) == 0)
+ retval = EXIT_SUCCESS;
+
+abort:
+ /* If we didn't daemonise then we need to punt the pidfile now */
+ if (pidfd > -1) {
+ close (pidfd);
+ unlink (options->pidfile);
+ }
+
+ free (options);
+
+#ifdef THERE_IS_NO_FORK
+ /* There may have been an error before the dhcp_run function
+ * clears this, so just do it here to be safe */
+ free (dhcpcd_skiproutes);
+#endif
+
+ logger (LOG_INFO, "exiting");
+ logToQt(LOG_INFO, DHCPCD_EXIT, "exiting due abort");
+ closeQtLoggerSocket();
+ exit (retval);
+ /* NOTREACHED */
+}