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