]> git.ipfire.org Git - thirdparty/systemd.git/blame - udevd.c
[PATCH] udevd - kill the lockfile
[thirdparty/systemd.git] / udevd.c
CommitLineData
7fafc032 1/*
53921bfa 2 * udevd.c - hotplug event serializer
7fafc032 3 *
7fafc032
KS
4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
8e2229c4 22#include <pthread.h>
a695feae 23#include <stddef.h>
90c210eb 24#include <sys/wait.h>
7fafc032
KS
25#include <signal.h>
26#include <unistd.h>
27#include <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
a695feae 31#include <time.h>
53921bfa
KS
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/un.h>
7fafc032 35
a695feae 36#include "list.h"
7fafc032 37#include "udev.h"
35b7d88c 38#include "udev_version.h"
7fafc032
KS
39#include "udevd.h"
40#include "logging.h"
41
35b7d88c 42
95a6f4c8 43unsigned char logname[42];
53921bfa
KS
44static pthread_mutex_t msg_lock;
45static pthread_mutex_t msg_active_lock;
46static pthread_cond_t msg_active;
47static pthread_mutex_t exec_lock;
48static pthread_mutex_t exec_active_lock;
49static pthread_cond_t exec_active;
50static pthread_mutex_t running_lock;
51static pthread_attr_t thr_attr;
52static int expected_seqnum = 0;
7fafc032 53
a695feae 54LIST_HEAD(msg_list);
53921bfa 55LIST_HEAD(exec_list);
35b7d88c 56LIST_HEAD(running_list);
7fafc032 57
7fafc032 58
53921bfa 59static void msg_dump_queue(void)
7fafc032 60{
53921bfa 61 struct hotplug_msg *msg;
7fafc032 62
53921bfa
KS
63 list_for_each_entry(msg, &msg_list, list)
64 dbg("sequence %d in queue", msg->seqnum);
35b7d88c
KS
65}
66
53921bfa 67static void msg_dump(struct hotplug_msg *msg)
35b7d88c 68{
53921bfa
KS
69 dbg("sequence %d, '%s', '%s', '%s'",
70 msg->seqnum, msg->action, msg->devpath, msg->subsystem);
35b7d88c
KS
71}
72
53921bfa 73static struct hotplug_msg *msg_create(void)
35b7d88c 74{
53921bfa 75 struct hotplug_msg *new_msg;
35b7d88c 76
53921bfa
KS
77 new_msg = malloc(sizeof(struct hotplug_msg));
78 if (new_msg == NULL) {
79 dbg("error malloc");
80 return NULL;
81 }
53921bfa 82 return new_msg;
35b7d88c
KS
83}
84
8e2229c4
KS
85static void msg_delete(struct hotplug_msg *msg)
86{
87 if (msg != NULL)
88 free(msg);
89}
90
53921bfa
KS
91/* orders the message in the queue by sequence number */
92static void msg_queue_insert(struct hotplug_msg *msg)
35b7d88c 93{
53921bfa 94 struct hotplug_msg *loop_msg;
35b7d88c 95
53921bfa
KS
96 /* sort message by sequence number into list*/
97 list_for_each_entry(loop_msg, &msg_list, list)
98 if (loop_msg->seqnum > msg->seqnum)
99 break;
100 list_add_tail(&msg->list, &loop_msg->list);
101 dbg("queued message seq %d", msg->seqnum);
35b7d88c 102
53921bfa
KS
103 /* store timestamp of queuing */
104 msg->queue_time = time(NULL);
35b7d88c 105
53921bfa
KS
106 /* signal queue activity to manager */
107 pthread_mutex_lock(&msg_active_lock);
108 pthread_cond_signal(&msg_active);
109 pthread_mutex_unlock(&msg_active_lock);
7fafc032 110
53921bfa 111 return ;
7fafc032
KS
112}
113
53921bfa
KS
114/* forks event and removes event from run queue when finished */
115static void *run_threads(void * parm)
7fafc032 116{
90c210eb 117 pid_t pid;
53921bfa 118 struct hotplug_msg *msg;
90c210eb 119
53921bfa 120 msg = parm;
a695feae
KS
121 setenv("ACTION", msg->action, 1);
122 setenv("DEVPATH", msg->devpath, 1);
90c210eb
KS
123
124 pid = fork();
125 switch (pid) {
126 case 0:
33db4b8d 127 /* child */
35b7d88c 128 execl(UDEV_BIN, "udev", msg->subsystem, NULL);
33db4b8d
KS
129 dbg("exec of child failed");
130 exit(1);
90c210eb
KS
131 break;
132 case -1:
33db4b8d 133 dbg("fork of child failed");
53921bfa 134 goto exit;
90c210eb 135 default:
53921bfa
KS
136 /* wait for exit of child */
137 dbg("==> exec seq %d [%d] working at '%s'",
138 msg->seqnum, pid, msg->devpath);
139 wait(NULL);
140 dbg("<== exec seq %d came back", msg->seqnum);
90c210eb 141 }
53921bfa
KS
142
143exit:
144 /* remove event from run list */
145 pthread_mutex_lock(&running_lock);
146 list_del_init(&msg->list);
147 pthread_mutex_unlock(&running_lock);
148
8e2229c4 149 msg_delete(msg);
53921bfa
KS
150
151 /* signal queue activity to exec manager */
152 pthread_mutex_lock(&exec_active_lock);
153 pthread_cond_signal(&exec_active);
154 pthread_mutex_unlock(&exec_active_lock);
155
156 pthread_exit(0);
7fafc032
KS
157}
158
53921bfa
KS
159/* returns already running task with devpath */
160static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
7fafc032 161{
53921bfa
KS
162 struct hotplug_msg *loop_msg;
163 struct hotplug_msg *tmp_msg;
35b7d88c 164
53921bfa
KS
165 list_for_each_entry_safe(loop_msg, tmp_msg, &running_list, list)
166 if (strncmp(loop_msg->devpath, msg->devpath, sizeof(loop_msg->devpath)) == 0)
167 return loop_msg;
168 return NULL;
7fafc032
KS
169}
170
53921bfa
KS
171/* queue management executes the events and delays events for the same devpath */
172static void *exec_queue_manager(void * parm)
7fafc032 173{
53921bfa
KS
174 struct hotplug_msg *loop_msg;
175 struct hotplug_msg *tmp_msg;
a695feae 176 struct hotplug_msg *msg;
53921bfa
KS
177 pthread_t run_tid;
178
179 while (1) {
180 pthread_mutex_lock(&exec_lock);
181 list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, list) {
182 msg = running_with_devpath(loop_msg);
183 if (msg == NULL) {
184 /* move event to run list */
185 pthread_mutex_lock(&running_lock);
186 list_move_tail(&loop_msg->list, &running_list);
187 pthread_mutex_unlock(&running_lock);
188
189 pthread_create(&run_tid, &thr_attr, run_threads, (void *) loop_msg);
190
191 dbg("moved seq %d to running list", loop_msg->seqnum);
192 } else {
193 dbg("delay seq %d, cause seq %d already working on '%s'",
194 loop_msg->seqnum, msg->seqnum, msg->devpath);
195 }
196 }
197 pthread_mutex_unlock(&exec_lock);
198
199 /* wait for activation, new events or childs coming back */
200 pthread_mutex_lock(&exec_active_lock);
201 pthread_cond_wait(&exec_active, &exec_active_lock);
202 pthread_mutex_unlock(&exec_active_lock);
203 }
204}
205
86590cd5 206static void exec_queue_activate(void)
53921bfa 207{
53921bfa
KS
208 pthread_mutex_lock(&exec_active_lock);
209 pthread_cond_signal(&exec_active);
210 pthread_mutex_unlock(&exec_active_lock);
211}
212
86590cd5
KS
213/* move message from incoming to exec queue */
214static void msg_move_exec(struct list_head *head)
215{
216 list_move_tail(head, &exec_list);
217 exec_queue_activate();
218}
219
53921bfa
KS
220/* queue management thread handles the timeouts and dispatches the events */
221static void *msg_queue_manager(void * parm)
222{
223 struct hotplug_msg *loop_msg;
a695feae 224 struct hotplug_msg *tmp_msg;
53921bfa
KS
225 time_t msg_age = 0;
226 struct timespec tv;
a695feae 227
53921bfa
KS
228 while (1) {
229 dbg("msg queue manager, next expected is %d", expected_seqnum);
230 pthread_mutex_lock(&msg_lock);
231 pthread_mutex_lock(&exec_lock);
a695feae 232recheck:
53921bfa
KS
233 list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, list) {
234 /* move event with expected sequence to the exec list */
235 if (loop_msg->seqnum == expected_seqnum) {
236 msg_move_exec(&loop_msg->list);
237 expected_seqnum++;
238 dbg("moved seq %d to exec, next expected is %d",
239 loop_msg->seqnum, expected_seqnum);
240 continue;
241 }
a695feae 242
53921bfa
KS
243 /* move event with expired timeout to the exec list */
244 msg_age = time(NULL) - loop_msg->queue_time;
245 if (msg_age > EVENT_TIMEOUT_SEC-1) {
246 msg_move_exec(&loop_msg->list);
247 expected_seqnum = loop_msg->seqnum+1;
248 dbg("moved seq %d to exec, reset next expected to %d",
249 loop_msg->seqnum, expected_seqnum);
250 goto recheck;
251 } else {
252 break;
253 }
a695feae 254 }
35b7d88c 255
53921bfa
KS
256 msg_dump_queue();
257 pthread_mutex_unlock(&exec_lock);
258 pthread_mutex_unlock(&msg_lock);
35b7d88c 259
53921bfa
KS
260 /* wait until queue gets active or next message timeout expires */
261 pthread_mutex_lock(&msg_active_lock);
262
263 if (list_empty(&msg_list) == 0) {
264 tv.tv_sec = time(NULL) + EVENT_TIMEOUT_SEC - msg_age;
265 tv.tv_nsec = 0;
266 dbg("next event expires in %li seconds",
267 EVENT_TIMEOUT_SEC - msg_age);
268 pthread_cond_timedwait(&msg_active, &msg_active_lock, &tv);
269 } else {
270 pthread_cond_wait(&msg_active, &msg_active_lock);
271 }
272 pthread_mutex_unlock(&msg_active_lock);
7fafc032 273 }
7fafc032
KS
274}
275
53921bfa
KS
276/* every connect creates a thread which gets the msg, queues it and exits */
277static void *client_threads(void * parm)
7fafc032 278{
53921bfa
KS
279 int sock;
280 struct hotplug_msg *msg;
281 int retval;
a695feae 282
53921bfa
KS
283 sock = (int) parm;
284
285 msg = msg_create();
286 if (msg == NULL) {
287 dbg("unable to store message");
288 goto exit;
7fafc032 289 }
7fafc032 290
53921bfa
KS
291 retval = recv(sock, msg, sizeof(struct hotplug_msg), 0);
292 if (retval < 0) {
293 dbg("unable to receive message");
294 goto exit;
295 }
a695feae 296
53921bfa
KS
297 if (strncmp(msg->magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
298 dbg("message magic '%s' doesn't match, ignore it", msg->magic);
8e2229c4 299 msg_delete(msg);
53921bfa
KS
300 goto exit;
301 }
a695feae 302
86590cd5
KS
303 /* if no seqnum is given, we move straight to exec queue */
304 if (msg->seqnum == 0) {
305 pthread_mutex_lock(&exec_lock);
306 list_add(&msg->list, &exec_list);
307 exec_queue_activate();
308 pthread_mutex_unlock(&exec_lock);
309 } else {
310 pthread_mutex_lock(&msg_lock);
311 msg_queue_insert(msg);
312 pthread_mutex_unlock(&msg_lock);
313 }
53921bfa
KS
314
315exit:
316 close(sock);
317 pthread_exit(0);
a695feae 318}
1c5c245e 319
53921bfa 320static void sig_handler(int signum)
7fafc032 321{
53921bfa
KS
322 switch (signum) {
323 case SIGINT:
324 case SIGTERM:
53921bfa
KS
325 exit(20 + signum);
326 break;
327 default:
328 dbg("unhandled signal");
7fafc032 329 }
33db4b8d 330}
7fafc032 331
33db4b8d
KS
332int main(int argc, char *argv[])
333{
53921bfa
KS
334 int ssock;
335 int csock;
336 struct sockaddr_un saddr;
337 struct sockaddr_un caddr;
1dadabd7 338 socklen_t addrlen;
53921bfa
KS
339 socklen_t clen;
340 pthread_t cli_tid;
341 pthread_t mgr_msg_tid;
342 pthread_t mgr_exec_tid;
343 int retval;
344
95a6f4c8
GKH
345 init_logging("udevd");
346
7fafc032
KS
347 signal(SIGINT, sig_handler);
348 signal(SIGTERM, sig_handler);
7fafc032 349
53921bfa
KS
350 memset(&saddr, 0x00, sizeof(saddr));
351 saddr.sun_family = AF_LOCAL;
872344c4
KS
352 /* use abstract namespace for socket path */
353 strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
1dadabd7 354 addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
53921bfa 355
53921bfa
KS
356 ssock = socket(AF_LOCAL, SOCK_STREAM, 0);
357 if (ssock == -1) {
358 dbg("error getting socket");
359 exit(1);
360 }
361
1dadabd7 362 retval = bind(ssock, &saddr, addrlen);
53921bfa
KS
363 if (retval < 0) {
364 dbg("bind failed\n");
365 goto exit;
366 }
367
368 retval = listen(ssock, SOMAXCONN);
369 if (retval < 0) {
370 dbg("listen failed\n");
371 goto exit;
372 }
373
374 pthread_mutex_init(&msg_lock, NULL);
375 pthread_mutex_init(&msg_active_lock, NULL);
376 pthread_mutex_init(&exec_lock, NULL);
377 pthread_mutex_init(&exec_active_lock, NULL);
378 pthread_mutex_init(&running_lock, NULL);
379
380 /* set default attributes for created threads */
381 pthread_attr_init(&thr_attr);
382 pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED);
8e2229c4 383 pthread_attr_setstacksize(&thr_attr, 16 * 1024);
53921bfa
KS
384
385 /* init queue management */
386 pthread_create(&mgr_msg_tid, &thr_attr, msg_queue_manager, NULL);
387 pthread_create(&mgr_exec_tid, &thr_attr, exec_queue_manager, NULL);
388
389 clen = sizeof(caddr);
390 /* main loop */
391 while (1) {
392 csock = accept(ssock, &caddr, &clen);
393 if (csock < 0) {
53921bfa 394 dbg("client accept failed\n");
8e2229c4 395 continue;
53921bfa
KS
396 }
397 pthread_create(&cli_tid, &thr_attr, client_threads, (void *) csock);
398 }
399exit:
400 close(ssock);
53921bfa 401 exit(1);
7fafc032 402}