]> git.ipfire.org Git - thirdparty/systemd.git/blob - udevd.c
[PATCH] udevd - cleanup and better timeout handling
[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 "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 }