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