]>
Commit | Line | Data |
---|---|---|
7fafc032 | 1 | /* |
53921bfa | 2 | * udevd.c - hotplug event serializer |
7fafc032 | 3 | * |
7fafc032 | 4 | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
2f6cbd19 | 5 | * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> |
7fafc032 KS |
6 | * |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | * | |
21 | */ | |
22 | ||
a695feae | 23 | #include <stddef.h> |
90c210eb | 24 | #include <sys/wait.h> |
7fafc032 KS |
25 | #include <signal.h> |
26 | #include <unistd.h> | |
27 | #include <errno.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
a695feae | 31 | #include <time.h> |
53921bfa KS |
32 | #include <sys/types.h> |
33 | #include <sys/socket.h> | |
34 | #include <sys/un.h> | |
2f6cbd19 | 35 | #include <sys/time.h> |
7fafc032 | 36 | |
a695feae | 37 | #include "list.h" |
7fafc032 | 38 | #include "udev.h" |
35b7d88c | 39 | #include "udev_version.h" |
7fafc032 KS |
40 | #include "udevd.h" |
41 | #include "logging.h" | |
42 | ||
53921bfa | 43 | static int expected_seqnum = 0; |
2f6cbd19 KS |
44 | volatile static int children_waiting; |
45 | volatile static int msg_q_timeout; | |
7fafc032 | 46 | |
a695feae | 47 | LIST_HEAD(msg_list); |
53921bfa | 48 | LIST_HEAD(exec_list); |
35b7d88c | 49 | LIST_HEAD(running_list); |
7fafc032 | 50 | |
2f6cbd19 KS |
51 | static void exec_queue_manager(void); |
52 | static void msg_queue_manager(void); | |
7fafc032 | 53 | |
d026a35d | 54 | #ifdef LOG |
51a8bb2f | 55 | unsigned char logname[42]; |
d026a35d | 56 | void log_message (int level, const char *format, ...) |
51a8bb2f | 57 | { |
d026a35d GKH |
58 | va_list args; |
59 | ||
60 | va_start(args, format); | |
61 | vsyslog(level, format, args); | |
62 | va_end(args); | |
51a8bb2f | 63 | } |
d026a35d | 64 | #endif |
51a8bb2f | 65 | |
53921bfa | 66 | static void msg_dump_queue(void) |
7fafc032 | 67 | { |
53921bfa | 68 | struct hotplug_msg *msg; |
7fafc032 | 69 | |
53921bfa KS |
70 | list_for_each_entry(msg, &msg_list, list) |
71 | dbg("sequence %d in queue", msg->seqnum); | |
35b7d88c KS |
72 | } |
73 | ||
53921bfa | 74 | static void msg_dump(struct hotplug_msg *msg) |
35b7d88c | 75 | { |
53921bfa KS |
76 | dbg("sequence %d, '%s', '%s', '%s'", |
77 | msg->seqnum, msg->action, msg->devpath, msg->subsystem); | |
35b7d88c KS |
78 | } |
79 | ||
53921bfa | 80 | static struct hotplug_msg *msg_create(void) |
35b7d88c | 81 | { |
53921bfa | 82 | struct hotplug_msg *new_msg; |
35b7d88c | 83 | |
53921bfa | 84 | new_msg = malloc(sizeof(struct hotplug_msg)); |
2f6cbd19 | 85 | if (new_msg == NULL) |
53921bfa | 86 | dbg("error malloc"); |
53921bfa | 87 | return new_msg; |
35b7d88c KS |
88 | } |
89 | ||
2f6cbd19 | 90 | static void run_queue_delete(struct hotplug_msg *msg) |
8e2229c4 | 91 | { |
2f6cbd19 KS |
92 | list_del(&msg->list); |
93 | free(msg); | |
94 | exec_queue_manager(); | |
8e2229c4 KS |
95 | } |
96 | ||
53921bfa KS |
97 | /* orders the message in the queue by sequence number */ |
98 | static void msg_queue_insert(struct hotplug_msg *msg) | |
35b7d88c | 99 | { |
53921bfa | 100 | struct hotplug_msg *loop_msg; |
35b7d88c | 101 | |
53921bfa KS |
102 | /* sort message by sequence number into list*/ |
103 | list_for_each_entry(loop_msg, &msg_list, list) | |
104 | if (loop_msg->seqnum > msg->seqnum) | |
105 | break; | |
106 | list_add_tail(&msg->list, &loop_msg->list); | |
107 | dbg("queued message seq %d", msg->seqnum); | |
35b7d88c | 108 | |
53921bfa KS |
109 | /* store timestamp of queuing */ |
110 | msg->queue_time = time(NULL); | |
35b7d88c | 111 | |
2f6cbd19 KS |
112 | /* run msg queue manager */ |
113 | msg_queue_manager(); | |
7fafc032 | 114 | |
53921bfa | 115 | return ; |
7fafc032 KS |
116 | } |
117 | ||
53921bfa | 118 | /* forks event and removes event from run queue when finished */ |
2f6cbd19 | 119 | static void udev_run(struct hotplug_msg *msg) |
7fafc032 | 120 | { |
90c210eb | 121 | pid_t pid; |
f8911dbb KS |
122 | char action[32]; |
123 | char devpath[256]; | |
124 | char *env[] = { action, devpath, NULL }; | |
125 | ||
126 | snprintf(action, sizeof(action), "ACTION=%s", msg->action); | |
127 | snprintf(devpath, sizeof(devpath), "DEVPATH=%s", msg->devpath); | |
90c210eb KS |
128 | |
129 | pid = fork(); | |
130 | switch (pid) { | |
131 | case 0: | |
33db4b8d | 132 | /* child */ |
f8911dbb | 133 | execle(UDEV_BIN, "udev", msg->subsystem, NULL, env); |
33db4b8d KS |
134 | dbg("exec of child failed"); |
135 | exit(1); | |
90c210eb KS |
136 | break; |
137 | case -1: | |
33db4b8d | 138 | dbg("fork of child failed"); |
2f6cbd19 KS |
139 | run_queue_delete(msg); |
140 | break; | |
90c210eb | 141 | default: |
2f6cbd19 KS |
142 | /* get SIGCHLD in main loop */ |
143 | dbg("==> exec seq %d [%d] working at '%s'", msg->seqnum, pid, msg->devpath); | |
144 | msg->pid = pid; | |
90c210eb | 145 | } |
7fafc032 KS |
146 | } |
147 | ||
53921bfa KS |
148 | /* returns already running task with devpath */ |
149 | static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg) | |
7fafc032 | 150 | { |
53921bfa | 151 | struct hotplug_msg *loop_msg; |
2f6cbd19 | 152 | list_for_each_entry(loop_msg, &running_list, list) |
53921bfa KS |
153 | if (strncmp(loop_msg->devpath, msg->devpath, sizeof(loop_msg->devpath)) == 0) |
154 | return loop_msg; | |
155 | return NULL; | |
7fafc032 KS |
156 | } |
157 | ||
2f6cbd19 KS |
158 | /* exec queue management routine executes the events and delays events for the same devpath */ |
159 | static void exec_queue_manager() | |
7fafc032 | 160 | { |
53921bfa KS |
161 | struct hotplug_msg *loop_msg; |
162 | struct hotplug_msg *tmp_msg; | |
a695feae | 163 | struct hotplug_msg *msg; |
53921bfa | 164 | |
2f6cbd19 KS |
165 | list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, list) { |
166 | msg = running_with_devpath(loop_msg); | |
167 | if (!msg) { | |
168 | /* move event to run list */ | |
169 | list_move_tail(&loop_msg->list, &running_list); | |
170 | udev_run(loop_msg); | |
171 | dbg("moved seq %d to running list", loop_msg->seqnum); | |
172 | } else { | |
173 | dbg("delay seq %d, cause seq %d already working on '%s'", | |
174 | loop_msg->seqnum, msg->seqnum, msg->devpath); | |
53921bfa | 175 | } |
53921bfa KS |
176 | } |
177 | } | |
178 | ||
2f6cbd19 | 179 | static void msg_move_exec(struct hotplug_msg *msg) |
86590cd5 | 180 | { |
2f6cbd19 KS |
181 | list_move_tail(&msg->list, &exec_list); |
182 | exec_queue_manager(); | |
183 | expected_seqnum = msg->seqnum+1; | |
184 | dbg("moved seq %d to exec, next expected is %d", | |
185 | msg->seqnum, expected_seqnum); | |
86590cd5 KS |
186 | } |
187 | ||
2f6cbd19 KS |
188 | /* msg queue management routine handles the timeouts and dispatches the events */ |
189 | static void msg_queue_manager() | |
53921bfa KS |
190 | { |
191 | struct hotplug_msg *loop_msg; | |
a695feae | 192 | struct hotplug_msg *tmp_msg; |
53921bfa | 193 | time_t msg_age = 0; |
a695feae | 194 | |
2f6cbd19 | 195 | dbg("msg queue manager, next expected is %d", expected_seqnum); |
a695feae | 196 | recheck: |
2f6cbd19 KS |
197 | list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, list) { |
198 | /* move event with expected sequence to the exec list */ | |
199 | if (loop_msg->seqnum == expected_seqnum) { | |
200 | msg_move_exec(loop_msg); | |
201 | continue; | |
a695feae | 202 | } |
35b7d88c | 203 | |
2f6cbd19 KS |
204 | /* move event with expired timeout to the exec list */ |
205 | msg_age = time(NULL) - loop_msg->queue_time; | |
206 | if (msg_age > EVENT_TIMEOUT_SEC-1) { | |
207 | msg_move_exec(loop_msg); | |
208 | goto recheck; | |
53921bfa | 209 | } else { |
2f6cbd19 | 210 | break; |
53921bfa | 211 | } |
2f6cbd19 KS |
212 | } |
213 | ||
214 | msg_dump_queue(); | |
215 | ||
216 | if (list_empty(&msg_list) == 0) { | |
217 | /* set timeout for remaining queued events */ | |
218 | struct itimerval itv = {{0, 0}, {EVENT_TIMEOUT_SEC - msg_age, 0}}; | |
219 | dbg("next event expires in %li seconds", | |
220 | EVENT_TIMEOUT_SEC - msg_age); | |
221 | setitimer(ITIMER_REAL, &itv, 0); | |
7fafc032 | 222 | } |
7fafc032 KS |
223 | } |
224 | ||
2f6cbd19 KS |
225 | /* receive the msg, do some basic sanity checks, and queue it */ |
226 | static void handle_msg(int sock) | |
7fafc032 | 227 | { |
53921bfa KS |
228 | struct hotplug_msg *msg; |
229 | int retval; | |
0028653c KS |
230 | struct msghdr smsg; |
231 | struct cmsghdr *cmsg; | |
232 | struct iovec iov; | |
233 | struct ucred *cred; | |
234 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; | |
a695feae | 235 | |
53921bfa KS |
236 | msg = msg_create(); |
237 | if (msg == NULL) { | |
238 | dbg("unable to store message"); | |
2f6cbd19 | 239 | return; |
7fafc032 | 240 | } |
7fafc032 | 241 | |
0028653c KS |
242 | iov.iov_base = msg; |
243 | iov.iov_len = sizeof(struct hotplug_msg); | |
244 | ||
245 | memset(&smsg, 0x00, sizeof(struct msghdr)); | |
246 | smsg.msg_iov = &iov; | |
247 | smsg.msg_iovlen = 1; | |
248 | smsg.msg_control = cred_msg; | |
249 | smsg.msg_controllen = sizeof(cred_msg); | |
250 | ||
251 | retval = recvmsg(sock, &smsg, 0); | |
53921bfa | 252 | if (retval < 0) { |
2f6cbd19 KS |
253 | if (errno != EINTR) |
254 | dbg("unable to receive message"); | |
255 | return; | |
53921bfa | 256 | } |
0028653c KS |
257 | cmsg = CMSG_FIRSTHDR(&smsg); |
258 | cred = (struct ucred *) CMSG_DATA(cmsg); | |
259 | ||
7b1cbec9 KS |
260 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { |
261 | dbg("no sender credentials received, message ignored"); | |
262 | goto skip; | |
263 | } | |
264 | ||
0028653c KS |
265 | if (cred->uid != 0) { |
266 | dbg("sender uid=%i, message ignored", cred->uid); | |
7b1cbec9 | 267 | goto skip; |
0028653c KS |
268 | } |
269 | ||
53921bfa KS |
270 | if (strncmp(msg->magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) { |
271 | dbg("message magic '%s' doesn't match, ignore it", msg->magic); | |
7b1cbec9 | 272 | goto skip; |
53921bfa | 273 | } |
a695feae | 274 | |
86590cd5 | 275 | /* if no seqnum is given, we move straight to exec queue */ |
2f6cbd19 | 276 | if (msg->seqnum == -1) { |
86590cd5 | 277 | list_add(&msg->list, &exec_list); |
2f6cbd19 | 278 | exec_queue_manager(); |
86590cd5 | 279 | } else { |
86590cd5 | 280 | msg_queue_insert(msg); |
86590cd5 | 281 | } |
7b1cbec9 KS |
282 | return; |
283 | ||
284 | skip: | |
285 | free(msg); | |
286 | return; | |
a695feae | 287 | } |
1c5c245e | 288 | |
53921bfa | 289 | static void sig_handler(int signum) |
7fafc032 | 290 | { |
53921bfa KS |
291 | switch (signum) { |
292 | case SIGINT: | |
293 | case SIGTERM: | |
53921bfa KS |
294 | exit(20 + signum); |
295 | break; | |
2f6cbd19 KS |
296 | case SIGALRM: |
297 | msg_q_timeout = 1; | |
298 | break; | |
299 | case SIGCHLD: | |
300 | children_waiting = 1; | |
301 | break; | |
53921bfa KS |
302 | default: |
303 | dbg("unhandled signal"); | |
7fafc032 | 304 | } |
33db4b8d | 305 | } |
7fafc032 | 306 | |
2f6cbd19 KS |
307 | static void udev_done(int pid) |
308 | { | |
309 | /* find msg associated with pid and delete it */ | |
310 | struct hotplug_msg *msg; | |
311 | ||
312 | list_for_each_entry(msg, &running_list, list) { | |
313 | if (msg->pid == pid) { | |
314 | dbg("<== exec seq %d came back", msg->seqnum); | |
315 | run_queue_delete(msg); | |
316 | return; | |
317 | } | |
318 | } | |
319 | } | |
320 | ||
33db4b8d KS |
321 | int main(int argc, char *argv[]) |
322 | { | |
53921bfa | 323 | int ssock; |
53921bfa | 324 | struct sockaddr_un saddr; |
1dadabd7 | 325 | socklen_t addrlen; |
53921bfa | 326 | int retval; |
0028653c | 327 | const int on = 1; |
f8911dbb | 328 | struct sigaction act; |
53921bfa | 329 | |
95a6f4c8 | 330 | init_logging("udevd"); |
896e5aa9 | 331 | dbg("version %s", UDEV_VERSION); |
95a6f4c8 | 332 | |
7b1cbec9 KS |
333 | if (getuid() != 0) { |
334 | dbg("need to be root, exit"); | |
335 | exit(1); | |
336 | } | |
337 | ||
f8911dbb KS |
338 | /* set signal handler */ |
339 | act.sa_handler = sig_handler; | |
340 | sigemptyset (&act.sa_mask); | |
341 | act.sa_flags = SA_RESTART; | |
342 | sigaction(SIGINT, &act, NULL); | |
343 | sigaction(SIGTERM, &act, NULL); | |
2f6cbd19 KS |
344 | |
345 | /* we want these two to interrupt system calls */ | |
f8911dbb KS |
346 | act.sa_flags = 0; |
347 | sigaction(SIGALRM, &act, NULL); | |
348 | sigaction(SIGCHLD, &act, NULL); | |
7fafc032 | 349 | |
53921bfa KS |
350 | memset(&saddr, 0x00, sizeof(saddr)); |
351 | saddr.sun_family = AF_LOCAL; | |
872344c4 KS |
352 | /* use abstract namespace for socket path */ |
353 | strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH); | |
1dadabd7 | 354 | addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1; |
53921bfa | 355 | |
2f6cbd19 | 356 | ssock = socket(AF_LOCAL, SOCK_DGRAM, 0); |
53921bfa | 357 | if (ssock == -1) { |
7b1cbec9 | 358 | dbg("error getting socket, exit"); |
53921bfa KS |
359 | exit(1); |
360 | } | |
361 | ||
2f6cbd19 | 362 | /* the bind takes care of ensuring only one copy running */ |
f8911dbb | 363 | retval = bind(ssock, (struct sockaddr *) &saddr, addrlen); |
53921bfa | 364 | if (retval < 0) { |
7b1cbec9 | 365 | dbg("bind failed, exit"); |
53921bfa KS |
366 | goto exit; |
367 | } | |
368 | ||
0028653c KS |
369 | /* enable receiving of the sender credentials */ |
370 | setsockopt(ssock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | |
371 | ||
2f6cbd19 KS |
372 | while (1) { |
373 | handle_msg(ssock); | |
53921bfa | 374 | |
2f6cbd19 KS |
375 | while(msg_q_timeout) { |
376 | msg_q_timeout = 0; | |
377 | msg_queue_manager(); | |
378 | } | |
53921bfa | 379 | |
2f6cbd19 KS |
380 | while(children_waiting) { |
381 | children_waiting = 0; | |
382 | /* reap all dead children */ | |
383 | while(1) { | |
384 | int pid = waitpid(-1, 0, WNOHANG); | |
385 | if ((pid == -1) || (pid == 0)) | |
386 | break; | |
387 | udev_done(pid); | |
388 | } | |
53921bfa | 389 | } |
53921bfa KS |
390 | } |
391 | exit: | |
392 | close(ssock); | |
53921bfa | 393 | exit(1); |
7fafc032 | 394 | } |