From e2c236b300e4fa9f4f7fdb672251892c2be3e149 Mon Sep 17 00:00:00 2001 From: Christoph Lohmann <20h@r-36.net> Date: Sun, 15 Apr 2012 23:08:48 +0200 Subject: Initial commit. --- nldev.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 nldev.c (limited to 'nldev.c') diff --git a/nldev.c b/nldev.c new file mode 100644 index 0000000..400379a --- /dev/null +++ b/nldev.c @@ -0,0 +1,288 @@ +/* + * Copy me if you can. + * by 20h + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arg.h" + +char *argv0; +int listfd = -1; +int dofork = 0, dodebug = 0; + +void +edie(char *fmt, ...) +{ + va_list fmtargs; + + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); + fprintf(stderr, ": "); + + perror(NULL); + + exit(1); +} + +void +die(char *fmt, ...) +{ + va_list fmtargs; + + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); + + exit(1); +} + +void +dbg(char *fmt, ...) +{ + va_list fmtargs; + + if (dodebug) { + fprintf(stderr, "%s: ", argv0); + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); + fprintf(stderr, "\n"); + } +} + +void +disableoom(void) +{ + int fd; + + fd = open("/proc/self/oom_score_adj", O_RDWR); + if (fd < 0) { + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd < 0) + edie("disabling oom failed."); + write(fd, "-17", 3); + close(fd); + } else { + write(fd, "-1000", 5); + close(fd); + } +} + +void +child(char *runpath) +{ + int fd, pid; + + if (!(pid = fork())) { + if (dofork) { + fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + if (write(0, 0, 0) < 0) + dup2(fd, 0); + if (write(2, 0, 0) < 0) + dup2(fd, 0); + if (fd > 2) + close(fd); + } + } + + 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 +sighandler(int sig) +{ + switch(sig) { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGABRT: + case SIGTERM: + case SIGKILL: + if (listfd >= 0) { + shutdown(listfd, SHUT_RDWR); + close(listfd); + } + exit(0); + break; + default: + break; + } +} + +void +initsignals(void) +{ + signal(SIGHUP, sighandler); + signal(SIGINT, sighandler); + signal(SIGQUIT, sighandler); + signal(SIGABRT, sighandler); + signal(SIGTERM, sighandler); + signal(SIGKILL, sighandler); + + signal(SIGCHLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); +} + +void +usage(void) +{ + die("usage: %s [-hdb] [-kl] [-f subsystem] [-r run]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_nl nls; + struct pollfd fds; + char buf[4097], *subsystem, *runpath, *key, *value; + int i, len, slen, showudev, showkernel; + + showkernel = 1; + showudev = 1; + subsystem = NULL; + runpath = "/bin/mdev"; + + ARGBEGIN { + case 'b': + dofork = 1; + break; + case 'd': + dodebug = 1; + break; + case 'f': + subsystem = EARGF(usage()); + break; + case 'k': + showudev = 0; + break; + case 'l': + showkernel = 0; + break; + case 'r': + runpath = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + memset(&nls, 0, sizeof(nls)); + nls.nl_family = AF_NETLINK; + nls.nl_pid = getpid(); + nls.nl_groups = -1; + + fds.events = POLLIN; + fds.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + listfd = fds.fd; + if (fds.fd < 0) + edie("socket"); + + slen = 128*1024*1024; + if (setsockopt(fds.fd, SOL_SOCKET, SO_RCVBUFFORCE, &slen, + sizeof(slen)) < 0) { + edie("setsockopt"); + } + + if (bind(fds.fd, (void *)&nls, sizeof(nls))) + edie("bind"); + + if (dofork) { + if (daemon(0, 0) < 0) + edie("daemon"); + umask(022); + } + + initsignals(); + disableoom(); + + buf[sizeof(buf)-1] = '\0'; + while (poll(&fds, 1, -1) > -1) { + unsetenv("ACTION"); + unsetenv("DEVPATH"); + unsetenv("SUBSYSTEM"); + unsetenv("SEQNUM"); + unsetenv("MODALIAS"); + unsetenv("DEVNAME"); + unsetenv("DEVTYPE"); + unsetenv("MAJOR"); + unsetenv("MINOR"); + unsetenv("FIRMWARE"); + + len = recv(fds.fd, buf, sizeof(buf)-1, MSG_DONTWAIT); + if (len < 0) + edie("recv"); + + if (strstr(buf, "libudev")) { + if (!showudev) + continue; + } else { + if (!showkernel) + continue; + } + + 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); + } + } + + shutdown(listfd, SHUT_RDWR); + close(listfd); + + return 0; +} + -- cgit v1.2.3