]> git.ipfire.org Git - thirdparty/systemd.git/blob - udevd.c
[PATCH] udevd - next round of fixes
[thirdparty/systemd.git] / udevd.c
1 /*
2 * udevd.c
3 *
4 * Userspace devfs
5 *
6 * Copyright (C) 2004 Ling, Xiaofeng <xiaofeng.ling@intel.com>
7 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25 #include <stddef.h>
26 #include <sys/types.h>
27 #include <sys/ipc.h>
28 #include <sys/wait.h>
29 #include <sys/msg.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <fcntl.h>
38
39 #include "list.h"
40 #include "udev.h"
41 #include "udev_version.h"
42 #include "udevd.h"
43 #include "logging.h"
44
45
46 #define BUFFER_SIZE 1024
47
48 static int running_remove_queue(pid_t pid);
49 static int msg_exec(struct hotplug_msg *msg);
50
51 static int expect_seqnum = 0;
52 static int lock_file = -1;
53 static char *lock_filename = ".udevd_lock";
54
55 LIST_HEAD(msg_list);
56 LIST_HEAD(running_list);
57 LIST_HEAD(delayed_list);
58
59 static void sig_handler(int signum)
60 {
61 pid_t pid;
62
63 dbg("caught signal %d", signum);
64 switch (signum) {
65 case SIGALRM:
66 dbg("event timeout reached");
67 break;
68 case SIGCHLD:
69 /* catch signals from exiting childs */
70 while ( (pid = waitpid(-1, NULL, WNOHANG)) > 0) {
71 dbg("exec finished, pid %d", pid);
72 running_remove_queue(pid);
73 }
74 break;
75 case SIGINT:
76 case SIGTERM:
77 if (lock_file >= 0) {
78 close(lock_file);
79 unlink(lock_filename);
80 }
81 exit(20 + signum);
82 break;
83 default:
84 dbg("unhandled signal");
85 }
86 }
87
88 static void set_timeout(int seconds)
89 {
90 alarm(seconds);
91 dbg("set timeout in %d seconds", seconds);
92 }
93
94 static int running_moveto_queue(struct hotplug_msg *msg)
95 {
96 dbg("move sequence %d [%d] to running queue '%s'",
97 msg->seqnum, msg->pid, msg->devpath);
98 list_move_tail(&msg->list, &running_list);
99 return 0;
100 }
101
102 static int running_remove_queue(pid_t pid)
103 {
104 struct hotplug_msg *child;
105 struct hotplug_msg *tmp_child;
106
107 list_for_each_entry_safe(child, tmp_child, &running_list, list)
108 if (child->pid == pid) {
109 list_del_init(&child->list);
110 free(child);
111 return 0;
112 }
113 return -EINVAL;
114 }
115
116 static pid_t running_getpid_by_devpath(struct hotplug_msg *msg)
117 {
118 struct hotplug_msg *child;
119 struct hotplug_msg *tmp_child;
120
121 list_for_each_entry_safe(child, tmp_child, &running_list, list)
122 if (strncmp(child->devpath, msg->devpath, sizeof(child->devpath)) == 0)
123 return child->pid;
124 return 0;
125 }
126
127 static void delayed_dump_queue(void)
128 {
129 struct hotplug_msg *child;
130
131 list_for_each_entry(child, &delayed_list, list)
132 dbg("event for '%s' in queue", child->devpath);
133 }
134
135 static int delayed_moveto_queue(struct hotplug_msg *msg)
136 {
137 dbg("move event to delayed queue '%s'", msg->devpath);
138 list_move_tail(&msg->list, &delayed_list);
139 return 0;
140 }
141
142 static void delayed_check_queue(void)
143 {
144 struct hotplug_msg *delayed_child;
145 struct hotplug_msg *running_child;
146 struct hotplug_msg *tmp_child;
147
148 /* see if we have delayed exec's that can run now */
149 list_for_each_entry_safe(delayed_child, tmp_child, &delayed_list, list)
150 list_for_each_entry_safe(running_child, tmp_child, &running_list, list)
151 if (strncmp(delayed_child->devpath, running_child->devpath,
152 sizeof(running_child->devpath)) == 0) {
153 dbg("delayed exec for '%s' can run now", delayed_child->devpath);
154 msg_exec(delayed_child);
155 }
156 }
157
158 static void msg_dump(struct hotplug_msg *msg)
159 {
160 dbg("sequence %d, '%s', '%s', '%s'",
161 msg->seqnum, msg->action, msg->devpath, msg->subsystem);
162 }
163
164 static int msg_exec(struct hotplug_msg *msg)
165 {
166 pid_t pid;
167
168 msg_dump(msg);
169
170 setenv("ACTION", msg->action, 1);
171 setenv("DEVPATH", msg->devpath, 1);
172
173 /* delay exec, if we already have a udev working on the same devpath */
174 pid = running_getpid_by_devpath(msg);
175 if (pid != 0) {
176 dbg("delay exec of sequence %d, [%d] already working on '%s'",
177 msg->seqnum, pid, msg->devpath);
178 delayed_moveto_queue(msg);
179 }
180
181 pid = fork();
182 switch (pid) {
183 case 0:
184 /* child */
185 execl(UDEV_BIN, "udev", msg->subsystem, NULL);
186 dbg("exec of child failed");
187 exit(1);
188 break;
189 case -1:
190 dbg("fork of child failed");
191 return -1;
192 default:
193 /* exec in background, get the SIGCHLD with the sig handler */
194 msg->pid = pid;
195 running_moveto_queue(msg);
196 break;
197 }
198 return 0;
199 }
200
201 static void msg_dump_queue(void)
202 {
203 struct hotplug_msg *msg;
204
205 list_for_each_entry(msg, &msg_list, list)
206 dbg("sequence %d in queue", msg->seqnum);
207 }
208
209 static void msg_check_queue(void)
210 {
211 struct hotplug_msg *msg;
212 struct hotplug_msg *tmp_msg;
213 time_t msg_age;
214
215 recheck:
216 /* dispatch events until one is missing */
217 list_for_each_entry_safe(msg, tmp_msg, &msg_list, list) {
218 if (msg->seqnum != expect_seqnum)
219 break;
220 msg_exec(msg);
221 expect_seqnum++;
222 }
223
224 /* recalculate next timeout */
225 if (list_empty(&msg_list) == 0) {
226 msg_age = time(NULL) - msg->queue_time;
227 if (msg_age > EVENT_TIMEOUT_SEC-1) {
228 info("event %d, age %li seconds, skip event %d-%d",
229 msg->seqnum, msg_age, expect_seqnum, msg->seqnum-1);
230 expect_seqnum = msg->seqnum;
231 goto recheck;
232 }
233
234 /* the first sequence gets its own timeout */
235 if (expect_seqnum == 0) {
236 msg_age = EVENT_TIMEOUT_SEC - FIRST_EVENT_TIMEOUT_SEC;
237 expect_seqnum = 1;
238 }
239
240 set_timeout(EVENT_TIMEOUT_SEC - msg_age);
241 return;
242 }
243 }
244
245 static int msg_add_queue(struct hotplug_msg *msg)
246 {
247 struct hotplug_msg *new_msg;
248 struct hotplug_msg *tmp_msg;
249
250 new_msg = malloc(sizeof(*new_msg));
251 if (new_msg == NULL) {
252 dbg("error malloc");
253 return -ENOMEM;
254 }
255 memcpy(new_msg, msg, sizeof(*new_msg));
256
257 /* store timestamp of queuing */
258 new_msg->queue_time = time(NULL);
259
260 /* sort message by sequence number into list*/
261 list_for_each_entry(tmp_msg, &msg_list, list)
262 if (tmp_msg->seqnum > new_msg->seqnum)
263 break;
264 list_add_tail(&new_msg->list, &tmp_msg->list);
265
266 return 0;
267 }
268
269 static void work(void)
270 {
271 struct hotplug_msg *msg;
272 int msgid;
273 key_t key;
274 char buf[BUFFER_SIZE];
275 int ret;
276
277 key = ftok(UDEVD_BIN, IPC_KEY_ID);
278 msg = (struct hotplug_msg *) buf;
279 msgid = msgget(key, IPC_CREAT);
280 if (msgid == -1) {
281 dbg("open message queue error");
282 exit(1);
283 }
284 while (1) {
285 ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
286 if (ret != -1) {
287 dbg("received sequence %d, expected sequence %d", msg->seqnum, expect_seqnum);
288 if (msg->seqnum >= expect_seqnum) {
289 msg_add_queue(msg);
290 msg_dump_queue();
291 msg_check_queue();
292 continue;
293 }
294 dbg("too late for event with sequence %d, event skipped ", msg->seqnum);
295 } else {
296 if (errno == EINTR) {
297 msg_check_queue();
298 msg_dump_queue();
299 delayed_check_queue();
300 delayed_dump_queue();
301 continue;
302 }
303 dbg("ipc message receive error '%s'", strerror(errno));
304 }
305 }
306 }
307
308 static int one_and_only(void)
309 {
310 char string[100];
311
312 lock_file = open(lock_filename, O_RDWR | O_CREAT, 0x640);
313
314 /* see if we can open */
315 if (lock_file < 0)
316 return -1;
317
318 /* see if we can lock */
319 if (lockf(lock_file, F_TLOCK, 0) < 0) {
320 close(lock_file);
321 unlink(lock_filename);
322 return -1;
323 }
324
325 snprintf(string, sizeof(string), "%d\n", getpid());
326 write(lock_file, string, strlen(string));
327
328 return 0;
329 }
330
331 int main(int argc, char *argv[])
332 {
333 /* only let one version of the daemon run at any one time */
334 if (one_and_only() != 0)
335 exit(0);
336
337 /* set up signal handler */
338 signal(SIGINT, sig_handler);
339 signal(SIGTERM, sig_handler);
340 signal(SIGALRM, sig_handler);
341 signal(SIGCHLD, sig_handler);
342
343 work();
344 exit(0);
345 }