chiark / gitweb /
22c38653e7712b68c30822952ccc857d38bb5164
[elogind.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                 return -1;
322         }
323
324         snprintf(string, sizeof(string), "%d\n", getpid());
325         write(lock_file, string, strlen(string));
326
327         return 0;
328 }
329
330 int main(int argc, char *argv[])
331 {
332         /* only let one version of the daemon run at any one time */
333         if (one_and_only() != 0)
334                 exit(0);
335
336         /* set up signal handler */
337         signal(SIGINT, sig_handler);
338         signal(SIGTERM, sig_handler);
339         signal(SIGALRM, sig_handler);
340         signal(SIGCHLD, sig_handler);
341
342         work();
343         exit(0);
344 }