]>
Commit | Line | Data |
---|---|---|
7fafc032 KS |
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 | ||
a695feae | 25 | #include <stddef.h> |
7fafc032 KS |
26 | #include <sys/types.h> |
27 | #include <sys/ipc.h> | |
90c210eb | 28 | #include <sys/wait.h> |
7fafc032 KS |
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> | |
a695feae | 36 | #include <time.h> |
1c5c245e | 37 | #include <fcntl.h> |
7fafc032 | 38 | |
a695feae | 39 | #include "list.h" |
7fafc032 KS |
40 | #include "udev.h" |
41 | #include "udevd.h" | |
42 | #include "logging.h" | |
43 | ||
33db4b8d | 44 | #define BUFFER_SIZE 1024 |
7fafc032 KS |
45 | |
46 | static int expect_seqnum = 0; | |
a695feae KS |
47 | static int lock_file = -1; |
48 | static char *lock_filename = ".udevd_lock"; | |
7fafc032 | 49 | |
a695feae | 50 | LIST_HEAD(msg_list); |
7fafc032 | 51 | |
a695feae | 52 | static void sig_handler(int signum) |
7fafc032 KS |
53 | { |
54 | dbg("caught signal %d", signum); | |
55 | switch (signum) { | |
56 | case SIGALRM: | |
7fafc032 KS |
57 | dbg("event timeout reached"); |
58 | break; | |
a695feae KS |
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; | |
7fafc032 KS |
68 | default: |
69 | dbg("unhandled signal"); | |
70 | } | |
71 | } | |
72 | ||
73 | static void dump_queue(void) | |
74 | { | |
a695feae | 75 | struct hotplug_msg *msg; |
7fafc032 | 76 | |
a695feae KS |
77 | list_for_each_entry(msg, &msg_list, list) |
78 | dbg("sequence %d in queue", msg->seqnum); | |
7fafc032 KS |
79 | } |
80 | ||
a695feae | 81 | static void dump_msg(struct hotplug_msg *msg) |
7fafc032 KS |
82 | { |
83 | dbg("sequence %d, '%s', '%s', '%s'", | |
a695feae | 84 | msg->seqnum, msg->action, msg->devpath, msg->subsystem); |
7fafc032 KS |
85 | } |
86 | ||
a695feae | 87 | static int dispatch_msg(struct hotplug_msg *msg) |
7fafc032 | 88 | { |
90c210eb | 89 | pid_t pid; |
90c210eb | 90 | |
a695feae | 91 | dump_msg(msg); |
90c210eb | 92 | |
a695feae KS |
93 | setenv("ACTION", msg->action, 1); |
94 | setenv("DEVPATH", msg->devpath, 1); | |
90c210eb KS |
95 | |
96 | pid = fork(); | |
97 | switch (pid) { | |
98 | case 0: | |
33db4b8d | 99 | /* child */ |
a695feae | 100 | execl(UDEV_EXEC, "udev", msg->subsystem, NULL); |
33db4b8d KS |
101 | dbg("exec of child failed"); |
102 | exit(1); | |
90c210eb KS |
103 | break; |
104 | case -1: | |
33db4b8d | 105 | dbg("fork of child failed"); |
90c210eb KS |
106 | return -1; |
107 | default: | |
a695feae | 108 | wait(NULL); |
90c210eb KS |
109 | } |
110 | return 0; | |
7fafc032 KS |
111 | } |
112 | ||
a695feae | 113 | static void set_timeout(int seconds) |
7fafc032 | 114 | { |
33db4b8d | 115 | alarm(seconds); |
a695feae | 116 | dbg("set timeout in %d seconds", seconds); |
7fafc032 KS |
117 | } |
118 | ||
119 | static void check_queue(void) | |
120 | { | |
a695feae KS |
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); | |
7fafc032 | 131 | expect_seqnum++; |
a695feae KS |
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; | |
7fafc032 | 147 | } |
a695feae KS |
148 | |
149 | /* queue is empty */ | |
150 | set_timeout(UDEVD_TIMEOUT_SECONDS); | |
7fafc032 KS |
151 | } |
152 | ||
a695feae | 153 | static int queue_msg(struct hotplug_msg *msg) |
7fafc032 | 154 | { |
a695feae KS |
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; | |
7fafc032 | 162 | } |
a695feae | 163 | memcpy(new_msg, msg, sizeof(*new_msg)); |
7fafc032 | 164 | |
a695feae KS |
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 | } | |
1c5c245e | 176 | |
a695feae | 177 | static void work(void) |
7fafc032 | 178 | { |
a695feae | 179 | struct hotplug_msg *msg; |
7fafc032 KS |
180 | int msgid; |
181 | key_t key; | |
7fafc032 KS |
182 | char buf[BUFFER_SIZE]; |
183 | int ret; | |
184 | ||
a695feae KS |
185 | key = ftok(UDEVD_EXEC, IPC_KEY_ID); |
186 | msg = (struct hotplug_msg *) buf; | |
7fafc032 KS |
187 | msgid = msgget(key, IPC_CREAT); |
188 | if (msgid == -1) { | |
189 | dbg("open message queue error"); | |
a695feae | 190 | exit(1); |
7fafc032 KS |
191 | } |
192 | while (1) { | |
193 | ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0); | |
194 | if (ret != -1) { | |
a695feae | 195 | /* init the expected sequence with value from first call */ |
7fafc032 | 196 | if (expect_seqnum == 0) { |
a695feae | 197 | expect_seqnum = msg->seqnum; |
7fafc032 KS |
198 | dbg("init next expected sequence number to %d", expect_seqnum); |
199 | } | |
a695feae KS |
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; | |
7fafc032 | 208 | } |
a695feae KS |
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); | |
33db4b8d | 217 | } else { |
7fafc032 | 218 | if (errno == EINTR) { |
a695feae KS |
219 | /* timeout */ |
220 | if (list_empty(&msg_list)) { | |
33db4b8d | 221 | info("we have nothing to do, so daemon exits..."); |
1c5c245e GKH |
222 | if (lock_file >= 0) { |
223 | close(lock_file); | |
224 | unlink(lock_filename); | |
225 | } | |
33db4b8d | 226 | exit(0); |
7fafc032 KS |
227 | } |
228 | check_queue(); | |
a695feae KS |
229 | dump_queue(); |
230 | continue; | |
7fafc032 | 231 | } |
a695feae | 232 | dbg("ipc message receive error '%s'", strerror(errno)); |
33db4b8d | 233 | } |
7fafc032 | 234 | } |
33db4b8d | 235 | } |
7fafc032 | 236 | |
1c5c245e GKH |
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) | |
a695feae | 245 | return -1; |
1c5c245e GKH |
246 | |
247 | /* see if we can lock */ | |
248 | if (lockf(lock_file, F_TLOCK, 0) < 0) { | |
249 | close(lock_file); | |
250 | unlink(lock_filename); | |
a695feae | 251 | return -1; |
1c5c245e GKH |
252 | } |
253 | ||
254 | snprintf(string, sizeof(string), "%d\n", getpid()); | |
255 | write(lock_file, string, strlen(string)); | |
a695feae | 256 | |
1c5c245e GKH |
257 | return 0; |
258 | } | |
259 | ||
33db4b8d KS |
260 | int main(int argc, char *argv[]) |
261 | { | |
1c5c245e GKH |
262 | /* only let one version of the daemon run at any one time */ |
263 | if (one_and_only() != 0) | |
264 | exit(0); | |
265 | ||
7fafc032 KS |
266 | /* set up signal handler */ |
267 | signal(SIGINT, sig_handler); | |
268 | signal(SIGTERM, sig_handler); | |
269 | signal(SIGKILL, sig_handler); | |
a695feae | 270 | signal(SIGALRM, sig_handler); |
33db4b8d KS |
271 | |
272 | /* we exit if we have nothing to do, next event will start us again */ | |
a695feae | 273 | set_timeout(UDEVD_TIMEOUT_SECONDS); |
7fafc032 | 274 | |
a695feae KS |
275 | work(); |
276 | exit(0); | |
7fafc032 | 277 | } |