]> git.ipfire.org Git - thirdparty/systemd.git/blob - udevd.c
[PATCH] make udevd only have one instance running at a time
[thirdparty/systemd.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 }