rfkilld.c - rfkilld - An rfkill daemon, which runs scripts according to rfkill events.
 (HTM) git clone git://r-36.net/rfkilld
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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