chiark / gitweb /
26e6b23486b23963fa34cfb09ed102520deded59
[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 "udevd.h"
42 #include "logging.h"
43
44 #define BUFFER_SIZE                     1024
45
46 static int expect_seqnum = 0;
47 static int lock_file = -1;
48 static char *lock_filename = ".udevd_lock";
49
50 LIST_HEAD(msg_list);
51
52 static void sig_handler(int signum)
53 {
54         dbg("caught signal %d", signum);
55         switch (signum) {
56         case SIGALRM:
57                 dbg("event timeout reached");
58                 break;
59         case SIGINT:
60         case SIGTERM:
61         case SIGKILL:
62                 if (lock_file >= 0) {
63                         close(lock_file);
64                         unlink(lock_filename);
65                 }
66                 exit(20 + signum);
67                 break;
68         default:
69                 dbg("unhandled signal");
70         }
71 }
72
73 static void dump_queue(void)
74 {
75         struct hotplug_msg *msg;
76
77         list_for_each_entry(msg, &msg_list, list)
78                 dbg("sequence %d in queue", msg->seqnum);
79 }
80
81 static void dump_msg(struct hotplug_msg *msg)
82 {
83         dbg("sequence %d, '%s', '%s', '%s'",
84             msg->seqnum, msg->action, msg->devpath, msg->subsystem);
85 }
86
87 static int dispatch_msg(struct hotplug_msg *msg)
88 {
89         pid_t pid;
90
91         dump_msg(msg);
92
93         setenv("ACTION", msg->action, 1);
94         setenv("DEVPATH", msg->devpath, 1);
95
96         pid = fork();
97         switch (pid) {
98         case 0:
99                 /* child */
100                 execl(UDEV_EXEC, "udev", msg->subsystem, NULL);
101                 dbg("exec of child failed");
102                 exit(1);
103                 break;
104         case -1:
105                 dbg("fork of child failed");
106                 return -1;
107         default:
108                 wait(NULL);
109         }
110         return 0;
111 }
112
113 static void set_timeout(int seconds)
114 {
115         alarm(seconds);
116         dbg("set timeout in %d seconds", seconds);
117 }
118
119 static void check_queue(void)
120 {
121         struct hotplug_msg *msg;
122         struct hotplug_msg *tmp_msg;
123         time_t msg_age;
124
125 recheck:
126         /* dispatch events until one is missing */
127         list_for_each_entry_safe(msg, tmp_msg, &msg_list, list) {
128                 if (msg->seqnum != expect_seqnum)
129                         break;
130                 dispatch_msg(msg);
131                 expect_seqnum++;
132                 list_del_init(&msg->list);
133                 free(msg);
134         }
135
136         /* recalculate timeout */
137         if (list_empty(&msg_list) == 0) {
138                 msg_age = time(NULL) - msg->queue_time;
139                 if (msg_age > EVENT_TIMEOUT_SECONDS-1) {
140                         info("event %d, age %li seconds, skip event %d-%d",
141                              msg->seqnum, msg_age, expect_seqnum, msg->seqnum-1);
142                         expect_seqnum = msg->seqnum;
143                         goto recheck;
144                 }
145                 set_timeout(EVENT_TIMEOUT_SECONDS - msg_age);
146                 return;
147         }
148
149         /* queue is empty */
150         set_timeout(UDEVD_TIMEOUT_SECONDS);
151 }
152
153 static int queue_msg(struct hotplug_msg *msg)
154 {
155         struct hotplug_msg *new_msg;
156         struct hotplug_msg *tmp_msg;
157
158         new_msg = malloc(sizeof(*new_msg));
159         if (new_msg == NULL) {
160                 dbg("error malloc");
161                 return -ENOMEM;
162         }
163         memcpy(new_msg, msg, sizeof(*new_msg));
164
165         /* store timestamp of queuing */
166         new_msg->queue_time = time(NULL);
167
168         /* sort message by sequence number into list*/
169         list_for_each_entry(tmp_msg, &msg_list, list)
170                 if (tmp_msg->seqnum > new_msg->seqnum)
171                         break;
172         list_add_tail(&new_msg->list, &tmp_msg->list);
173
174         return 0;
175 }
176
177 static void work(void)
178 {
179         struct hotplug_msg *msg;
180         int msgid;
181         key_t key;
182         char buf[BUFFER_SIZE];
183         int ret;
184
185         key = ftok(UDEVD_EXEC, IPC_KEY_ID);
186         msg = (struct hotplug_msg *) buf;
187         msgid = msgget(key, IPC_CREAT);
188         if (msgid == -1) {
189                 dbg("open message queue error");
190                 exit(1);
191         }
192         while (1) {
193                 ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
194                 if (ret != -1) {
195                         /* init the expected sequence with value from first call */
196                         if (expect_seqnum == 0) {
197                                 expect_seqnum = msg->seqnum;
198                                 dbg("init next expected sequence number to %d", expect_seqnum);
199                         }
200                         dbg("current sequence %d, expected sequence %d", msg->seqnum, expect_seqnum);
201                         if (msg->seqnum == expect_seqnum) {
202                                 /* execute expected event */
203                                 dispatch_msg(msg);
204                                 expect_seqnum++;
205                                 check_queue();
206                                 dump_queue();
207                                 continue;
208                         }
209                         if (msg->seqnum > expect_seqnum) {
210                                 /* something missing, queue event*/
211                                 queue_msg(msg);
212                                 check_queue();
213                                 dump_queue();
214                                 continue;
215                         }
216                         dbg("too late for event with sequence %d, even skipped ", msg->seqnum);
217                 } else {
218                         if (errno == EINTR) {
219                                 /* timeout */
220                                 if (list_empty(&msg_list)) {
221                                         info("we have nothing to do, so daemon exits...");
222                                         if (lock_file >= 0) {
223                                                 close(lock_file);
224                                                 unlink(lock_filename);
225                                         }
226                                         exit(0);
227                                 }
228                                 check_queue();
229                                 dump_queue();
230                                 continue;
231                         }
232                         dbg("ipc message receive error '%s'", strerror(errno));
233                 }
234         }
235 }
236
237 static int one_and_only(void)
238 {
239         char string[100];
240
241         lock_file = open(lock_filename, O_RDWR | O_CREAT, 0x640);
242
243         /* see if we can open */
244         if (lock_file < 0)
245                 return -1;
246         
247         /* see if we can lock */
248         if (lockf(lock_file, F_TLOCK, 0) < 0) {
249                 close(lock_file);
250                 unlink(lock_filename);
251                 return -1;
252         }
253
254         snprintf(string, sizeof(string), "%d\n", getpid());
255         write(lock_file, string, strlen(string));
256
257         return 0;
258 }
259
260 int main(int argc, char *argv[])
261 {
262         /* only let one version of the daemon run at any one time */
263         if (one_and_only() != 0)
264                 exit(0);
265
266         /* set up signal handler */
267         signal(SIGINT, sig_handler);
268         signal(SIGTERM, sig_handler);
269         signal(SIGKILL, sig_handler);
270         signal(SIGALRM, sig_handler);
271
272         /* we exit if we have nothing to do, next event will start us again */
273         set_timeout(UDEVD_TIMEOUT_SECONDS);
274
275         work();
276         exit(0);
277 }