chiark / gitweb /
6af265a3898b811a07a8d08a915698beda679902
[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 TIMEOUT_SECONDS         10
42
43 static void reset_timer(void);
44 static void reset_queue(void);
45
46
47 static int expect_seqnum = 0;
48 static int timeout_value = TIMEOUT_SECONDS;
49 static int timeout = 0;
50 static struct hotplug_msg *head = NULL;
51 static char exec_program[100];
52
53
54 static void sig_handler(int signum)
55 {
56         dbg("caught signal %d", signum);
57         switch (signum) {
58                 case SIGHUP:
59                         dbg("reset requested, all waiting events killed");
60                         reset_timer();
61                         reset_queue();
62                         timeout = 0;
63                         expect_seqnum = 0;
64                         break;
65
66                 case SIGINT:
67                 case SIGTERM:
68                 case SIGKILL:
69                         exit(20 + signum);
70                         break;
71
72                 default:
73                         dbg("unhandled signal");
74         }
75 }
76
77 static void sig_alarmhandler(int signum)
78 {
79         dbg("caught signal %d", signum);
80         switch (signum) {
81         case SIGALRM:
82                 timeout = 1;
83                 dbg("event timeout reached");
84                 break;
85
86         default:
87                 dbg("unhandled signal");
88         }
89 }
90
91 static void dump_queue(void)
92 {
93         struct hotplug_msg *p;
94         p = head;
95
96         dbg("next expected sequence is %d", expect_seqnum);
97         while(p) {
98                 dbg("sequence %d in queue", p->seqnum);
99                 p=p->next;
100         }
101 }
102
103 static void dump_msg(struct hotplug_msg *pmsg)
104 {
105         dbg("sequence %d, '%s', '%s', '%s'",
106             pmsg->seqnum, pmsg->action, pmsg->devpath, pmsg->subsystem);
107 }
108
109 static int dispatch_msg(struct hotplug_msg *pmsg)
110 {
111         pid_t pid;
112         char *argv[3];
113         int retval;
114         extern char **environ;
115
116         dump_msg(pmsg);
117         dbg("exec '%s'", exec_program);
118
119         setenv("ACTION", pmsg->action, 1);
120         setenv("DEVPATH", pmsg->devpath, 1);
121
122         argv[0] = exec_program;
123         argv[1] = pmsg->subsystem;
124         argv[2] = NULL;
125
126         pid = fork();
127         switch (pid) {
128         case 0:
129                 retval = execve(argv[0], argv, environ);
130                 if (retval != 0) {
131                         dbg("child execve failed");
132                         exit(1);
133                 }
134                 break;
135         case -1:
136                 dbg("fork failed");
137                 return -1;
138         default:
139                 wait(0);
140                 break;
141         }
142         return 0;
143 }
144
145 static void reset_timer(void)
146 {
147         alarm(0);
148 }
149
150 static void set_timer(void)
151 {
152         signal(SIGALRM, sig_alarmhandler);
153         alarm(timeout_value);
154 }
155
156 static void reset_queue(void)
157 {
158         struct hotplug_msg *p;
159         p = head;
160
161         while(head) {
162                 p = head;
163                 head = head->next;
164                 free(p);
165         }
166 }
167
168 static void check_queue(void)
169 {
170         struct hotplug_msg *p;
171         p = head;
172
173         dump_queue();
174         while(head && head->seqnum == expect_seqnum) {
175                 dispatch_msg(head);
176                 expect_seqnum++;
177                 p = head;
178                 head = head->next;
179                 free(p);
180         }
181         if (head != NULL)
182                 set_timer();
183         else
184                 reset_timer();
185 }
186
187 static void add_queue(struct hotplug_msg *pmsg)
188 {
189         struct hotplug_msg *pnewmsg;
190         struct hotplug_msg *p;
191         struct hotplug_msg *p1;
192
193         p = head;
194         p1 = NULL;
195         pnewmsg = malloc(sizeof(struct hotplug_msg));
196         *pnewmsg = *pmsg;
197         pnewmsg->next = NULL;
198         while(p && pmsg->seqnum > p->seqnum) {
199                 p1 = p;
200                 p = p->next;
201         }
202         pnewmsg->next = p;
203         if (p1 == NULL) {
204                 head = pnewmsg;
205         } else {
206                 p1->next = pnewmsg;
207         }
208         dump_queue();
209 }
210
211 static int process_queue(void)
212 {
213         int msgid;
214         key_t key;
215         struct hotplug_msg *pmsg;
216         char buf[BUFFER_SIZE];
217         int ret;
218
219         key = ftok(DEFAULT_EXEC_PROGRAM, IPC_KEY_ID);
220         pmsg = (struct hotplug_msg *) buf;
221         msgid = msgget(key, IPC_CREAT);
222         if (msgid == -1) {
223                 dbg("open message queue error");
224                 goto exit;
225         }
226         while (1) {
227                 ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
228                 if (ret != -1) {
229                         dbg("current sequence %d, expected sequence %d", pmsg->seqnum, expect_seqnum);
230
231                         /* init expected sequence with value from first call */
232                         if (expect_seqnum == 0) {
233                                 expect_seqnum = pmsg->seqnum;
234                                 dbg("init next expected sequence number to %d", expect_seqnum);
235                         }
236
237                         if (pmsg->seqnum > expect_seqnum) {
238                                 add_queue(pmsg);
239                                 set_timer();
240                         } else {
241                                 if (pmsg->seqnum == expect_seqnum) {
242                                         dispatch_msg(pmsg);
243                                         expect_seqnum++;
244                                         check_queue();
245                                 } else {
246                                         dbg("timeout event for unexpected sequence number %d", pmsg->seqnum);
247                                 }
248                         }
249                 } else
250                         if (errno == EINTR) {
251                                 if (head != NULL) {
252                                         /* timeout, skip all missing, proceed with next queued event */
253                                         dbg("timeout reached, skip events %d - %d", expect_seqnum, head->seqnum-1);
254                                         expect_seqnum = head->seqnum;
255                                 }
256                                 check_queue();
257                                 timeout = 0;
258                         } else {
259                                 dbg("ipc message receive error '%s'", strerror(errno));
260                         }
261         }
262         return 0;
263 exit:
264         return -1;
265 }
266
267 int main(int argc, char *argv[])
268 {
269         /* get program to exec on events */
270         if (argc == 2)
271                 strncpy(exec_program, argv[1], sizeof(exec_program));
272         else
273                 strcpy(exec_program, DEFAULT_EXEC_PROGRAM);
274
275         /* set up signal handler */
276         signal(SIGINT, sig_handler);
277         signal(SIGTERM, sig_handler);
278         signal(SIGKILL, sig_handler);
279         signal(SIGHUP, sig_handler);
280
281         /* main loop */
282         process_queue();
283         return 0;
284 }