rfkilld

An rfkill daemon, which runs scripts according to rfkill events.
git clone git://r-36.net/rfkilld
Log | Files | Refs | LICENSE

commit 361feba39eb798d0d3c1c037a4b77d4d8fbadf9e
parent 8e7750e0150e3975826c7922043193fdca882dd0
Author: Christoph Lohmann <20h@r-36.net>
Date:   Wed,  2 May 2012 21:20:50 +0200

Removing udev, fixing arg.h and fixing the scripts.

Diffstat:
arg.h | 42++++++++++++++++++++++++++++++++----------
config.mk | 10+++++-----
etc/rfkilld/bluetooth.sh | 2+-
etc/rfkilld/wlan.sh | 2+-
etc/rfkilld/wwan.sh | 4++--
rfkilld.c | 286++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
6 files changed, 246 insertions(+), 100 deletions(-)

diff --git a/arg.h b/arg.h @@ -1,18 +1,40 @@ -#ifndef ARG_H -#define ARG_H +/* + * Copy me if you can. + * by 20h + */ -#define USED(x) ((void)(x)) +#ifndef __ARG_H__ +#define __ARG_H__ extern char *argv0; -#define ARGBEGIN for(argv0 = *argv, argv++, argc--;\ - argv[0] && argv[0][0]=='-' && argv[0][1];\ - argc--, argv++) {\ +#define USED(x) ((void)(x)) + +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ char _argc;\ - _argc = argv[0][1];\ - switch(_argc) -#define ARGEND USED(_argc);} USED(argv);USED(argc); -#define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ + 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 diff --git a/config.mk b/config.mk @@ -1,21 +1,21 @@ # rfkilld metadata NAME = rfkilld -VERSION = 0.3 +VERSION = 0.4 # Customize below to fit your system # paths -PREFIX ?= /usr/local +PREFIX = /usr/local MANPREFIX = ${PREFIX}/share/man # includes and libs INCS = -I. -I/usr/include -LIBS = -L/usr/lib -lc -ludev +LIBS = -L/usr/lib -lc # flags -CPPFLAGS = -DVERSION=\"${VERSION}\" +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_GNU_SOURCE CFLAGS = -g -std=gnu99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} -LDFLAGS = -g ${LIBS} +LDFLAGS = -static -g ${LIBS} #LDFLAGS = -s ${LIBS} # compiler and linker diff --git a/etc/rfkilld/bluetooth.sh b/etc/rfkilld/bluetooth.sh @@ -1,7 +1,7 @@ #!/bin/sh case $1 in - 0) + 0|2) ;; 1) rfkill block bluetooth diff --git a/etc/rfkilld/wlan.sh b/etc/rfkilld/wlan.sh @@ -1,7 +1,7 @@ #!/bin/sh case $1 in - 0) + 0|2) conn -k wifi ;; 1) diff --git a/etc/rfkilld/wwan.sh b/etc/rfkilld/wwan.sh @@ -1,9 +1,9 @@ #!/bin/sh case $1 in - 0) + 0|2) ;; - 1|2) + 1) # conn -s wwan ;; *) diff --git a/rfkilld.c b/rfkilld.c @@ -6,28 +6,71 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> -#include <libudev.h> #include <poll.h> #include <ctype.h> #include <string.h> #include <stdarg.h> #include <syslog.h> #include <signal.h> +#include <errno.h> #include <strings.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/socket.h> #include <fcntl.h> +#include <linux/types.h> +#include <linux/netlink.h> #include "arg.h" char *argv0; char *etcdir = "/etc/rfkilld"; -char *lastname = NULL; -char *lasttype = NULL; -char *laststate = NULL; -int running = 1; -int dolog = 0; +char lastname[129]; +char lasttype[129]; +int listfd = -1; +int dolog = 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"); + } +} int setnonblocking(int fd) @@ -47,7 +90,7 @@ void runifexecutable(char *file, char *oname, char *ostate) { char cmd[512], name[64], state[16]; - int pid; + int pid, fd; strncpy(name, oname, sizeof(name)-1); name[sizeof(name)-1] = '\0'; @@ -57,9 +100,15 @@ runifexecutable(char *file, char *oname, char *ostate) snprintf(cmd, sizeof(cmd), "%s/%s.sh", etcdir, file); if (!access(cmd, X_OK)) { if (!(pid = fork())) { - if (!fork()) + if (!fork()) { + fd = open("/dev/null", O_RDWR); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); if(execl(cmd, name, state, NULL) < 0) - perror("execl"); + edie("execl"); + } exit(0); } waitpid(pid, NULL, 0); @@ -67,40 +116,17 @@ runifexecutable(char *file, char *oname, char *ostate) } void -runscripts(struct udev_device *dev) +runscripts(char *name, char *type, char *state) { - struct udev_list_entry *props; - char *type, *name, *state; - - props = udev_device_get_properties_list_entry(dev); - type = (char *)udev_device_get_property_value(dev, "RFKILL_NAME"); - name = (char *)udev_device_get_property_value(dev, "RFKILL_TYPE"); - state = (char *)udev_device_get_property_value(dev, "RFKILL_STATE"); - - if (lasttype != NULL && lastname != NULL && laststate != NULL) { - if (!strcmp(lasttype, type) && !strcmp(lastname, name) - && !strcmp(laststate, state)) - goto runscriptshandlecleanup; - } + dbg("runscripts: %s %s %s", name, type, state); - if (dolog) + if (dolog) { syslog(LOG_NOTICE, "name: %s; type: %s; state: %s;\n", name, type, state); + } runifexecutable(name, type, state); runifexecutable(type, name, state); - -runscriptshandlecleanup: - if (lasttype != NULL) - free(lasttype); - if (lastname != NULL) - free(lastname); - if (laststate != NULL) - free(laststate); - - lasttype = strdup(type); - lastname = strdup(name); - laststate = strdup(state); } void @@ -117,7 +143,11 @@ sighandler(int sig) case SIGTERM: if (dolog) closelog(); - running = 0; + if (listfd >= 0) { + shutdown(listfd, SHUT_RDWR); + close(listfd); + } + exit(0); break; default: break; @@ -140,27 +170,34 @@ initsignals(void) void usage(void) { - fprintf(stderr, "usage: %s [-hbl] [-e etcdir]\n", - argv0); - fflush(stderr); - exit(1); + die("usage: %s [-hbdl] [-e etcdir]\n", argv0); } int main(int argc, char *argv[]) { - struct udev *udev; - struct udev_monitor *mon; - struct udev_device *dev; - struct pollfd fds[1]; - int ret, dodaemonize; + struct sockaddr_nl nls, cnls; + struct msghdr hdr; + struct iovec iov; + char buf[4097], *key, *value, *type, *name, *state, + cbuf[CMSG_SPACE(sizeof(struct ucred))], *subsystem; + struct cmsghdr *chdr; + struct ucred *cred; + struct pollfd fds; + int i, len, slen, dodaemonize; dodaemonize = 0; + memset(lastname, 0, sizeof(lastname)); + memset(lasttype, 0, sizeof(lasttype)); ARGBEGIN { case 'b': dodaemonize = 1; break; + case 'd': + printf("dodebug = 1\n"); + dodebug = 1; + break; case 'l': dolog = 1; break; @@ -171,54 +208,141 @@ main(int argc, char *argv[]) usage(); } ARGEND; - if(dodaemonize) - daemon(0, 0); + 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"); + } + slen = 1; + if (setsockopt(fds.fd, SOL_SOCKET, SO_PASSCRED, &slen, + sizeof(slen)) < 0) { + edie("setsockopt"); + } + + if (bind(fds.fd, (void *)&nls, sizeof(nls))) + edie("bind"); + + if(dodaemonize) { + if (daemon(0, 0) < 0) + edie("daemon"); + umask(022); + } if(dolog) openlog("rfkilld", 0, LOG_DAEMON); initsignals(); - udev = udev_new(); - if (!udev) { - perror("udev_new"); - exit(1); - } + while (poll(&fds, 1, -1) > -1) { + iov.iov_base = &buf; + iov.iov_len = sizeof(buf); + memset(&hdr, 0, sizeof(hdr)); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + hdr.msg_control = cbuf; + hdr.msg_controllen = sizeof(cbuf); + hdr.msg_name = &cnls; + hdr.msg_namelen = sizeof(cnls); - mon = udev_monitor_new_from_netlink(udev, "kernel"); - udev_monitor_filter_add_match_subsystem_devtype(mon, "rfkill", NULL); - udev_monitor_enable_receiving(mon); + len = recvmsg(fds.fd, &hdr, 0); + if (len < 0) { + if (errno == EINTR) + continue; + edie("recvmsg"); + } + if (len < 32 || len >= sizeof(buf)) + continue; - fds[0].fd = udev_monitor_get_fd(mon); - fds[0].events = POLLIN|POLLPRI; - if (setnonblocking(fds[0].fd)) { - perror("setnonblocking"); - exit(1); - } - while(running) { - ret = poll(fds, 1, -1); - if (ret > 0) { - if ((fds[0].revents & POLLIN) \ - || (fds[0].revents & POLLPRI)) { - dev = udev_monitor_receive_device(mon); - if (dev) { - runscripts(dev); - udev_device_unref(dev); - } + chdr = CMSG_FIRSTHDR(&hdr); + if (chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS) + continue; + + /* + * Don't allow anyone but root to send us messages. + * + * We will allow users to send us messages, when + * udev is enabled. Udev is just a toy you should + * only use for testing. + */ + cred = (struct ucred *)CMSG_DATA(chdr); + if (cred->uid != 0) + continue; + + if (!memcmp(buf, "libudev", 8)) { + continue; + } else { + if (cnls.nl_pid > 0) + continue; + } + + type = NULL; + name = NULL; + state = NULL; + for (i = 0; i < len; i += slen + 1) { + key = buf + i; + value = strchr(key, '='); + slen = strlen(buf+i); + if (!slen || value == NULL) + continue; + + value[0] = '\0'; + value++; + + printf("%s = %s\n", key, value); + if (!strcmp(key, "SUBSYSTEM")) + subsystem = value; + if (!strcmp(key, "RFKILL_NAME")) + name = value; + if (!strcmp(key, "RFKILL_TYPE")) + type = value; + /* + * 0 -> soft blocked + * 1 -> unblocked + * 2 -> hard blocked + */ + if (!strcmp(key, "RFKILL_STATE")) + state = value; + } + dbg("name = %s, type = %s, state = %s", name, type, + state); + if (strcmp(subsystem, "rfkill")) + continue; + + if (name != NULL && type != NULL && state != NULL) { + if (strcmp(name, lastname) && + strcmp(type, lasttype)) { + slen = strlen(name); + if (slen > sizeof(lastname)-1) + die("name is too large\n"); + memmove(lastname, name, slen+1); + + slen = strlen(type); + if (slen > sizeof(lasttype)-1) + die("type is too large\n"); + memmove(lasttype, type, slen+1); + + runscripts(name, type, state); } } } - udev_monitor_unref(mon); - udev_unref(udev); if (dolog) closelog(); - if (lasttype != NULL) - free(lasttype); - if (lastname != NULL) - free(lastname); - if (laststate != NULL) - free(laststate); - exit(0); + + shutdown(listfd, SHUT_RDWR); + close(listfd); + + return 0; }