Use epoll() in ndhc.

This commit is contained in:
Nicholas J. Kain 2010-12-01 13:11:09 -05:00
parent a259640779
commit a154f96538

View File

@ -34,6 +34,7 @@
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/epoll.h>
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <net/if.h> #include <net/if.h>
#include <errno.h> #include <errno.h>
@ -60,13 +61,14 @@
#define NUMPACKETS 3 /* number of packets to send before delay */ #define NUMPACKETS 3 /* number of packets to send before delay */
#define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */ #define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */
static int signalFd; static int epollfd, signalFd;
static struct epoll_event events[3];
static char pidfile[MAX_PATH_LENGTH] = PID_FILE_DEFAULT; static char pidfile[MAX_PATH_LENGTH] = PID_FILE_DEFAULT;
static uint32_t requested_ip, server_addr, timeout; static uint32_t requested_ip, server_addr, timeout;
static uint32_t lease, t1, t2, xid, start; static uint32_t lease, t1, t2, xid, start;
static int state, packet_num, fd, listen_mode; static int state, packet_num, listenFd, listen_mode;
enum { enum {
LISTEN_NONE, LISTEN_NONE,
@ -88,6 +90,28 @@ struct client_config_t client_config = {
.arp = "\0", .arp = "\0",
}; };
static void epoll_add(int fd)
{
struct epoll_event ev;
int r;
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLHUP;
ev.data.fd = fd;
r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
if (r == -1)
suicide("epoll_add failed %s", strerror(errno));
}
static void epoll_del(int fd)
{
struct epoll_event ev;
int r;
ev.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLHUP;
ev.data.fd = fd;
r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);
if (r == -1)
suicide("epoll_del failed %s", strerror(errno));
}
static void show_usage(void) static void show_usage(void)
{ {
printf( printf(
@ -117,17 +141,23 @@ static void change_listen_mode(int new_mode)
log_line("entering %s listen mode", log_line("entering %s listen mode",
new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none"); new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
listen_mode = new_mode; listen_mode = new_mode;
if (fd >= 0) { if (listenFd >= 0) {
close(fd); epoll_del(listenFd);
fd = -1; close(listenFd);
listenFd = -1;
}
if (new_mode == LISTEN_KERNEL) {
listenFd = listen_socket(INADDR_ANY, CLIENT_PORT,
client_config.interface);
epoll_add(listenFd);
}
else if (new_mode == LISTEN_RAW) {
listenFd = raw_socket(client_config.ifindex);
epoll_add(listenFd);
} }
if (new_mode == LISTEN_KERNEL)
fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
else if (new_mode == LISTEN_RAW)
fd = raw_socket(client_config.ifindex);
else /* LISTEN_NONE */ else /* LISTEN_NONE */
return; return;
if (fd < 0) { if (listenFd < 0) {
log_error("FATAL: couldn't listen on socket: %s.", strerror(errno)); log_error("FATAL: couldn't listen on socket: %s.", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -303,9 +333,9 @@ static void handle_packet(void)
struct dhcpMessage packet; struct dhcpMessage packet;
if (listen_mode == LISTEN_KERNEL) if (listen_mode == LISTEN_KERNEL)
len = get_packet(&packet, fd); len = get_packet(&packet, listenFd);
else if (listen_mode == LISTEN_RAW) else if (listen_mode == LISTEN_RAW)
len = get_raw_packet(&packet, fd); len = get_raw_packet(&packet, listenFd);
else /* LISTEN_NONE */ else /* LISTEN_NONE */
return; return;
@ -470,39 +500,37 @@ static void signal_dispatch()
static void do_work(void) static void do_work(void)
{ {
struct timeval tv; int timeoutms;
fd_set rfds;
epollfd = epoll_create1(0);
if (epollfd == -1)
suicide("epoll_create1 failed");
epoll_add(signalFd);
change_listen_mode(LISTEN_RAW);
for (;;) { for (;;) {
timeoutms = (timeout - time(0)) * 1000;
tv.tv_sec = timeout - time(0); if (timeoutms <= 0) {
tv.tv_usec = 0;
if (tv.tv_sec <= 0) {
handle_timeout(); handle_timeout();
continue; continue;
} }
FD_ZERO(&rfds); int r = epoll_wait(epollfd, events, 3, timeoutms);
FD_SET(signalFd, &rfds); if (r == -1) {
if (fd >= 0) if (errno == EINTR)
FD_SET(fd, &rfds);
if (select(fd > signalFd ? fd + 1 : signalFd + 1,
&rfds, NULL, NULL, &tv) == -1) {
switch (errno) {
case EBADF:
fd = -1;
default:
log_error("Error: \"%s\" on select!",
strerror(errno));
case EINTR: /* Signal received, go back to top. */
continue; continue;
else
suicide("epoll_wait failed");
} }
} for (int i = 0; i < r; ++i) {
int fd = events[i].data.fd;
if (FD_ISSET(signalFd, &rfds)) if (fd == signalFd)
signal_dispatch(); signal_dispatch();
if (FD_ISSET(fd, &rfds)) else if (fd == listenFd)
handle_packet(); handle_packet();
else
suicide("epoll_wait: unknown fd");
}
} }
} }
@ -643,7 +671,6 @@ int main(int argc, char **argv)
state = INIT_SELECTING; state = INIT_SELECTING;
run_script(NULL, SCRIPT_DECONFIG); run_script(NULL, SCRIPT_DECONFIG);
change_listen_mode(LISTEN_RAW);
do_work(); do_work();