From c9e4f2c16ea032e9359b0203a2a11973d172ace7 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Thu, 12 Mar 2015 15:55:08 +0100 Subject: forward the events to a helper process via pipe To reduce number of forks we pass over the events to a helper program via a pipe. when there are no events for a certain time the helper program can exit to save memory. Once new events arrives the nldev application will respawn the helper. --- Makefile | 6 ++- nldev-handler.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ nldev.c | 132 +++++++++++++++++++++++++------------------------------- 3 files changed, 191 insertions(+), 75 deletions(-) create mode 100644 nldev-handler.c diff --git a/Makefile b/Makefile index e28138f..a15252b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ include config.mk SRC = ${NAME}.c OBJ = ${SRC:.c=.o} log.o -all: options ${NAME} +all: options ${NAME} nldev-handler options: @echo ${NAME} build options: @@ -24,6 +24,10 @@ ${NAME}: ${OBJ} @echo CC -o $@ @${CC} -o $@ ${OBJ} ${LDFLAGS} +# hack for now +nldev-handler: nldev-handler.o log.o + ${CC} -o $@ $^ ${LDFLAGS} + clean: @echo cleaning @rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz diff --git a/nldev-handler.c b/nldev-handler.c new file mode 100644 index 0000000..4fa6e2a --- /dev/null +++ b/nldev-handler.c @@ -0,0 +1,128 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +#define EVENT_TIMEOUT 2000 + +void +child(char *runpath) +{ + pid_t pid; + + if (!(pid = fork())) { + dbg("running %s", runpath); + if (execlp(runpath, basename(runpath), NULL) < 0) + edie("execvp"); + exit(0); + } + if (pid < 0) + edie("fork"); + + waitpid(pid, NULL, 0); +} + + +void +usage(void) +{ + die("usage: %s [-d] [-r run]\n", argv0); +} + +int main(int argc, char *argv[]) +{ + struct pollfd fds; + int r; + static char buf[16384]; + char *runpath = "/bin/mdev"; + char *subsystem = NULL; + + ARGBEGIN { + case 'd': + dodebug = 1; + break; + case 'r': + runpath = EARGF(usage()); + break; + case 'f': + subsystem = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + dbg("runpath=%s\n", runpath); + + fds.fd = 0; /* stdin */ + fds.events = POLLIN; + while ((r = poll(&fds, 1, EVENT_TIMEOUT)) > 0) { + size_t len; + int i, slen; + char *key, *value; + + clearenv(); + setenv("PATH", "/sbin:/bin", 1); + + if (!(fds.revents & POLLIN)) + continue; + + len = read(fds.fd, buf, sizeof(buf)-1); + buf[len] = 0; + dbg("Got %u bytes", len); + + slen = 0; + for (i = 0; i < len; i += slen + 1) { + key = buf + i; + value = strchr(key, '='); + slen = strlen(buf+i); + + if (!slen || value == NULL) + continue; + if (subsystem && !strncmp(key, "SUBSYSTEM=", 10) + && !strstr(key+10, subsystem)) { + dbg("subsystem filter '%s' applied.", + subsystem); + break; + } + + value[0] = '\0'; + value++; + + /* + * We generally trust the kernel. But there + * might be some udev flaw. (It's >20k sloc!) + */ + if (strcmp(key, "PATH")) { + setenv(key, value, 1); + dbg("%s = \"%s\"", key, value); + } + } + if (getenv("ACTION") != NULL && + getenv("DEVPATH") != NULL && + getenv("SUBSYSTEM") != NULL && + getenv("SEQNUM") != NULL) { + child(runpath); + } + + if (fds.revents & POLLHUP) { + dbg("parent hung up\n"); + return 0; + } + } + if (r == -1) + edie("poll"); + + if (r == 0) + dbg("exit due to timeout"); + + return 0; +} + + diff --git a/nldev.c b/nldev.c index eab53ff..f859eed 100644 --- a/nldev.c +++ b/nldev.c @@ -24,10 +24,15 @@ #include "arg.h" #include "log.h" -char *argv0; int listfd = -1; int dofork = 0; +struct handler { + int fd; + int argc; + char **argv; +}; + void disableoom(void) { @@ -47,30 +52,34 @@ disableoom(void) } void -child(char *runpath) +spawn_handler(struct handler *child) { - int fd, pid; - - if (!(pid = fork())) { - if (dofork && !dodebug) { - fd = open("/dev/null", O_RDWR); - if (fd >= 0) { - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) - close(fd); - } - } + int pipefd[2]; + + if (pipe(pipefd) == -1) + edie("pipe"); + + pid_t pid = fork(); - dbg("running %s", runpath); - if (execlp(runpath, basename(runpath), NULL) < 0) - edie("execvp"); - exit(0); - } if (pid < 0) edie("fork"); - waitpid(pid, NULL, 0); + if (pid == 0) { + close(pipefd[1]); + dup2(pipefd[0], 0); + execv(child->argv[0], child->argv); + edie("execl"); + } + close(pipefd[0]); + child->fd = pipefd[1]; +} + +int +write_to_handler(struct handler *child, const char *buf, size_t count) +{ + if (child->fd == -1) + spawn_handler(child); + return write(child->fd, buf, count); } void @@ -143,26 +152,25 @@ init_netlink_socket(void) void usage(void) { - die("usage: %s [-hdb] [-ku] [-f subsystem] [-r run]\n", argv0); + die("usage: %s [-hdb] [-ku] [-- run [...]]\n", argv0); } int main(int argc, char *argv[]) { struct sockaddr_nl cnls; - struct pollfd fds; + struct pollfd fds[2]; struct msghdr hdr; struct iovec iov; - char buf[4097], *subsystem, *runpath, *key, *value, - cbuf[CMSG_SPACE(sizeof(struct ucred))]; + char buf[4097], cbuf[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *chdr; struct ucred *cred; - int i, len, slen, showudev, showkernel; + int len, showudev, showkernel; + + struct handler child; showkernel = 1; showudev = 1; - subsystem = NULL; - runpath = "/bin/mdev"; ARGBEGIN { case 'b': @@ -171,25 +179,27 @@ main(int argc, char *argv[]) case 'd': dodebug = 1; break; - case 'f': - subsystem = EARGF(usage()); - break; case 'k': showudev = 0; break; case 'u': showkernel = 0; break; - case 'r': - runpath = EARGF(usage()); - break; default: usage(); } ARGEND; - fds.events = POLLIN; - fds.fd = init_netlink_socket(); - listfd = fds.fd; + child.fd = -1; + child.argc = argc; + child.argv = argv; + + fds[0].events = POLLIN; + fds[0].fd = init_netlink_socket(); + + fds[1].fd = child.fd; + fds[1].events = POLLERR; + + listfd = fds[0].fd; if (dofork) { if (daemon(0, 0) < 0) @@ -201,9 +211,15 @@ main(int argc, char *argv[]) disableoom(); buf[sizeof(buf)-1] = '\0'; - while (poll(&fds, 1, -1) > -1) { - clearenv(); - setenv("PATH", "/sbin:/bin", 1); + while (poll(fds, 2, -1) > -1) { + if (fds[1].revents & POLLERR) { + dbg("handler exited"); + close(child.fd); + fds[1].fd = child.fd = -1; + } + + if (!(fds[0].revents & POLLIN)) + continue; iov.iov_base = &buf; iov.iov_len = sizeof(buf); @@ -215,7 +231,7 @@ main(int argc, char *argv[]) hdr.msg_name = &cnls; hdr.msg_namelen = sizeof(cnls); - len = recvmsg(fds.fd, &hdr, 0); + len = recvmsg(fds[0].fd, &hdr, 0); if (len < 0) { if (errno == EINTR) continue; @@ -255,40 +271,8 @@ main(int argc, char *argv[]) if (cnls.nl_pid > 0) continue; } - - slen = 0; - for (i = 0; i < len; i += slen + 1) { - key = buf + i; - value = strchr(key, '='); - slen = strlen(buf+i); - - if (!slen || value == NULL) - continue; - if (subsystem && !strncmp(key, "SUBSYSTEM=", 10) - && !strstr(key+10, subsystem)) { - dbg("subsystem filter '%s' applied.", - subsystem); - break; - } - - value[0] = '\0'; - value++; - - /* - * We generally trust the kernel. But there - * might be some udev flaw. (It's >20k sloc!) - */ - if (strcmp(key, "PATH")) { - setenv(key, value, 1); - dbg("%s = \"%s\"", key, value); - } - } - if (getenv("ACTION") != NULL && - getenv("DEVPATH") != NULL && - getenv("SUBSYSTEM") != NULL && - getenv("SEQNUM") != NULL) { - child(runpath); - } + write_to_handler(&child, buf, len); + fds[1].fd = child.fd; } shutdown(listfd, SHUT_RDWR); -- cgit v1.2.3