/* * dhcpcd - DHCP client daemon * Copyright 2006-2008 Roy Marples * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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" #include "status.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'}, {"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] \n"); } int nd_main(char *ifname) { 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; gethostname (options->hostname, sizeof (options->hostname)); if (strcmp (options->hostname, "(none)") == 0 || strcmp (options->hostname, "localhost") == 0) memset (options->hostname, 0, sizeof (options->hostname)); /* #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(""); setInterfaceName(ifname); initQtLoggerSocket(); if (strlen (ifname) > IF_NAMESIZE) { logger (LOG_ERR, "`%s' too long for an interface name (max=%d)", ifname, IF_NAMESIZE); logToQt(STAT_ERROR,-1,"interface name is too long"); goto abort; } else { strlcpy (options->interface, ifname, sizeof (options->interface)); } 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(STAT_ERROR, -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(STAT_INFO, -1, "exiting due abort"); exit (retval); /* NOTREACHED */ }