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