2 * udevd.c - hotplug event serializer
4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
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.
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.
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.
32 #include <sys/types.h>
33 #include <sys/socket.h>
38 #include "udev_version.h"
43 unsigned char logname[42];
44 static pthread_mutex_t msg_lock;
45 static pthread_mutex_t msg_active_lock;
46 static pthread_cond_t msg_active;
47 static pthread_mutex_t exec_lock;
48 static pthread_mutex_t exec_active_lock;
49 static pthread_cond_t exec_active;
50 static pthread_mutex_t running_lock;
51 static pthread_attr_t thr_attr;
52 static int expected_seqnum = 0;
56 LIST_HEAD(running_list);
59 static void msg_dump_queue(void)
61 struct hotplug_msg *msg;
63 list_for_each_entry(msg, &msg_list, list)
64 dbg("sequence %d in queue", msg->seqnum);
67 static void msg_dump(struct hotplug_msg *msg)
69 dbg("sequence %d, '%s', '%s', '%s'",
70 msg->seqnum, msg->action, msg->devpath, msg->subsystem);
73 static struct hotplug_msg *msg_create(void)
75 struct hotplug_msg *new_msg;
77 new_msg = malloc(sizeof(struct hotplug_msg));
78 if (new_msg == NULL) {
85 static void msg_delete(struct hotplug_msg *msg)
91 /* orders the message in the queue by sequence number */
92 static void msg_queue_insert(struct hotplug_msg *msg)
94 struct hotplug_msg *loop_msg;
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)
100 list_add_tail(&msg->list, &loop_msg->list);
101 dbg("queued message seq %d", msg->seqnum);
103 /* store timestamp of queuing */
104 msg->queue_time = time(NULL);
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);
114 /* forks event and removes event from run queue when finished */
115 static void *run_threads(void * parm)
118 struct hotplug_msg *msg;
121 setenv("ACTION", msg->action, 1);
122 setenv("DEVPATH", msg->devpath, 1);
128 execl(UDEV_BIN, "udev", msg->subsystem, NULL);
129 dbg("exec of child failed");
133 dbg("fork of child failed");
136 /* wait for exit of child */
137 dbg("==> exec seq %d [%d] working at '%s'",
138 msg->seqnum, pid, msg->devpath);
140 dbg("<== exec seq %d came back", msg->seqnum);
144 /* remove event from run list */
145 pthread_mutex_lock(&running_lock);
146 list_del_init(&msg->list);
147 pthread_mutex_unlock(&running_lock);
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);
159 /* returns already running task with devpath */
160 static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
162 struct hotplug_msg *loop_msg;
163 struct hotplug_msg *tmp_msg;
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)
171 /* queue management executes the events and delays events for the same devpath */
172 static void *exec_queue_manager(void * parm)
174 struct hotplug_msg *loop_msg;
175 struct hotplug_msg *tmp_msg;
176 struct hotplug_msg *msg;
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);
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);
189 pthread_create(&run_tid, &thr_attr, run_threads, (void *) loop_msg);
191 dbg("moved seq %d to running list", loop_msg->seqnum);
193 dbg("delay seq %d, cause seq %d already working on '%s'",
194 loop_msg->seqnum, msg->seqnum, msg->devpath);
197 pthread_mutex_unlock(&exec_lock);
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);
206 static void exec_queue_activate(void)
208 pthread_mutex_lock(&exec_active_lock);
209 pthread_cond_signal(&exec_active);
210 pthread_mutex_unlock(&exec_active_lock);
213 /* move message from incoming to exec queue */
214 static void msg_move_exec(struct list_head *head)
216 list_move_tail(head, &exec_list);
217 exec_queue_activate();
220 /* queue management thread handles the timeouts and dispatches the events */
221 static void *msg_queue_manager(void * parm)
223 struct hotplug_msg *loop_msg;
224 struct hotplug_msg *tmp_msg;
229 dbg("msg queue manager, next expected is %d", expected_seqnum);
230 pthread_mutex_lock(&msg_lock);
231 pthread_mutex_lock(&exec_lock);
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);
238 dbg("moved seq %d to exec, next expected is %d",
239 loop_msg->seqnum, expected_seqnum);
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);
257 pthread_mutex_unlock(&exec_lock);
258 pthread_mutex_unlock(&msg_lock);
260 /* wait until queue gets active or next message timeout expires */
261 pthread_mutex_lock(&msg_active_lock);
263 if (list_empty(&msg_list) == 0) {
264 tv.tv_sec = time(NULL) + EVENT_TIMEOUT_SEC - msg_age;
266 dbg("next event expires in %li seconds",
267 EVENT_TIMEOUT_SEC - msg_age);
268 pthread_cond_timedwait(&msg_active, &msg_active_lock, &tv);
270 pthread_cond_wait(&msg_active, &msg_active_lock);
272 pthread_mutex_unlock(&msg_active_lock);
276 /* every connect creates a thread which gets the msg, queues it and exits */
277 static void *client_threads(void * parm)
280 struct hotplug_msg *msg;
287 dbg("unable to store message");
291 retval = recv(sock, msg, sizeof(struct hotplug_msg), 0);
293 dbg("unable to receive message");
297 if (strncmp(msg->magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
298 dbg("message magic '%s' doesn't match, ignore it", msg->magic);
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);
310 pthread_mutex_lock(&msg_lock);
311 msg_queue_insert(msg);
312 pthread_mutex_unlock(&msg_lock);
320 static void sig_handler(int signum)
328 dbg("unhandled signal");
332 int main(int argc, char *argv[])
336 struct sockaddr_un saddr;
337 struct sockaddr_un caddr;
341 pthread_t mgr_msg_tid;
342 pthread_t mgr_exec_tid;
345 init_logging("udevd");
347 signal(SIGINT, sig_handler);
348 signal(SIGTERM, sig_handler);
350 memset(&saddr, 0x00, sizeof(saddr));
351 saddr.sun_family = AF_LOCAL;
352 /* use abstract namespace for socket path */
353 strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
354 addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
356 ssock = socket(AF_LOCAL, SOCK_STREAM, 0);
358 dbg("error getting socket");
362 retval = bind(ssock, &saddr, addrlen);
364 dbg("bind failed\n");
368 retval = listen(ssock, SOMAXCONN);
370 dbg("listen failed\n");
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);
380 /* set default attributes for created threads */
381 pthread_attr_init(&thr_attr);
382 pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED);
383 pthread_attr_setstacksize(&thr_attr, 16 * 1024);
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);
389 clen = sizeof(caddr);
392 csock = accept(ssock, &caddr, &clen);
394 dbg("client accept failed\n");
397 pthread_create(&cli_tid, &thr_attr, client_threads, (void *) csock);