chiark / gitweb /
da887ec29b4c4b2a49f1fe440f58645e07e4f722
[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 <sys/types.h>
26 #include <sys/ipc.h>
27 #include <sys/wait.h>
28 #include <sys/msg.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "udev.h"
37 #include "udevd.h"
38 #include "logging.h"
39
40 #define BUFFER_SIZE                     1024
41 #define EVENT_TIMEOUT_SECONDS           10
42 #define DAEMON_TIMEOUT_SECONDS          30
43
44
45 static int expect_seqnum = 0;
46 static struct hotplug_msg *head = NULL;
47
48
49 static void sig_alarmhandler(int signum)
50 {
51         dbg("caught signal %d", signum);
52         switch (signum) {
53         case SIGALRM:
54                 dbg("event timeout reached");
55                 break;
56
57         default:
58                 dbg("unhandled signal");
59         }
60 }
61
62 static void dump_queue(void)
63 {
64         struct hotplug_msg *p;
65         p = head;
66
67         dbg("next expected sequence is %d", expect_seqnum);
68         while(p != NULL) {
69                 dbg("sequence %d in queue", p->seqnum);
70                 p = p->next;
71         }
72 }
73
74 static void dump_msg(struct hotplug_msg *pmsg)
75 {
76         dbg("sequence %d, '%s', '%s', '%s'",
77             pmsg->seqnum, pmsg->action, pmsg->devpath, pmsg->subsystem);
78 }
79
80 static int dispatch_msg(struct hotplug_msg *pmsg)
81 {
82         pid_t pid;
83         char *argv[3];
84         extern char **environ;
85
86         dump_msg(pmsg);
87
88         setenv("ACTION", pmsg->action, 1);
89         setenv("DEVPATH", pmsg->devpath, 1);
90         argv[0] = DEFAULT_UDEV_EXEC;
91         argv[1] = pmsg->subsystem;
92         argv[2] = NULL;
93
94         pid = fork();
95         switch (pid) {
96         case 0:
97                 /* child */
98                 execve(argv[0], argv, environ);
99                 dbg("exec of child failed");
100                 exit(1);
101                 break;
102         case -1:
103                 dbg("fork of child failed");
104                 return -1;
105         default:
106                 wait(0);
107         }
108         return 0;
109 }
110
111 static void set_timer(int seconds)
112 {
113         signal(SIGALRM, sig_alarmhandler);
114         alarm(seconds);
115 }
116
117 static void check_queue(void)
118 {
119         struct hotplug_msg *p;
120         p = head;
121
122         dump_queue();
123         while(head != NULL && head->seqnum == expect_seqnum) {
124                 dispatch_msg(head);
125                 expect_seqnum++;
126                 p = head;
127                 head = head->next;
128                 free(p);
129         }
130         if (head != NULL)
131                 set_timer(EVENT_TIMEOUT_SECONDS);
132         else
133                 set_timer(DAEMON_TIMEOUT_SECONDS);
134 }
135
136 static void add_queue(struct hotplug_msg *pmsg)
137 {
138         struct hotplug_msg *pnewmsg;
139         struct hotplug_msg *p;
140         struct hotplug_msg *p1;
141
142         p = head;
143         p1 = NULL;
144         pnewmsg = malloc(sizeof(struct hotplug_msg));
145         *pnewmsg = *pmsg;
146         pnewmsg->next = NULL;
147         while(p != NULL && pmsg->seqnum > p->seqnum) {
148                 p1 = p;
149                 p = p->next;
150         }
151         pnewmsg->next = p;
152         if (p1 == NULL) {
153                 head = pnewmsg;
154         } else {
155                 p1->next = pnewmsg;
156         }
157         dump_queue();
158 }
159
160 static int process_queue(void)
161 {
162         int msgid;
163         key_t key;
164         struct hotplug_msg *pmsg;
165         char buf[BUFFER_SIZE];
166         int ret;
167
168         key = ftok(DEFAULT_UDEVD_EXEC, IPC_KEY_ID);
169         pmsg = (struct hotplug_msg *) buf;
170         msgid = msgget(key, IPC_CREAT);
171         if (msgid == -1) {
172                 dbg("open message queue error");
173                 return -1;
174         }
175         while (1) {
176                 ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
177                 if (ret != -1) {
178                         dbg("current sequence %d, expected sequence %d", pmsg->seqnum, expect_seqnum);
179
180                         /* init expected sequence with value from first call */
181                         if (expect_seqnum == 0) {
182                                 expect_seqnum = pmsg->seqnum;
183                                 dbg("init next expected sequence number to %d", expect_seqnum);
184                         }
185
186                         if (pmsg->seqnum > expect_seqnum) {
187                                 add_queue(pmsg);
188                                 set_timer(EVENT_TIMEOUT_SECONDS);
189                         } else {
190                                 if (pmsg->seqnum == expect_seqnum) {
191                                         dispatch_msg(pmsg);
192                                         expect_seqnum++;
193                                         check_queue();
194                                 } else {
195                                         dbg("timeout event for unexpected sequence number %d", pmsg->seqnum);
196                                 }
197                         }
198                 } else {
199                         if (errno == EINTR) {
200                                 if (head != NULL) {
201                                         /* event timeout, skip all missing, proceed with next queued event */
202                                         info("timeout reached, skip events %d - %d", expect_seqnum, head->seqnum-1);
203                                         expect_seqnum = head->seqnum;
204                                 } else {
205                                         info("we have nothing to do, so daemon exits...");
206                                         exit(0);
207                                 }
208                                 check_queue();
209                         } else {
210                                 dbg("ipc message receive error '%s'", strerror(errno));
211                         }
212                 }
213         }
214         return 0;
215 }
216
217 static void sig_handler(int signum)
218 {
219         dbg("caught signal %d", signum);
220         switch (signum) {
221                 case SIGINT:
222                 case SIGTERM:
223                 case SIGKILL:
224                         exit(20 + signum);
225                         break;
226
227                 default:
228                         dbg("unhandled signal");
229         }
230 }
231
232 int main(int argc, char *argv[])
233 {
234         /* set up signal handler */
235         signal(SIGINT, sig_handler);
236         signal(SIGTERM, sig_handler);
237         signal(SIGKILL, sig_handler);
238
239         /* we exit if we have nothing to do, next event will start us again */
240         set_timer(DAEMON_TIMEOUT_SECONDS);
241
242         /* main loop */
243         process_queue();
244         return 0;
245 }