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