Initial commit. - nldev - NetLink DEVice manager; a lightweight netlink frontend for mdev.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit e2c236b300e4fa9f4f7fdb672251892c2be3e149
 (HTM) Author: Christoph Lohmann <20h@r-36.net>
       Date:   Sun, 15 Apr 2012 23:08:48 +0200
       
       Initial commit.
       
       Diffstat:
         LICENSE                             |      21 +++++++++++++++++++++
         Makefile                            |      56 +++++++++++++++++++++++++++++++
         arg.h                               |      41 +++++++++++++++++++++++++++++++
         config.mk                           |      23 +++++++++++++++++++++++
         nldev.1                             |      69 ++++++++++++++++++++++++++++++
         nldev.c                             |     288 +++++++++++++++++++++++++++++++
       
       6 files changed, 498 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/LICENSE b/LICENSE
       @@ -0,0 +1,21 @@
       +MIT/X Consortium License
       +
       +© 2012 Christoph Lohmann <20h@r-36.net>
       +
       +Permission is hereby granted, free of charge, to any person obtaining a
       +copy of this software and associated documentation files (the "Software"),
       +tto deal in the Software without restriction, including without limitation
       +tthe rights to use, copy, modify, merge, publish, distribute, sublicense,
       +and/or sell copies of the Software, and to permit persons to whom the
       +Software is furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in
       +all copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
       +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
       +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
       +DEALINGS IN THE SOFTWARE.
 (DIR) diff --git a/Makefile b/Makefile
       @@ -0,0 +1,56 @@
       +# nldev - NetLink Device manager 
       +# See LICENSE file for copyright and license details.
       +
       +include config.mk
       +
       +SRC = ${NAME}.c
       +OBJ = ${SRC:.c=.o}
       +
       +all: options ${NAME}
       +
       +options:
       +        @echo ${NAME} build options:
       +        @echo "CFLAGS   = ${CFLAGS}"
       +        @echo "LDFLAGS  = ${LDFLAGS}"
       +        @echo "CC       = ${CC}"
       +
       +.c.o:
       +        @echo CC $<
       +        @${CC} -c ${CFLAGS} $<
       +
       +${OBJ}: config.mk
       +
       +${NAME}: ${OBJ}
       +        @echo CC -o $@
       +        @${CC} -o $@ ${OBJ} ${LDFLAGS}
       +
       +clean:
       +        @echo cleaning
       +        @rm -f ${NAME} ${OBJ} ${NAME}-${VERSION}.tar.gz
       +
       +dist: clean
       +        @echo creating dist tarball
       +        @mkdir -p ${NAME}-${VERSION}
       +        @cp -R LICENSE Makefile README.md config.mk \
       +                ${SRC} ${NAME}.1 *.h ${NAME}-${VERSION}
       +        @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION}
       +        @gzip ${NAME}-${VERSION}.tar
       +        @rm -rf ${NAME}-${VERSION}
       +
       +install: all
       +        @echo installing executable file to ${DESTDIR}${PREFIX}/bin
       +        @mkdir -p ${DESTDIR}${PREFIX}/bin
       +        @cp -f ${NAME} ${DESTDIR}${PREFIX}/bin
       +        @chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME}
       +        @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
       +        @mkdir -p ${DESTDIR}${MANPREFIX}/man1
       +        @cp -f ${NAME}.1 ${DESTDIR}${MANPREFIX}/man1
       +        @chmod 644 ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
       +
       +uninstall:
       +        @echo removing executable file from ${DESTDIR}${PREFIX}/bin
       +        @rm -f ${DESTDIR}${PREFIX}/bin/${NAME}
       +        @echo removing manual page from ${DESTDIR}${PREFIX}/man1
       +        @rm -f ${DESTDIR}${MANPREFIX}/man1/${NAME}.1
       +
       +.PHONY: all options clean dist install uninstall
 (DIR) diff --git a/arg.h b/arg.h
       @@ -0,0 +1,41 @@
       +/*
       + * Copy me if you can.
       + * by 20h
       + */
       +
       +#ifndef __ARG_H__
       +#define __ARG_H__
       +
       +extern char *argv0;
       +
       +#define USED(x) ((void)(x))
       +
       +#define ARGBEGIN        for (argv0 = *argv, argv++, argc--;\
       +                                        argv[0] && argv[0][1]\
       +                                        && argv[0][0] == '-';\
       +                                        argc--, argv++) {\
       +                                char _argc;\
       +                                char **_argv;\
       +                                if (argv[0][1] == '-' && argv[0][2] == '\0') {\
       +                                        argv++;\
       +                                        argc--;\
       +                                        break;\
       +                                }\
       +                                for (argv[0]++, _argv = argv; argv[0][0];\
       +                                                argv[0]++) {\
       +                                        if (_argv != argv)\
       +                                                break;\
       +                                        _argc = argv[0][0];\
       +                                        switch (_argc)
       +
       +#define ARGEND                        }\
       +                                USED(_argc);\
       +                        }\
       +                        USED(argv);\
       +                        USED(argc);
       +
       +#define EARGF(x)        ((argv[1] == NULL)? ((x), abort(), (char *)0) :\
       +                        (argc--, argv++, argv[0]))
       +
       +#endif
       +
 (DIR) diff --git a/config.mk b/config.mk
       @@ -0,0 +1,23 @@
       +# nldev metadata
       +NAME = nldev
       +VERSION = 0.3
       +
       +# Customize below to fit your system
       +
       +# paths
       +PREFIX ?= /usr
       +MANPREFIX = ${PREFIX}/share/man
       +
       +# includes and libs
       +INCS = -I. -I/usr/include
       +LIBS = -L/usr/lib -lc
       +
       +# flags
       +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE
       +CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
       +LDFLAGS = -static -g ${LIBS}
       +#LDFLAGS = -s ${LIBS}
       +
       +# compiler and linker
       +CC = cc
       +
 (DIR) diff --git a/nldev.1 b/nldev.1
       @@ -0,0 +1,69 @@
       +.Dd April 15, 2012 
       +.Dt NLDEV 1
       +.Os
       +.
       +.Sh NAME
       +.Nm nldev
       +.Nd a simple netlink device manager
       +.
       +.Sh SYNOPSIS
       +.Nm
       +.Bk -words
       +.Op Fl h
       +.Op Fl d
       +.Op Fl b 
       +.Op Fl k
       +.Op Fl l
       +.Op Fl f Ar subsystem
       +.Op Fl r Ar runpath 
       +.Ek
       +.
       +.Sh DESCRIPTION
       +.Bd -filled
       +.Nm
       +is a simple netlink device manager. It does this by emulating
       +tthe behaviour of the hotplug system built into the Linux kernel.
       +The defaults are meant to fit to the mdev applet included in
       +busybox.
       +.Ed
       +. 
       +.Sh OPTIONS
       +.Nm
       +options and default settings.
       +.Pp
       +.Bl -tag -width ".Fl test Ao Ar string Ac"
       +.
       +.It Fl b
       +Run in background.
       +.
       +.It Fl d 
       +Turn on debug messages. This will not work in conjunction with
       +-b.
       +.
       +.It Fl f Ar subsystem
       +This option will filter for the subsystem key in a netlink message
       +and compare it to the subsystem string.
       +.
       +.It Fl h
       +Show usage.
       +.
       +.It Fl k
       +Only show netlink messages from the kernel.
       +.
       +.Bd -filled
       +.It Fl l
       +Only show netlink messages from libudev.
       +.
       +.It Fl r Ar runpath
       +This option specifies the runpath for the helper that is launched
       +on every received netlink event (default: /bin/mdev).
       +.El
       +.
       +.Sh AUTHORS
       +See LICENSE file for authors in the distribution.
       +.
       +.Sh LICENSE
       +.Nm
       +is released under the MIT/X Consortium License.
       +.
       +
 (DIR) diff --git a/nldev.c b/nldev.c
       @@ -0,0 +1,288 @@
       +/*
       + * Copy me if you can.
       + * by 20h
       + */
       +
       +#include <unistd.h>
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdarg.h>
       +#include <poll.h>
       +#include <ctype.h>
       +#include <string.h>
       +#include <errno.h>
       +#include <signal.h>
       +#include <libgen.h>
       +#include <fcntl.h>
       +#include <sys/socket.h>
       +#include <sys/types.h>
       +#include <sys/wait.h>
       +#include <sys/stat.h>
       +#include <linux/types.h>
       +#include <linux/netlink.h>
       +
       +#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;
       +}
       +