From: Matthew Dillon Date: Wed, 20 Aug 2014 01:57:05 +0000 (-0700) Subject: dhclient - Add -x and -w and adjust rc scripts X-Git-Tag: v4.1.0~240 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/0f5f50b1f05966ef4580d3e93d57a50eb67c30cd dhclient - Add -x and -w and adjust rc scripts * Add the -w option which prevents dhclient from exiting on interface down. dhclient will sleep 10 seconds and retry, forever. * Add the -x option which kills any running dhclient on the specified interface. * Adjust rc.d/dhclient to use the -x option to kill the dhclient instead of taking down the interface (which no longer kills it), and also adjust the default flags to pass -w. --- diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index bbdc08f848..ac723de2e5 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -58,7 +58,7 @@ devfs_config_files="/etc/defaults/devfs.conf /etc/devfs.conf" hostname="" # Set this! nisdomainname="NO" # Set to NIS domain if using NIS (or NO). dhclient_program="/sbin/dhclient" # Path to dhcp client program. -dhclient_flags="" # Additional flags to pass to dhcp client. +dhclient_flags="-w" # Additional flags to pass to dhcp client. firewall_enable="NO" # Set to YES to enable firewall functionality firewall_script="/etc/rc.firewall" # Which script to run to set up the firewall firewall_type="UNKNOWN" # Firewall type (see /etc/rc.firewall) diff --git a/etc/rc.d/dhclient b/etc/rc.d/dhclient index 4e097c2b45..12020c9ab9 100644 --- a/etc/rc.d/dhclient +++ b/etc/rc.d/dhclient @@ -39,7 +39,7 @@ dhclient_start() dhclient_stop() { - ifconfig $ifn down # cause dhclient to die + ${dhclient_program} -x $ifn } ifn="$2" diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8 index d32c380d40..bc8c8697a0 100644 --- a/sbin/dhclient/dhclient.8 +++ b/sbin/dhclient/dhclient.8 @@ -43,7 +43,7 @@ .Nd Dynamic Host Configuration Protocol (DHCP) client .Sh SYNOPSIS .Nm -.Op Fl dqu +.Op Fl dquwx .Op Fl c Ar file .Op Fl l Ar file .Ar interface @@ -98,6 +98,16 @@ Forces .Nm to reject leases with unknown options in them. The default behaviour is to accept such lease offers. +.It Fl w +.Nm +stays around permanently and will attempt to reconnect even if +the interface does not exist, is down, or goes down. +This is the default when interfaces are configured DHCP in +.Pa /etc/dhclient.conf +.It Fl x +Kill any running +.Nm +on the specified interface. .El .Pp The DHCP protocol allows a host to contact a central server which diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index f9c3a4d1cf..bd589fa726 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -1,5 +1,3 @@ -/* $OpenBSD: src/sbin/dhclient/dhclient.c,v 1.158 2012/10/27 23:08:53 krw Exp $ */ - /* * Copyright 2004 Henning Brauer * Copyright (c) 1995, 1996, 1997, 1998, 1999 @@ -57,6 +55,7 @@ #include #include #include +#include #include #include "dhcpd.h" @@ -70,13 +69,17 @@ char *path_dhclient_conf = _PATH_DHCLIENT_CONF; char *path_dhclient_db = NULL; +char *orig_ifname; int log_perror = 1; int privfd; int nullfd = -1; int no_daemon; +int stayalive = 0; int unknown_ok = 1; int routefd = -1; +int forceup = 1; /* force interface up */ +pid_t monitor_pid; struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; struct in_addr inaddr_any; @@ -86,15 +89,17 @@ struct interface_info *ifi; struct client_state *client; struct client_config *config; -int findproto(char *, int); +int findproto(char *, int); struct sockaddr *get_ifa(char *, int); -void usage(void); -int check_option(struct client_lease *l, int option); -int ipv4addrs(char * buf); -int res_hnok(const char *dn); +void usage(void); +int check_option(struct client_lease *l, int option); +int ipv4addrs(char * buf); +int res_hnok(const char *dn); char *option_as_string(unsigned int code, unsigned char *data, int len); -int fork_privchld(int, int); -void get_ifname(char *, char *); +int fork_privchld(int, int); +void get_ifname(char *, char *); +static void sig_handle(int sig); +static int killclient(int fd); time_t scripttime; static FILE *leaseFile; @@ -263,14 +268,21 @@ die: int main(int argc, char *argv[]) { - int ch, fd, quiet = 0, i = 0, pipe_fd[2]; + int ch, fd; + int pipe_fd[2]; + int quiet = 0; + int dokillclient = 0; + int i; struct passwd *pw; /* Initially, log errors to stderr as well as to syslogd. */ openlog(getprogname(), LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); setlogmask(LOG_UPTO(LOG_INFO)); - while ((ch = getopt(argc, argv, "c:dl:qu")) != -1) + signal(SIGINT, sig_handle); + signal(SIGHUP, sig_handle); + + while ((ch = getopt(argc, argv, "c:dl:quwx")) != -1) { switch (ch) { case 'c': path_dhclient_conf = optarg; @@ -287,15 +299,81 @@ main(int argc, char *argv[]) case 'u': unknown_ok = 0; break; + case 'w': + stayalive = 1; + break; + case 'x': + dokillclient = 1; + break; default: usage(); } + } argc -= optind; argv += optind; if (argc != 1) usage(); + orig_ifname = argv[0]; + + if (dokillclient) { + char buf[256]; + + snprintf(buf, sizeof(buf), + "/var/run/dhclient.%s.pid", orig_ifname); + fd = open(buf, O_RDWR, 0644); + if (fd < 0 || killclient(fd)) { + fprintf(stderr, + "no dhclient running on %s\n", + orig_ifname); + } else { + fprintf(stderr, + "stopping dhclient on %s\n", + orig_ifname); + } + if (fd >= 0) + close(fd); + exit(1); + } + + if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) + error("cannot open %s: %m", _PATH_DEVNULL); + + /* + * If asked to stay alive forever get our daemon going right now + * Then set up to fork/monitor and refork on exit. + * + * When I say 'forever' I really mean it. If there are configuration + * problems or missing interfaces or whatever, dhclient will wait + * 10 seconds and try again. + */ + if (stayalive) { + pid_t pid; + pid_t rpid; + int omask; + + go_daemon(); + + for (;;) { + omask = sigblock(sigmask(SIGINT) | sigmask(SIGHUP)); + pid = fork(); + if (pid > 0) + monitor_pid = pid; + sigsetmask(omask); + + if (pid == 0) /* child falls out of loop */ + break; + while (pid > 0) { + rpid = waitpid(pid, NULL, 0); + if (rpid == pid) + break; + if (rpid != EINTR) + break; + } + sleep(10); + } + } ifi = calloc(1, sizeof(*ifi)); if (ifi == NULL) @@ -308,6 +386,7 @@ main(int argc, char *argv[]) error("config calloc"); get_ifname(ifi->name, argv[0]); + if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", _PATH_DHCLIENT_DB, ifi->name) == -1) error("asprintf"); @@ -327,11 +406,13 @@ main(int argc, char *argv[]) read_client_conf(); if (interface_status(ifi->name) == 0) { - interface_link_forceup(ifi->name); + if (forceup) + interface_link_forceup(ifi->name); /* Give it up to 4 seconds of silent grace to find link */ i = -4; - } else + } else { i = 0; + } while (!(ifi->linkstat = interface_status(ifi->name))) { if (i == 0) @@ -349,15 +430,13 @@ main(int argc, char *argv[]) fprintf(stderr, " got link\n"); dispatch: - if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) - error("cannot open %s: %m", _PATH_DEVNULL); - if ((pw = getpwnam("_dhcp")) == NULL) error("no such user: _dhcp"); if (pipe(pipe_fd) == -1) error("pipe"); + go_daemon(); fork_privchld(pipe_fd[0], pipe_fd[1]); close(pipe_fd[0]); @@ -396,9 +475,7 @@ main(int argc, char *argv[]) if (ifi->linkstat) { client->state = S_REBOOTING; state_reboot(); - } else - go_daemon(); - + } dispatch(); /* not reached */ @@ -664,7 +741,6 @@ bind_lease(void) piaddr(client->active->address), (long long)(client->active->renewal - time(NULL))); client->state = S_BOUND; - go_daemon(); } /* @@ -996,7 +1072,6 @@ state_panic(void) note("bound: immediate renewal."); state_bound(); } - go_daemon(); return; } } @@ -1036,7 +1111,6 @@ activate_next: script_go(); client->state = S_INIT; set_timeout_interval(config->retry_interval, state_init); - go_daemon(); } void @@ -1781,26 +1855,64 @@ dhcp_option_ev_name(char *buf, size_t buflen, const struct option *option) void go_daemon(void) { - static int state = 0; + char buf[256]; + int fd; - if (no_daemon || state) + /* + * Only once + */ + if (no_daemon == 2) return; - state = 1; + /* + * Setup pidfile, kill any dhclient already running for this + * interface. + */ + snprintf(buf, sizeof(buf), "/var/run/dhclient.%s.pid", orig_ifname); + fd = open(buf, O_RDWR|O_CREAT, 0644); + if (fd >= 0) { + if (killclient(fd)) { + fprintf(stderr, + "starting dhclient on %s\n", + orig_ifname); + } else { + fprintf(stderr, + "restarting dhclient on %s\n", + orig_ifname); + } + } + + /* + * Daemonize if requested + */ + if (no_daemon == 0) { + /* Stop logging to stderr... */ + log_perror = 0; - /* Stop logging to stderr... */ - log_perror = 0; + if (daemon(1, 0) == -1) + error("daemon"); - if (daemon(1, 0) == -1) - error("daemon"); + /* we are chrooted, daemon(3) fails to open /dev/null */ + if (nullfd != -1) { + dup2(nullfd, STDIN_FILENO); + dup2(nullfd, STDOUT_FILENO); + dup2(nullfd, STDERR_FILENO); + close(nullfd); + nullfd = -1; + } + } - /* we are chrooted, daemon(3) fails to open /dev/null */ - if (nullfd != -1) { - dup2(nullfd, STDIN_FILENO); - dup2(nullfd, STDOUT_FILENO); - dup2(nullfd, STDERR_FILENO); - close(nullfd); - nullfd = -1; + /* + * No further daemonizations, write out pid file and lock. + */ + no_daemon = 2; + if (fd >= 0) { + lseek(fd, 0L, SEEK_SET); + ftruncate(fd, 0); + snprintf(buf, sizeof(buf), "%ld\n", (long)getpid()); + write(fd, buf, strlen(buf)); + flock(fd, LOCK_EX); + /* leave descriptor open and locked */ } } @@ -2010,8 +2122,16 @@ fork_privchld(int fd, int fd2) { struct pollfd pfd[1]; int nfds, pfail = 0; + pid_t pid; + int omask; - switch (fork()) { + omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP)); + pid = fork(); + if (pid > 0) + monitor_pid = pid; + sigsetmask(omask); + + switch (pid) { case -1: error("cannot fork"); break; @@ -2095,3 +2215,45 @@ get_ifname(char *ifname, char *arg) } else if (strlcpy(ifi->name, arg, IFNAMSIZ) >= IFNAMSIZ) error("Interface name too long"); } + +static +void +sig_handle(int signo) +{ + if (monitor_pid > 0) + kill(monitor_pid, signo); + fprintf(stderr, "killed by signal\n"); + exit(1); +} + +static +int +killclient(int fd) +{ + int noclient = 1; + + /* + * Kill previously running dhclient + */ + if (flock(fd, LOCK_EX|LOCK_NB) < 0) { + char buf[256]; + ssize_t n; + pid_t pid; + + lseek(fd, 0L, SEEK_SET); + n = read(fd, buf, sizeof(buf)); + if (n > 0) { + buf[n-1] = 0; + pid = strtol(buf, NULL, 10); + if ((int)pid > 0) { + noclient = 0; + kill(pid, SIGINT); + } + } + if (flock(fd, LOCK_EX|LOCK_NB) < 0) + usleep(20000); + while (flock(fd, LOCK_EX|LOCK_NB) < 0) + sleep(1); + } + return noclient; +}