From d5b915377b3528bf3bb0df6dc10b5863397e59d9 Mon Sep 17 00:00:00 2001 From: Russ Dill Date: Tue, 17 Sep 2002 22:44:01 +0000 Subject: [PATCH] socketpair signal handling --- AUTHORS | 4 ++-- ChangeLog | 7 +++++++ README | 20 ++++++++++++-------- dhcpc.c | 47 +++++++++++++++++++++++++++-------------------- dhcpd.c | 54 +++++++++++++++++++++++++++++++++++++----------------- files.c | 5 +---- files.h | 2 +- socket.c | 3 +-- udhcpc.8 | 24 ++++++++++++++++++++++-- 9 files changed, 110 insertions(+), 56 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4d5aab9..89a6de4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,9 @@ -Moreton Bay DHCP Server +udhcp server/client package ----------------------- +Russ Dill Matthew Ramsay Chris Trew -Russ Dill Other Credits: -------------- diff --git a/ChangeLog b/ChangeLog index f7061d1..d9221d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,11 @@ 0.9.8 (pending) ++ updated client manpage (me) ++ both client and server now use sockets for signal handling, + hopefully, this will be the last needed change in signal + handling, I'm fairly certain all the possible races are now + closed. (me) ++ The server now restarts the auto_time timer when it receives + a SIGUSR1 (write out config file). (me) + Improve signal handling (David Poole) + Fix to config file parsing (Matt Kraai) + Fix load lease logic (me) diff --git a/README b/README index 3fd3b41..f418190 100644 --- a/README +++ b/README @@ -16,17 +16,21 @@ udhcpd /etc/udhcpd.eth1.conf The udhcp server employs a number of simple config files: -udhcpd.leased +udhcpd.leases ------------ The udhcpd.leases behavior is designed for an embedded system. The file is written either every auto_time seconds, or when a SIGUSR1 -is received. When the file is written, a script can be optionally -called to commit the file to flash. Lease times are stored in the -file by time remaining in lease (for systems without clock that works -when there is no power), or by the absolute time that it expires in -seconds from epoch. In the remainig format, expired leases are stored -as zero. The file is of the format: +is received (the auto_time timer restarts if a SIGUSR1 is received). +If you send a SIGTERM to udhcpd directly after a SIGUSR1, udhcpd will +finish writing the leases file and wait for the aftermentioned script +to be executed and finish before quiting, so you do not need to sleep +between sending signals. When the file is written, a script can be +optionally called to commit the file to flash. Lease times are stored +in the file by time remaining in lease (for systems without clock +that works when there is no power), or by the absolute time that it +expires in seconds from epoch. In the remainig format, expired leases +are stored as zero. The file is of the format: 16 byte MAC 4 byte ip address @@ -147,7 +151,7 @@ udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state, and SIGUSR2 will force a release of the current lease, and cause udhcpc to go into an inactive state (until it is killed, or receives a SIGUSR1). You do not need to sleep between sending signals, as signals received are processed -in a logical order during the main loop (release, then renew, then term). +sequencially in the order they are received. diff --git a/dhcpc.c b/dhcpc.c index 9fd2c01..90295c7 100644 --- a/dhcpc.c +++ b/dhcpc.c @@ -52,9 +52,7 @@ static unsigned long server_addr; static unsigned long timeout; static int packet_num; /* = 0 */ static int fd; -static int renew_requested; /* = 0 */ -static int release_requested; /* = 0 */ -static int term_received; /* = 0 */ +static int signal_pipe[2]; #define LISTEN_NONE 0 #define LISTEN_KERNEL 1 @@ -177,10 +175,9 @@ static void exit_client(int retval) /* Signal handler */ static void signal_handler(int sig) { - switch (sig) { - case SIGUSR1: renew_requested = 1; break; - case SIGUSR2: release_requested = 1; break; - case SIGTERM: term_received = 1; break; + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { + LOG(LOG_ERR, "Could not send signal: %s", + strerror(errno)); } } @@ -217,6 +214,8 @@ int main(int argc, char *argv[]) struct in_addr temp_addr; int pid_fd; time_t now; + int max_fd; + int sig; static struct option options[] = { {"clientid", required_argument, 0, 'c'}, @@ -313,6 +312,7 @@ int main(int argc, char *argv[]) } /* setup signal handlers */ + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGTERM, signal_handler); @@ -338,10 +338,12 @@ int main(int argc, char *argv[]) } } if (fd >= 0) FD_SET(fd, &rfds); - + FD_SET(signal_pipe[0], &rfds); + if (tv.tv_sec > 0) { DEBUG(LOG_INFO, "Waiting on select...\n"); - retval = select(fd + 1, &rfds, NULL, NULL, &tv); + max_fd = signal_pipe[0] > fd ? signal_pipe[0] : fd; + retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); } else retval = 0; /* If we already timed out, fall through */ now = time(0); @@ -524,21 +526,26 @@ int main(int argc, char *argv[]) } break; /* case BOUND, RELEASED: - ignore all packets */ - } - } else if (retval == -1 && errno == EINTR) { - /* a signal was caught */ - if (release_requested) { - perform_release(); - release_requested = 0; + } + } else if (retval > 0 && FD_ISSET(signal_pipe[0], &rfds)) { + if (read(signal_pipe[0], &sig, sizeof(signal)) < 0) { + DEBUG(LOG_ERR, "Could not read signal: %s", + strerror(errno)); + continue; /* probably just EINTR */ } - if (renew_requested) { + switch (sig) { + case SIGUSR1: perform_renew(); - renew_requested = 0; - } - if (term_received) { + break; + case SIGUSR2: + perform_release(); + break; + case SIGTERM: LOG(LOG_INFO, "Received SIGTERM"); exit_client(0); - } + } + } else if (retval == -1 && errno == EINTR) { + /* a signal was caught */ } else { /* An error occured */ DEBUG(LOG_ERR, "Error on select"); diff --git a/dhcpd.c b/dhcpd.c index 00035eb..9c6aca8 100644 --- a/dhcpd.c +++ b/dhcpd.c @@ -1,6 +1,6 @@ /* dhcpd.c * - * Moreton Bay DHCP Server + * udhcp Server * Copyright (C) 1999 Matthew Ramsay * Chris Trew * @@ -54,7 +54,7 @@ /* globals */ struct dhcpOfferedAddr *leases; struct server_config_t server_config; - +static int signal_pipe[2]; /* Exit and cleanup */ static void exit_server(int retval) @@ -65,12 +65,13 @@ static void exit_server(int retval) } -/* SIGTERM handler */ -static void udhcpd_killed(int sig) +/* Signal handler */ +static void signal_handler(int sig) { - sig = 0; - LOG(LOG_INFO, "Received SIGTERM"); - exit_server(0); + if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0) { + LOG(LOG_ERR, "Could not send signal: %s", + strerror(errno)); + } } @@ -92,6 +93,8 @@ int main(int argc, char *argv[]) struct option_set *option; struct dhcpOfferedAddr *lease; int pid_fd; + int max_sock; + int sig; OPEN_LOG("udhcpd"); LOG(LOG_INFO, "udhcp server (v%s) started", VERSION); @@ -129,8 +132,9 @@ int main(int argc, char *argv[]) #endif - signal(SIGUSR1, write_leases); - signal(SIGTERM, udhcpd_killed); + socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe); + signal(SIGUSR1, signal_handler); + signal(SIGTERM, signal_handler); timeout_end = time(0) + server_config.auto_time; while(1) { /* loop until universe collapses */ @@ -143,26 +147,42 @@ int main(int argc, char *argv[]) FD_ZERO(&rfds); FD_SET(server_socket, &rfds); + FD_SET(signal_pipe[0], &rfds); if (server_config.auto_time) { tv.tv_sec = timeout_end - time(0); - if (tv.tv_sec <= 0) { - tv.tv_sec = server_config.auto_time; - timeout_end = time(0) + server_config.auto_time; - write_leases(0); - } tv.tv_usec = 0; } - retval = select(server_socket + 1, &rfds, NULL, NULL, server_config.auto_time ? &tv : NULL); + if (!server_config.auto_time || tv.tv_sec > 0) { + max_sock = server_socket > signal_pipe[0] ? server_socket : signal_pipe[0]; + retval = select(max_sock + 1, &rfds, NULL, NULL, + server_config.auto_time ? &tv : NULL); + } else retval = 0; /* If we already timed out, fall through */ if (retval == 0) { - write_leases(0); + write_leases(); timeout_end = time(0) + server_config.auto_time; continue; - } else if (retval < 0) { + } else if (retval < 0 && errno != EINTR) { DEBUG(LOG_INFO, "error on select"); continue; } + if (FD_ISSET(signal_pipe[0], &rfds)) { + if (read(signal_pipe[0], &sig, sizeof(sig)) < 0) + continue; /* probably just EINTR */ + switch (sig) { + case SIGUSR1: + LOG(LOG_INFO, "Received a SIGUSR1"); + write_leases(); + /* why not just reset the timeout, eh */ + timeout_end = time(0) + server_config.auto_time; + continue; + case SIGTERM: + LOG(LOG_INFO, "Received a SIGTERM"); + exit_server(0); + } + } + if ((bytes = get_packet(&packet, server_socket)) < 0) { /* this waits for a packet - idle */ if (bytes == -1 && errno != EINTR) { DEBUG(LOG_INFO, "error on read, %s, reopening socket", strerror(errno)); diff --git a/files.c b/files.c index a3ee166..842e0f2 100644 --- a/files.c +++ b/files.c @@ -219,8 +219,7 @@ int read_config(char *file) } -/* the dummy var is here so this can be a signal handler */ -void write_leases(int dummy) +void write_leases(void) { FILE *fp; unsigned int i; @@ -228,8 +227,6 @@ void write_leases(int dummy) time_t curr = time(0); unsigned long lease_time; - dummy = 0; - if (!(fp = fopen(server_config.lease_file, "w"))) { LOG(LOG_ERR, "Unable to open %s for writing", server_config.lease_file); return; diff --git a/files.h b/files.h index c0368aa..1f2638f 100644 --- a/files.h +++ b/files.h @@ -11,7 +11,7 @@ struct config_keyword { int read_config(char *file); -void write_leases(int dummy); +void write_leases(void); void read_leases(char *file); #endif diff --git a/socket.c b/socket.c index bf823b8..c9d462e 100644 --- a/socket.c +++ b/socket.c @@ -1,7 +1,7 @@ /* * socket.c -- DHCP server client/server socket creation * - * Moreton Bay DHCP Server + * udhcp client/server * Copyright (C) 1999 Matthew Ramsay * Chris Trew * @@ -153,6 +153,5 @@ int raw_socket(int ifindex) } return fd; - } diff --git a/udhcpc.8 b/udhcpc.8 index 5d5a0a1..752a736 100644 --- a/udhcpc.8 +++ b/udhcpc.8 @@ -20,6 +20,10 @@ Do not fork after obtaining a lease. Send the client hostname .IR HOSTNAME . .TP +.BI \-h\ HOSTNAME +Alias for -H +.IR HOSTNAME . +.TP .BI \-i\ INTERFACE ,\ \-\-interface= INTERFACE Configure .IR INTERFACE . @@ -47,7 +51,7 @@ Display version. .SH USAGE When an event occurs, .B udhcpc -executes a script. There are three possible arguments to this +executes a script. There are four possible arguments to this script: .TP .B deconfig @@ -68,9 +72,22 @@ configure the interface and set any other relevant parameters .B renew .B renew is used when -.B udhcpc when a lease is renewed. The interface is already +.B udhcpc +when a lease is renewed. The interface is already configured, so the IP address will not change. Other parameters (e.g., default gateway, subnet mask, dns server) may. +.TP +.B nak +.B nak +is used when +.B udhcpc +receieves a NAK packet from the server. The +enviromental variable +.B $message +will contain the reason for the +NAK message if the server included one. Processing this message +is optional, as the script will also be called with deconfig if +need be. .PP Parameters are passed to the script via the following environment variables: @@ -163,6 +180,9 @@ The DHCP message type (safely ignored). .B serverid The server IP address. .TP +.B message +Reason for a DHCPNAK. +.TP .B tftp The TFTP server name. .TP