rfkilld

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

rfkilld.c (6347B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <unistd.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <poll.h>
     10 #include <ctype.h>
     11 #include <string.h>
     12 #include <stdarg.h>
     13 #include <syslog.h>
     14 #include <signal.h>
     15 #include <libgen.h>
     16 #include <errno.h>
     17 #include <strings.h>
     18 #include <sys/wait.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <sys/socket.h>
     22 #include <fcntl.h>
     23 #include <linux/types.h>
     24 #include <linux/netlink.h>
     25 
     26 #include "arg.h"
     27 
     28 char *argv0;
     29 char *etcdir = "/etc/rfkilld";
     30 char lastname[129];
     31 char lasttype[129];
     32 int listfd = -1;
     33 int dolog = 0, dodebug = 0;
     34 
     35 void
     36 edie(char *fmt, ...)
     37 {
     38 	va_list fmtargs;
     39 
     40 	va_start(fmtargs, fmt);
     41 	vfprintf(stderr, fmt, fmtargs);
     42 	va_end(fmtargs);
     43 	fprintf(stderr, ": ");
     44 
     45 	perror(NULL);
     46 
     47 	exit(1);
     48 }
     49 
     50 void
     51 die(char *fmt, ...)
     52 {
     53 	va_list fmtargs;
     54 
     55 	va_start(fmtargs, fmt);
     56 	vfprintf(stderr, fmt, fmtargs);
     57 	va_end(fmtargs);
     58 
     59 	exit(1);
     60 }
     61 
     62 void
     63 dbg(char *fmt, ...)
     64 {
     65 	va_list fmtargs;
     66 
     67 	if (dodebug) {
     68 		fprintf(stderr, "%s: ", argv0);
     69 		va_start(fmtargs, fmt);
     70 		vfprintf(stderr, fmt, fmtargs);
     71 		va_end(fmtargs);
     72 		fprintf(stderr, "\n");
     73 	}
     74 }
     75 
     76 int
     77 setnonblocking(int fd)
     78 {
     79 	int flags;
     80 
     81 	flags = fcntl(fd, F_GETFL);
     82 	if (flags < 0)
     83 		return 1;
     84 	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
     85 		return 1;
     86 
     87 	return 0;
     88 }
     89 
     90 void
     91 runifexecutable(char *file, char *oname, char *ostate)
     92 {
     93 	char cmd[512], name[64], state[16];
     94 	int pid, fd;
     95 
     96 	strncpy(name, oname, sizeof(name)-1);
     97 	name[sizeof(name)-1] = '\0';
     98 	strncpy(state, ostate, sizeof(state)-1);
     99 	state[sizeof(state)-1] = '\0';
    100 
    101 	snprintf(cmd, sizeof(cmd), "%s/%s.sh", etcdir, file);
    102 	if (!access(cmd, X_OK)) {
    103 		if (!(pid = fork())) {
    104 			if (!fork()) {
    105 				fd = open("/dev/null", O_RDWR);
    106 				if (fd < 0)
    107 					edie("open");
    108 				dup2(fd, 1);
    109 				dup2(fd, 2);
    110 				if (fd > 2)
    111 					close(fd);
    112 				if(execl(cmd, basename(name),
    113 						state, name, NULL) < 0) {
    114 					edie("execl");
    115 				}
    116 			}
    117 			exit(0);
    118 		}
    119 		waitpid(pid, NULL, 0);
    120 	}
    121 }
    122 
    123 void
    124 runscripts(char *name, char *type, char *state)
    125 {
    126 	dbg("runscripts: %s %s %s", name, type, state);
    127 
    128 	if (dolog) {
    129 		syslog(LOG_NOTICE, "name: %s; type: %s; state: %s;\n",
    130 				name, type, state);
    131 	}
    132 
    133 	runifexecutable(name, type, state);
    134 	runifexecutable(type, name, state);
    135 }
    136 
    137 void
    138 sighandler(int sig)
    139 {
    140 	switch(sig) {
    141 	case SIGCHLD:
    142 		while(waitpid(-1, NULL, WNOHANG) > 0);
    143 		break;
    144 	case SIGHUP:
    145 	case SIGINT:
    146 	case SIGQUIT:
    147 	case SIGABRT:
    148 	case SIGTERM:
    149 		if (dolog)
    150 			closelog();
    151 		if (listfd >= 0) {
    152 			shutdown(listfd, SHUT_RDWR);
    153 			close(listfd);
    154 		}
    155 		exit(0);
    156 		break;
    157 	default:
    158 		break;
    159 	}
    160 }
    161 
    162 void
    163 initsignals(void)
    164 {
    165 	signal(SIGCHLD, sighandler);
    166 	signal(SIGHUP, sighandler);
    167 	signal(SIGINT, sighandler);
    168 	signal(SIGQUIT, sighandler);
    169 	signal(SIGABRT, sighandler);
    170 	signal(SIGTERM, sighandler);
    171 
    172 	signal(SIGPIPE, SIG_IGN);
    173 }
    174 
    175 void
    176 usage(void)
    177 {
    178 	die("usage: %s [-hfdl] [-e etcdir]\n", argv0);
    179 }
    180 
    181 int
    182 main(int argc, char *argv[])
    183 {
    184 	struct sockaddr_nl nls, cnls;
    185 	struct msghdr hdr;
    186 	struct iovec iov;
    187 	char buf[4097], *key, *value, *type, *name, *state,
    188 	     cbuf[CMSG_SPACE(sizeof(struct ucred))], *subsystem;
    189 	struct cmsghdr *chdr;
    190 	struct ucred *cred;
    191 	struct pollfd fds;
    192 	int i, len, slen, dodaemonize;
    193 
    194 	dodaemonize = 1;
    195 	memset(lastname, 0, sizeof(lastname));
    196 	memset(lasttype, 0, sizeof(lasttype));
    197 
    198 	ARGBEGIN {
    199 	case 'f':
    200 		dodaemonize = 0;
    201 		break;
    202 	case 'd':
    203 		printf("dodebug = 1\n");
    204 		dodebug = 1;
    205 		break;
    206 	case 'l':
    207 		dolog = 1;
    208 		break;
    209 	case 'e':
    210 		etcdir = EARGF(usage());
    211 		break;
    212 	default:
    213 		usage();
    214 	} ARGEND;
    215 
    216 	memset(&nls, 0, sizeof(nls));
    217 	nls.nl_family = AF_NETLINK;
    218 	nls.nl_pid = getpid();
    219 	nls.nl_groups = -1;
    220 
    221 	fds.events = POLLIN;
    222 	fds.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
    223 	listfd = fds.fd;
    224 	if (fds.fd < 0)
    225 		edie("socket");
    226 
    227 	slen = 128*1024*1024;
    228 	if (setsockopt(fds.fd, SOL_SOCKET, SO_RCVBUFFORCE, &slen,
    229 				sizeof(slen)) < 0) {
    230 		edie("setsockopt");
    231 	}
    232 	slen = 1;
    233 	if (setsockopt(fds.fd, SOL_SOCKET, SO_PASSCRED, &slen,
    234 				sizeof(slen)) < 0) {
    235 		edie("setsockopt");
    236 	}
    237 
    238 	if (bind(fds.fd, (void *)&nls, sizeof(nls)))
    239 		edie("bind");
    240 
    241 	if(dodaemonize) {
    242 		if (daemon(0, 0) < 0)
    243 			edie("daemon");
    244 		umask(022);
    245 	}
    246 
    247 	if(dolog)
    248 		openlog("rfkilld", 0, LOG_DAEMON);
    249 
    250 	initsignals();
    251 
    252 	while (poll(&fds, 1, -1) > -1) {
    253 		iov.iov_base = &buf;
    254 		iov.iov_len = sizeof(buf);
    255 		memset(&hdr, 0, sizeof(hdr));
    256 		hdr.msg_iov = &iov;
    257 		hdr.msg_iovlen = 1;
    258 		hdr.msg_control = cbuf;
    259 		hdr.msg_controllen = sizeof(cbuf);
    260 		hdr.msg_name = &cnls;
    261 		hdr.msg_namelen = sizeof(cnls);
    262 
    263 		len = recvmsg(fds.fd, &hdr, 0);
    264 		if (len < 0) {
    265 			if (errno == EINTR)
    266 				continue;
    267 			edie("recvmsg");
    268 		}
    269 		if (len < 32 || len >= sizeof(buf))
    270 			continue;
    271 
    272 		chdr = CMSG_FIRSTHDR(&hdr);
    273 		if (chdr == NULL || chdr->cmsg_type != SCM_CREDENTIALS)
    274 			continue;
    275 
    276 		/*
    277 		 * Don't allow anyone but root to send us messages.
    278 		 *
    279 		 * We will allow users to send us messages, when
    280 		 * udev is enabled. Udev is just a toy you should
    281 		 * only use for testing.
    282 		 */
    283 		cred = (struct ucred *)CMSG_DATA(chdr);
    284 		if (cred->uid != 0)
    285 			continue;
    286 
    287 		if (!memcmp(buf, "libudev", 8)) {
    288 			continue;
    289 		} else {
    290 			if (cnls.nl_pid > 0)
    291 				continue;
    292 		}
    293 
    294 		type = NULL;
    295 		name = NULL;
    296 		state = NULL;
    297 		for (i = 0; i < len; i += slen + 1) {
    298 			key = buf + i;
    299 			value = strchr(key, '=');
    300 			slen = strlen(buf+i);
    301 			if (!slen || value == NULL)
    302 				continue;
    303 
    304 			value[0] = '\0';
    305 			value++;
    306 
    307 			printf("%s = %s\n", key, value);
    308 			if (!strcmp(key, "SUBSYSTEM"))
    309 				subsystem = value;
    310 			if (!strcmp(key, "RFKILL_NAME"))
    311 				name = value;
    312 			if (!strcmp(key, "RFKILL_TYPE"))
    313 				type = value;
    314 			/*
    315 			 * 0 -> soft blocked
    316 			 * 1 -> unblocked
    317 			 * 2 -> hard blocked
    318 			 */
    319 			if (!strcmp(key, "RFKILL_STATE"))
    320 				state = value;
    321 		}
    322 		dbg("name = %s, type = %s, state = %s", name, type,
    323 				state);
    324 		if (strcmp(subsystem, "rfkill"))
    325 			continue;
    326 
    327 		if (name != NULL && type != NULL && state != NULL) {
    328 			if (strcmp(name, lastname) &&
    329 					strcmp(type, lasttype)) {
    330 				slen = strlen(name);
    331 				if (slen > sizeof(lastname)-1)
    332 					die("name is too large\n");
    333 				memmove(lastname, name, slen+1);
    334 
    335 				slen = strlen(type);
    336 				if (slen > sizeof(lasttype)-1)
    337 					die("type is too large\n");
    338 				memmove(lasttype, type, slen+1);
    339 
    340 				runscripts(name, type, state);
    341 			}
    342 		}
    343 	}
    344 
    345 	if (dolog)
    346 		closelog();
    347 
    348 	shutdown(listfd, SHUT_RDWR);
    349 	close(listfd);
    350 
    351 	return 0;
    352 }
    353