]>
Commit | Line | Data |
---|---|---|
d46f37fd | 1 | /* |
22582bb2 | 2 | * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org> |
d46f37fd | 3 | * |
55e9959b KS |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation, either version 2 of the License, or | |
7 | * (at your option) any later version. | |
d46f37fd | 8 | * |
55e9959b KS |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
d46f37fd KS |
16 | */ |
17 | ||
d46f37fd | 18 | #include <ctype.h> |
07630cea LP |
19 | #include <errno.h> |
20 | #include <fcntl.h> | |
959e8b5d | 21 | #include <net/if.h> |
0a6f50c0 | 22 | #include <poll.h> |
07630cea LP |
23 | #include <stddef.h> |
24 | #include <stdio.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
2181d30a | 27 | #include <sys/epoll.h> |
07630cea | 28 | #include <sys/prctl.h> |
2181d30a | 29 | #include <sys/signalfd.h> |
07630cea LP |
30 | #include <sys/wait.h> |
31 | #include <unistd.h> | |
d46f37fd | 32 | |
b5efdb8a | 33 | #include "alloc-util.h" |
3ffd4af2 | 34 | #include "fd-util.h" |
f97b34a6 | 35 | #include "format-util.h" |
07630cea | 36 | #include "netlink-util.h" |
8128f229 | 37 | #include "process-util.h" |
24882e06 | 38 | #include "signal-util.h" |
07630cea | 39 | #include "string-util.h" |
24882e06 | 40 | #include "udev.h" |
8128f229 TG |
41 | |
42 | typedef struct Spawn { | |
43 | const char *cmd; | |
44 | pid_t pid; | |
45 | usec_t timeout_warn; | |
46 | usec_t timeout; | |
53318514 | 47 | bool accept_failure; |
8128f229 | 48 | } Spawn; |
d46f37fd | 49 | |
9ec6e95b | 50 | struct udev_event *udev_event_new(struct udev_device *dev) { |
912541b0 KS |
51 | struct udev *udev = udev_device_get_udev(dev); |
52 | struct udev_event *event; | |
53 | ||
955d98c9 | 54 | event = new0(struct udev_event, 1); |
912541b0 KS |
55 | if (event == NULL) |
56 | return NULL; | |
57 | event->dev = dev; | |
58 | event->udev = udev; | |
59 | udev_list_init(udev, &event->run_list, false); | |
c26547d6 | 60 | udev_list_init(udev, &event->seclabel_list, false); |
8128f229 | 61 | event->birth_usec = clock_boottime_or_monotonic(); |
912541b0 | 62 | return event; |
aa8734ff KS |
63 | } |
64 | ||
9ec6e95b | 65 | void udev_event_unref(struct udev_event *event) { |
912541b0 KS |
66 | if (event == NULL) |
67 | return; | |
1c4baffc | 68 | sd_netlink_unref(event->rtnl); |
912541b0 | 69 | udev_list_cleanup(&event->run_list); |
c26547d6 | 70 | udev_list_cleanup(&event->seclabel_list); |
912541b0 KS |
71 | free(event->program_result); |
72 | free(event->name); | |
912541b0 | 73 | free(event); |
aa8734ff KS |
74 | } |
75 | ||
0d53705b DS |
76 | enum subst_type { |
77 | SUBST_UNKNOWN, | |
78 | SUBST_DEVNODE, | |
79 | SUBST_ATTR, | |
80 | SUBST_ENV, | |
81 | SUBST_KERNEL, | |
82 | SUBST_KERNEL_NUMBER, | |
83 | SUBST_DRIVER, | |
84 | SUBST_DEVPATH, | |
85 | SUBST_ID, | |
86 | SUBST_MAJOR, | |
87 | SUBST_MINOR, | |
88 | SUBST_RESULT, | |
89 | SUBST_PARENT, | |
90 | SUBST_NAME, | |
91 | SUBST_LINKS, | |
92 | SUBST_ROOT, | |
93 | SUBST_SYS, | |
94 | }; | |
95 | ||
96 | static size_t subst_format_var(struct udev_event *event, struct udev_device *dev, | |
97 | enum subst_type type, char *attr, | |
98 | char **dest, size_t l) { | |
99 | char *s = *dest; | |
100 | ||
101 | switch (type) { | |
102 | case SUBST_DEVPATH: | |
103 | l = strpcpy(&s, l, udev_device_get_devpath(dev)); | |
104 | break; | |
105 | case SUBST_KERNEL: | |
106 | l = strpcpy(&s, l, udev_device_get_sysname(dev)); | |
107 | break; | |
108 | case SUBST_KERNEL_NUMBER: | |
109 | if (udev_device_get_sysnum(dev) == NULL) | |
110 | break; | |
111 | l = strpcpy(&s, l, udev_device_get_sysnum(dev)); | |
112 | break; | |
113 | case SUBST_ID: | |
114 | if (event->dev_parent == NULL) | |
115 | break; | |
116 | l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); | |
117 | break; | |
118 | case SUBST_DRIVER: { | |
119 | const char *driver; | |
120 | ||
121 | if (event->dev_parent == NULL) | |
122 | break; | |
123 | ||
124 | driver = udev_device_get_driver(event->dev_parent); | |
125 | if (driver == NULL) | |
126 | break; | |
127 | l = strpcpy(&s, l, driver); | |
128 | break; | |
129 | } | |
130 | case SUBST_MAJOR: { | |
131 | char num[UTIL_PATH_SIZE]; | |
132 | ||
133 | sprintf(num, "%u", major(udev_device_get_devnum(dev))); | |
134 | l = strpcpy(&s, l, num); | |
135 | break; | |
136 | } | |
137 | case SUBST_MINOR: { | |
138 | char num[UTIL_PATH_SIZE]; | |
139 | ||
140 | sprintf(num, "%u", minor(udev_device_get_devnum(dev))); | |
141 | l = strpcpy(&s, l, num); | |
142 | break; | |
143 | } | |
144 | case SUBST_RESULT: { | |
145 | char *rest; | |
146 | int i; | |
147 | ||
148 | if (event->program_result == NULL) | |
149 | break; | |
150 | /* get part of the result string */ | |
151 | i = 0; | |
152 | if (attr != NULL) | |
153 | i = strtoul(attr, &rest, 10); | |
154 | if (i > 0) { | |
155 | char result[UTIL_PATH_SIZE]; | |
156 | char tmp[UTIL_PATH_SIZE]; | |
157 | char *cpos; | |
158 | ||
159 | strscpy(result, sizeof(result), event->program_result); | |
160 | cpos = result; | |
161 | while (--i) { | |
162 | while (cpos[0] != '\0' && !isspace(cpos[0])) | |
163 | cpos++; | |
164 | while (isspace(cpos[0])) | |
165 | cpos++; | |
166 | if (cpos[0] == '\0') | |
167 | break; | |
168 | } | |
169 | if (i > 0) { | |
170 | log_error("requested part of result string not found"); | |
171 | break; | |
172 | } | |
173 | strscpy(tmp, sizeof(tmp), cpos); | |
174 | /* %{2+}c copies the whole string from the second part on */ | |
175 | if (rest[0] != '+') { | |
176 | cpos = strchr(tmp, ' '); | |
177 | if (cpos) | |
178 | cpos[0] = '\0'; | |
179 | } | |
180 | l = strpcpy(&s, l, tmp); | |
181 | } else { | |
182 | l = strpcpy(&s, l, event->program_result); | |
183 | } | |
184 | break; | |
185 | } | |
186 | case SUBST_ATTR: { | |
187 | const char *value = NULL; | |
188 | char vbuf[UTIL_NAME_SIZE]; | |
189 | size_t len; | |
190 | int count; | |
191 | ||
192 | if (attr == NULL) { | |
193 | log_error("missing file parameter for attr"); | |
194 | break; | |
195 | } | |
196 | ||
197 | /* try to read the value specified by "[dmi/id]product_name" */ | |
198 | if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) | |
199 | value = vbuf; | |
200 | ||
201 | /* try to read the attribute the device */ | |
202 | if (value == NULL) | |
203 | value = udev_device_get_sysattr_value(event->dev, attr); | |
204 | ||
205 | /* try to read the attribute of the parent device, other matches have selected */ | |
206 | if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) | |
207 | value = udev_device_get_sysattr_value(event->dev_parent, attr); | |
208 | ||
209 | if (value == NULL) | |
210 | break; | |
211 | ||
212 | /* strip trailing whitespace, and replace unwanted characters */ | |
213 | if (value != vbuf) | |
214 | strscpy(vbuf, sizeof(vbuf), value); | |
215 | len = strlen(vbuf); | |
216 | while (len > 0 && isspace(vbuf[--len])) | |
217 | vbuf[len] = '\0'; | |
218 | count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); | |
219 | if (count > 0) | |
220 | log_debug("%i character(s) replaced" , count); | |
221 | l = strpcpy(&s, l, vbuf); | |
222 | break; | |
223 | } | |
224 | case SUBST_PARENT: { | |
225 | struct udev_device *dev_parent; | |
226 | const char *devnode; | |
227 | ||
228 | dev_parent = udev_device_get_parent(event->dev); | |
229 | if (dev_parent == NULL) | |
230 | break; | |
231 | devnode = udev_device_get_devnode(dev_parent); | |
232 | if (devnode != NULL) | |
233 | l = strpcpy(&s, l, devnode + strlen("/dev/")); | |
234 | break; | |
235 | } | |
236 | case SUBST_DEVNODE: | |
237 | if (udev_device_get_devnode(dev) != NULL) | |
238 | l = strpcpy(&s, l, udev_device_get_devnode(dev)); | |
239 | break; | |
240 | case SUBST_NAME: | |
241 | if (event->name != NULL) | |
242 | l = strpcpy(&s, l, event->name); | |
243 | else if (udev_device_get_devnode(dev) != NULL) | |
244 | l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); | |
245 | else | |
246 | l = strpcpy(&s, l, udev_device_get_sysname(dev)); | |
247 | break; | |
248 | case SUBST_LINKS: { | |
249 | struct udev_list_entry *list_entry; | |
250 | ||
251 | list_entry = udev_device_get_devlinks_list_entry(dev); | |
252 | if (list_entry == NULL) | |
253 | break; | |
254 | l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); | |
255 | udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) | |
256 | l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); | |
257 | break; | |
258 | } | |
259 | case SUBST_ROOT: | |
260 | l = strpcpy(&s, l, "/dev"); | |
261 | break; | |
262 | case SUBST_SYS: | |
263 | l = strpcpy(&s, l, "/sys"); | |
264 | break; | |
265 | case SUBST_ENV: | |
266 | if (attr == NULL) { | |
267 | break; | |
268 | } else { | |
269 | const char *value; | |
270 | ||
271 | value = udev_device_get_property_value(event->dev, attr); | |
272 | if (value == NULL) | |
273 | break; | |
274 | l = strpcpy(&s, l, value); | |
275 | break; | |
276 | } | |
277 | default: | |
278 | log_error("unknown substitution type=%i", type); | |
279 | break; | |
280 | } | |
281 | ||
282 | *dest = s; | |
283 | ||
284 | return l; | |
285 | } | |
286 | ||
e20a9171 DS |
287 | size_t udev_event_apply_format(struct udev_event *event, |
288 | const char *src, char *dest, size_t size, | |
289 | bool replace_whitespace) { | |
912541b0 | 290 | struct udev_device *dev = event->dev; |
912541b0 | 291 | static const struct subst_map { |
04a9d3a0 KS |
292 | const char *name; |
293 | const char fmt; | |
912541b0 KS |
294 | enum subst_type type; |
295 | } map[] = { | |
3fd0c4c6 KS |
296 | { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE }, |
297 | { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE }, | |
298 | { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, | |
299 | { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, | |
300 | { .name = "env", .fmt = 'E', .type = SUBST_ENV }, | |
301 | { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, | |
302 | { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, | |
303 | { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, | |
304 | { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, | |
305 | { .name = "id", .fmt = 'b', .type = SUBST_ID }, | |
306 | { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, | |
307 | { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, | |
308 | { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, | |
309 | { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, | |
310 | { .name = "name", .fmt = 'D', .type = SUBST_NAME }, | |
311 | { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, | |
312 | { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, | |
313 | { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, | |
912541b0 KS |
314 | }; |
315 | const char *from; | |
316 | char *s; | |
317 | size_t l; | |
318 | ||
3b64e4d4 TG |
319 | assert(dev); |
320 | ||
912541b0 KS |
321 | from = src; |
322 | s = dest; | |
323 | l = size; | |
324 | ||
325 | for (;;) { | |
326 | enum subst_type type = SUBST_UNKNOWN; | |
e20a9171 DS |
327 | char attrbuf[UTIL_PATH_SIZE], sbuf[UTIL_PATH_SIZE]; |
328 | char *attr = NULL, *_s; | |
329 | size_t _l; | |
330 | bool replws = replace_whitespace; | |
912541b0 KS |
331 | |
332 | while (from[0] != '\0') { | |
333 | if (from[0] == '$') { | |
334 | /* substitute named variable */ | |
335 | unsigned int i; | |
336 | ||
337 | if (from[1] == '$') { | |
338 | from++; | |
339 | goto copy; | |
340 | } | |
341 | ||
8fef0ff2 | 342 | for (i = 0; i < ELEMENTSOF(map); i++) { |
33502ffe | 343 | if (startswith(&from[1], map[i].name)) { |
912541b0 KS |
344 | type = map[i].type; |
345 | from += strlen(map[i].name)+1; | |
912541b0 KS |
346 | goto subst; |
347 | } | |
348 | } | |
349 | } else if (from[0] == '%') { | |
350 | /* substitute format char */ | |
351 | unsigned int i; | |
352 | ||
353 | if (from[1] == '%') { | |
354 | from++; | |
355 | goto copy; | |
356 | } | |
357 | ||
8fef0ff2 | 358 | for (i = 0; i < ELEMENTSOF(map); i++) { |
912541b0 KS |
359 | if (from[1] == map[i].fmt) { |
360 | type = map[i].type; | |
361 | from += 2; | |
912541b0 KS |
362 | goto subst; |
363 | } | |
364 | } | |
365 | } | |
065db052 | 366 | copy: |
912541b0 KS |
367 | /* copy char */ |
368 | if (l == 0) | |
369 | goto out; | |
370 | s[0] = from[0]; | |
371 | from++; | |
372 | s++; | |
373 | l--; | |
374 | } | |
375 | ||
376 | goto out; | |
065db052 | 377 | subst: |
912541b0 KS |
378 | /* extract possible $format{attr} */ |
379 | if (from[0] == '{') { | |
380 | unsigned int i; | |
381 | ||
382 | from++; | |
383 | for (i = 0; from[i] != '}'; i++) { | |
384 | if (from[i] == '\0') { | |
9f6445e3 | 385 | log_error("missing closing brace for format '%s'", src); |
912541b0 KS |
386 | goto out; |
387 | } | |
388 | } | |
389 | if (i >= sizeof(attrbuf)) | |
390 | goto out; | |
391 | memcpy(attrbuf, from, i); | |
392 | attrbuf[i] = '\0'; | |
393 | from += i+1; | |
394 | attr = attrbuf; | |
395 | } else { | |
396 | attr = NULL; | |
397 | } | |
398 | ||
e20a9171 DS |
399 | /* result subst handles space as field separator */ |
400 | if (type == SUBST_RESULT) | |
401 | replws = false; | |
402 | ||
403 | if (replws) { | |
404 | /* store dest string ptr and remaining len */ | |
405 | _s = s; | |
406 | _l = l; | |
407 | /* temporarily use sbuf */ | |
42d76879 | 408 | s = sbuf; |
e20a9171 DS |
409 | l = UTIL_PATH_SIZE; |
410 | } | |
411 | ||
0d53705b | 412 | l = subst_format_var(event, dev, type, attr, &s, l); |
e20a9171 DS |
413 | |
414 | /* replace whitespace in sbuf and copy to dest */ | |
415 | if (replws) { | |
416 | size_t tmplen = UTIL_PATH_SIZE - l; | |
417 | ||
418 | /* restore s and l to dest string values */ | |
419 | s = _s; | |
420 | l = _l; | |
421 | ||
422 | /* copy ws-replaced value to s */ | |
423 | tmplen = util_replace_whitespace(sbuf, s, MIN(tmplen, l)); | |
424 | l -= tmplen; | |
425 | s += tmplen; | |
426 | } | |
912541b0 | 427 | } |
065db052 KS |
428 | |
429 | out: | |
912541b0 | 430 | s[0] = '\0'; |
912541b0 | 431 | return l; |
f1128767 KS |
432 | } |
433 | ||
2181d30a | 434 | static int spawn_exec(struct udev_event *event, |
8314de1d | 435 | const char *cmd, char *const argv[], char **envp, |
9ec6e95b | 436 | int fd_stdout, int fd_stderr) { |
19c784c4 | 437 | _cleanup_close_ int fd = -1; |
f6e0a353 | 438 | int r; |
912541b0 KS |
439 | |
440 | /* discard child output or connect to pipe */ | |
441 | fd = open("/dev/null", O_RDWR); | |
442 | if (fd >= 0) { | |
f6e0a353 TG |
443 | r = dup2(fd, STDIN_FILENO); |
444 | if (r < 0) | |
445 | log_warning_errno(errno, "redirecting stdin failed: %m"); | |
446 | ||
447 | if (fd_stdout < 0) { | |
448 | r = dup2(fd, STDOUT_FILENO); | |
449 | if (r < 0) | |
450 | log_warning_errno(errno, "redirecting stdout failed: %m"); | |
451 | } | |
452 | ||
453 | if (fd_stderr < 0) { | |
454 | r = dup2(fd, STDERR_FILENO); | |
455 | if (r < 0) | |
456 | log_warning_errno(errno, "redirecting stderr failed: %m"); | |
457 | } | |
19c784c4 | 458 | } else |
f6e0a353 | 459 | log_warning_errno(errno, "open /dev/null failed: %m"); |
912541b0 KS |
460 | |
461 | /* connect pipes to std{out,err} */ | |
462 | if (fd_stdout >= 0) { | |
f6e0a353 TG |
463 | r = dup2(fd_stdout, STDOUT_FILENO); |
464 | if (r < 0) | |
465 | log_warning_errno(errno, "redirecting stdout failed: %m"); | |
466 | ||
467 | fd_stdout = safe_close(fd_stdout); | |
912541b0 | 468 | } |
f6e0a353 | 469 | |
912541b0 | 470 | if (fd_stderr >= 0) { |
f6e0a353 TG |
471 | r = dup2(fd_stderr, STDERR_FILENO); |
472 | if (r < 0) | |
473 | log_warning_errno(errno, "redirecting stdout failed: %m"); | |
474 | ||
475 | fd_stderr = safe_close(fd_stderr); | |
912541b0 KS |
476 | } |
477 | ||
478 | /* terminate child in case parent goes away */ | |
479 | prctl(PR_SET_PDEATHSIG, SIGTERM); | |
480 | ||
8314de1d TG |
481 | /* restore sigmask before exec */ |
482 | (void) reset_signal_mask(); | |
912541b0 KS |
483 | |
484 | execve(argv[0], argv, envp); | |
485 | ||
486 | /* exec failed */ | |
e1427b13 | 487 | return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); |
2181d30a KS |
488 | } |
489 | ||
f18f225c | 490 | static void spawn_read(struct udev_event *event, |
dd5eddd2 KS |
491 | usec_t timeout_usec, |
492 | const char *cmd, | |
493 | int fd_stdout, int fd_stderr, | |
9ec6e95b | 494 | char *result, size_t ressize) { |
4cd5d5ad TG |
495 | _cleanup_close_ int fd_ep = -1; |
496 | struct epoll_event ep_outpipe = { | |
497 | .events = EPOLLIN, | |
498 | .data.ptr = &fd_stdout, | |
499 | }; | |
500 | struct epoll_event ep_errpipe = { | |
501 | .events = EPOLLIN, | |
502 | .data.ptr = &fd_stderr, | |
503 | }; | |
912541b0 | 504 | size_t respos = 0; |
4cd5d5ad | 505 | int r; |
912541b0 KS |
506 | |
507 | /* read from child if requested */ | |
508 | if (fd_stdout < 0 && fd_stderr < 0) | |
509 | return; | |
510 | ||
511 | fd_ep = epoll_create1(EPOLL_CLOEXEC); | |
512 | if (fd_ep < 0) { | |
56f64d95 | 513 | log_error_errno(errno, "error creating epoll fd: %m"); |
4cd5d5ad | 514 | return; |
912541b0 KS |
515 | } |
516 | ||
517 | if (fd_stdout >= 0) { | |
4cd5d5ad TG |
518 | r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); |
519 | if (r < 0) { | |
56f64d95 | 520 | log_error_errno(errno, "fail to add stdout fd to epoll: %m"); |
4cd5d5ad | 521 | return; |
912541b0 KS |
522 | } |
523 | } | |
524 | ||
525 | if (fd_stderr >= 0) { | |
4cd5d5ad TG |
526 | r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); |
527 | if (r < 0) { | |
56f64d95 | 528 | log_error_errno(errno, "fail to add stderr fd to epoll: %m"); |
4cd5d5ad | 529 | return; |
912541b0 KS |
530 | } |
531 | } | |
532 | ||
533 | /* read child output */ | |
534 | while (fd_stdout >= 0 || fd_stderr >= 0) { | |
535 | int timeout; | |
536 | int fdcount; | |
537 | struct epoll_event ev[4]; | |
538 | int i; | |
539 | ||
dd5eddd2 | 540 | if (timeout_usec > 0) { |
40fe8b11 | 541 | usec_t age_usec; |
912541b0 | 542 | |
8128f229 | 543 | age_usec = clock_boottime_or_monotonic() - event->birth_usec; |
dd5eddd2 | 544 | if (age_usec >= timeout_usec) { |
9f6445e3 | 545 | log_error("timeout '%s'", cmd); |
4cd5d5ad | 546 | return; |
912541b0 | 547 | } |
dd5eddd2 | 548 | timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC; |
912541b0 KS |
549 | } else { |
550 | timeout = -1; | |
551 | } | |
552 | ||
8fef0ff2 | 553 | fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout); |
912541b0 KS |
554 | if (fdcount < 0) { |
555 | if (errno == EINTR) | |
556 | continue; | |
56f64d95 | 557 | log_error_errno(errno, "failed to poll: %m"); |
4cd5d5ad TG |
558 | return; |
559 | } else if (fdcount == 0) { | |
9f6445e3 | 560 | log_error("timeout '%s'", cmd); |
4cd5d5ad | 561 | return; |
912541b0 KS |
562 | } |
563 | ||
564 | for (i = 0; i < fdcount; i++) { | |
565 | int *fd = (int *)ev[i].data.ptr; | |
566 | ||
3f796750 TG |
567 | if (*fd < 0) |
568 | continue; | |
569 | ||
912541b0 KS |
570 | if (ev[i].events & EPOLLIN) { |
571 | ssize_t count; | |
572 | char buf[4096]; | |
573 | ||
574 | count = read(*fd, buf, sizeof(buf)-1); | |
575 | if (count <= 0) | |
576 | continue; | |
577 | buf[count] = '\0'; | |
578 | ||
579 | /* store stdout result */ | |
580 | if (result != NULL && *fd == fd_stdout) { | |
581 | if (respos + count < ressize) { | |
582 | memcpy(&result[respos], buf, count); | |
583 | respos += count; | |
584 | } else { | |
1fa2f38f | 585 | log_error("'%s' ressize %zu too short", cmd, ressize); |
912541b0 KS |
586 | } |
587 | } | |
588 | ||
589 | /* log debug output only if we watch stderr */ | |
590 | if (fd_stderr >= 0) { | |
591 | char *pos; | |
592 | char *line; | |
593 | ||
594 | pos = buf; | |
595 | while ((line = strsep(&pos, "\n"))) { | |
596 | if (pos != NULL || line[0] != '\0') | |
9f6445e3 | 597 | log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line); |
912541b0 KS |
598 | } |
599 | } | |
600 | } else if (ev[i].events & EPOLLHUP) { | |
4cd5d5ad TG |
601 | r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); |
602 | if (r < 0) { | |
56f64d95 | 603 | log_error_errno(errno, "failed to remove fd from epoll: %m"); |
4cd5d5ad | 604 | return; |
912541b0 KS |
605 | } |
606 | *fd = -1; | |
607 | } | |
608 | } | |
609 | } | |
610 | ||
611 | /* return the child's stdout string */ | |
baa30fbc | 612 | if (result != NULL) |
912541b0 | 613 | result[respos] = '\0'; |
2181d30a KS |
614 | } |
615 | ||
8128f229 TG |
616 | static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) { |
617 | Spawn *spawn = userdata; | |
618 | char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; | |
912541b0 | 619 | |
8128f229 | 620 | assert(spawn); |
912541b0 | 621 | |
8128f229 | 622 | kill_and_sigcont(spawn->pid, SIGKILL); |
912541b0 | 623 | |
8128f229 TG |
624 | log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid, |
625 | format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); | |
912541b0 | 626 | |
8128f229 TG |
627 | return 1; |
628 | } | |
67117413 | 629 | |
8128f229 TG |
630 | static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) { |
631 | Spawn *spawn = userdata; | |
632 | char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX]; | |
912541b0 | 633 | |
8128f229 | 634 | assert(spawn); |
67117413 | 635 | |
8128f229 TG |
636 | log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid, |
637 | format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout)); | |
638 | ||
639 | return 1; | |
640 | } | |
641 | ||
642 | static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) { | |
643 | Spawn *spawn = userdata; | |
644 | ||
645 | assert(spawn); | |
646 | ||
647 | switch (si->si_code) { | |
648 | case CLD_EXITED: | |
53318514 TG |
649 | if (si->si_status == 0) { |
650 | log_debug("Process '%s' succeeded.", spawn->cmd); | |
8128f229 TG |
651 | sd_event_exit(sd_event_source_get_event(s), 0); |
652 | ||
653 | return 1; | |
53318514 TG |
654 | } else if (spawn->accept_failure) |
655 | log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); | |
656 | else | |
657 | log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status); | |
912541b0 | 658 | |
8128f229 TG |
659 | break; |
660 | case CLD_KILLED: | |
661 | case CLD_DUMPED: | |
53318514 | 662 | log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status)); |
912541b0 | 663 | |
8128f229 TG |
664 | break; |
665 | default: | |
53318514 | 666 | log_error("Process '%s' failed due to unknown reason.", spawn->cmd); |
8128f229 | 667 | } |
912541b0 | 668 | |
8128f229 TG |
669 | sd_event_exit(sd_event_source_get_event(s), -EIO); |
670 | ||
671 | return 1; | |
672 | } | |
673 | ||
674 | static int spawn_wait(struct udev_event *event, | |
675 | usec_t timeout_usec, | |
676 | usec_t timeout_warn_usec, | |
53318514 TG |
677 | const char *cmd, pid_t pid, |
678 | bool accept_failure) { | |
8128f229 TG |
679 | Spawn spawn = { |
680 | .cmd = cmd, | |
681 | .pid = pid, | |
53318514 | 682 | .accept_failure = accept_failure, |
8128f229 | 683 | }; |
4afd3348 | 684 | _cleanup_(sd_event_unrefp) sd_event *e = NULL; |
8128f229 TG |
685 | int r, ret; |
686 | ||
687 | r = sd_event_new(&e); | |
688 | if (r < 0) | |
689 | return r; | |
690 | ||
691 | if (timeout_usec > 0) { | |
692 | usec_t usec, age_usec; | |
693 | ||
694 | usec = now(clock_boottime_or_monotonic()); | |
695 | age_usec = usec - event->birth_usec; | |
696 | if (age_usec < timeout_usec) { | |
697 | if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) { | |
698 | spawn.timeout_warn = timeout_warn_usec - age_usec; | |
699 | ||
920b52e4 TA |
700 | r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), |
701 | usec + spawn.timeout_warn, USEC_PER_SEC, | |
702 | on_spawn_timeout_warning, &spawn); | |
8128f229 TG |
703 | if (r < 0) |
704 | return r; | |
912541b0 | 705 | } |
8128f229 TG |
706 | |
707 | spawn.timeout = timeout_usec - age_usec; | |
708 | ||
709 | r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), | |
710 | usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn); | |
711 | if (r < 0) | |
712 | return r; | |
912541b0 KS |
713 | } |
714 | } | |
8128f229 TG |
715 | |
716 | r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn); | |
717 | if (r < 0) | |
718 | return r; | |
719 | ||
720 | r = sd_event_loop(e); | |
721 | if (r < 0) | |
722 | return r; | |
723 | ||
724 | r = sd_event_get_exit_code(e, &ret); | |
725 | if (r < 0) | |
726 | return r; | |
727 | ||
728 | return ret; | |
2181d30a KS |
729 | } |
730 | ||
9ec6e95b | 731 | int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) { |
912541b0 KS |
732 | int i = 0; |
733 | char *pos; | |
734 | ||
735 | if (strchr(cmd, ' ') == NULL) { | |
736 | argv[i++] = cmd; | |
737 | goto out; | |
738 | } | |
739 | ||
740 | pos = cmd; | |
741 | while (pos != NULL && pos[0] != '\0') { | |
742 | if (pos[0] == '\'') { | |
743 | /* do not separate quotes */ | |
744 | pos++; | |
745 | argv[i] = strsep(&pos, "\'"); | |
746 | if (pos != NULL) | |
747 | while (pos[0] == ' ') | |
748 | pos++; | |
749 | } else { | |
750 | argv[i] = strsep(&pos, " "); | |
751 | if (pos != NULL) | |
752 | while (pos[0] == ' ') | |
753 | pos++; | |
754 | } | |
912541b0 KS |
755 | i++; |
756 | } | |
e216e514 | 757 | out: |
912541b0 KS |
758 | argv[i] = NULL; |
759 | if (argc) | |
760 | *argc = i; | |
761 | return 0; | |
e216e514 KS |
762 | } |
763 | ||
2181d30a | 764 | int udev_event_spawn(struct udev_event *event, |
dd5eddd2 | 765 | usec_t timeout_usec, |
67117413 | 766 | usec_t timeout_warn_usec, |
53318514 | 767 | bool accept_failure, |
bbf35206 | 768 | const char *cmd, |
dd5eddd2 | 769 | char *result, size_t ressize) { |
912541b0 KS |
770 | int outpipe[2] = {-1, -1}; |
771 | int errpipe[2] = {-1, -1}; | |
772 | pid_t pid; | |
912541b0 KS |
773 | int err = 0; |
774 | ||
912541b0 | 775 | /* pipes from child to parent */ |
25e773ee | 776 | if (result != NULL || log_get_max_level() >= LOG_INFO) { |
912541b0 | 777 | if (pipe2(outpipe, O_NONBLOCK) != 0) { |
76ef789d | 778 | err = log_error_errno(errno, "pipe failed: %m"); |
912541b0 KS |
779 | goto out; |
780 | } | |
781 | } | |
25e773ee | 782 | if (log_get_max_level() >= LOG_INFO) { |
912541b0 | 783 | if (pipe2(errpipe, O_NONBLOCK) != 0) { |
76ef789d | 784 | err = log_error_errno(errno, "pipe failed: %m"); |
912541b0 KS |
785 | goto out; |
786 | } | |
787 | } | |
788 | ||
912541b0 KS |
789 | pid = fork(); |
790 | switch(pid) { | |
791 | case 0: | |
bbf35206 TG |
792 | { |
793 | char arg[UTIL_PATH_SIZE]; | |
794 | char *argv[128]; | |
795 | char program[UTIL_PATH_SIZE]; | |
796 | ||
912541b0 | 797 | /* child closes parent's ends of pipes */ |
7f6e12b0 LP |
798 | outpipe[READ_END] = safe_close(outpipe[READ_END]); |
799 | errpipe[READ_END] = safe_close(errpipe[READ_END]); | |
912541b0 | 800 | |
bbf35206 TG |
801 | strscpy(arg, sizeof(arg), cmd); |
802 | udev_build_argv(event->udev, arg, NULL, argv); | |
803 | ||
804 | /* allow programs in /usr/lib/udev/ to be called without the path */ | |
805 | if (argv[0][0] != '/') { | |
806 | strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); | |
807 | argv[0] = program; | |
808 | } | |
809 | ||
9f6445e3 | 810 | log_debug("starting '%s'", cmd); |
912541b0 | 811 | |
bbf35206 | 812 | spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev), |
b49d9b50 | 813 | outpipe[WRITE_END], errpipe[WRITE_END]); |
912541b0 | 814 | |
bbf35206 TG |
815 | _exit(2); |
816 | } | |
912541b0 | 817 | case -1: |
56f64d95 | 818 | log_error_errno(errno, "fork of '%s' failed: %m", cmd); |
912541b0 KS |
819 | err = -1; |
820 | goto out; | |
821 | default: | |
822 | /* parent closed child's ends of pipes */ | |
7f6e12b0 LP |
823 | outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); |
824 | errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); | |
912541b0 | 825 | |
dd5eddd2 KS |
826 | spawn_read(event, |
827 | timeout_usec, | |
828 | cmd, | |
829 | outpipe[READ_END], errpipe[READ_END], | |
830 | result, ressize); | |
912541b0 | 831 | |
53318514 | 832 | err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); |
912541b0 | 833 | } |
2181d30a KS |
834 | |
835 | out: | |
912541b0 KS |
836 | if (outpipe[READ_END] >= 0) |
837 | close(outpipe[READ_END]); | |
838 | if (outpipe[WRITE_END] >= 0) | |
839 | close(outpipe[WRITE_END]); | |
840 | if (errpipe[READ_END] >= 0) | |
841 | close(errpipe[READ_END]); | |
842 | if (errpipe[WRITE_END] >= 0) | |
843 | close(errpipe[WRITE_END]); | |
844 | return err; | |
2181d30a KS |
845 | } |
846 | ||
9ec6e95b | 847 | static int rename_netif(struct udev_event *event) { |
912541b0 | 848 | struct udev_device *dev = event->dev; |
16d26d55 TG |
849 | char name[IFNAMSIZ]; |
850 | const char *oldname; | |
851 | int r; | |
852 | ||
853 | oldname = udev_device_get_sysname(dev); | |
912541b0 | 854 | |
16d26d55 | 855 | strscpy(name, IFNAMSIZ, event->name); |
912541b0 | 856 | |
4c83d994 | 857 | r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); |
f647962d MS |
858 | if (r < 0) |
859 | return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); | |
16d26d55 | 860 | |
ff49bc32 | 861 | log_debug("renamed network interface '%s' to '%s'", oldname, name); |
16d26d55 | 862 | |
4c83d994 | 863 | return 0; |
d46f37fd KS |
864 | } |
865 | ||
dd5eddd2 | 866 | void udev_event_execute_rules(struct udev_event *event, |
adeba500 KS |
867 | usec_t timeout_usec, usec_t timeout_warn_usec, |
868 | struct udev_list *properties_list, | |
8314de1d | 869 | struct udev_rules *rules) { |
912541b0 | 870 | struct udev_device *dev = event->dev; |
912541b0 KS |
871 | |
872 | if (udev_device_get_subsystem(dev) == NULL) | |
1ea97217 | 873 | return; |
912541b0 | 874 | |
090be865 | 875 | if (streq(udev_device_get_action(dev), "remove")) { |
107f2e25 TG |
876 | udev_device_read_db(dev); |
877 | udev_device_tag_index(dev, NULL, false); | |
878 | udev_device_delete_db(dev); | |
879 | ||
912541b0 KS |
880 | if (major(udev_device_get_devnum(dev)) != 0) |
881 | udev_watch_end(event->udev, dev); | |
882 | ||
adeba500 KS |
883 | udev_rules_apply_to_event(rules, event, |
884 | timeout_usec, timeout_warn_usec, | |
8314de1d | 885 | properties_list); |
912541b0 KS |
886 | |
887 | if (major(udev_device_get_devnum(dev)) != 0) | |
e7f32890 | 888 | udev_node_remove(dev); |
912541b0 | 889 | } else { |
8f0f13f0 | 890 | event->dev_db = udev_device_clone_with_db(dev); |
912541b0 | 891 | if (event->dev_db != NULL) { |
912541b0 KS |
892 | /* disable watch during event processing */ |
893 | if (major(udev_device_get_devnum(dev)) != 0) | |
894 | udev_watch_end(event->udev, event->dev_db); | |
912541b0 | 895 | |
9eba69df ZJS |
896 | if (major(udev_device_get_devnum(dev)) == 0 && |
897 | streq(udev_device_get_action(dev), "move")) | |
898 | udev_device_copy_properties(dev, event->dev_db); | |
899 | } | |
b081b27e | 900 | |
adeba500 KS |
901 | udev_rules_apply_to_event(rules, event, |
902 | timeout_usec, timeout_warn_usec, | |
8314de1d | 903 | properties_list); |
912541b0 KS |
904 | |
905 | /* rename a new network interface, if needed */ | |
090be865 TA |
906 | if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && |
907 | event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { | |
1ea97217 | 908 | int r; |
912541b0 | 909 | |
1ea97217 | 910 | r = rename_netif(event); |
243d1825 TG |
911 | if (r < 0) |
912 | log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev), | |
913 | udev_device_get_sysname(dev), event->name); | |
914 | else { | |
243d1825 TG |
915 | r = udev_device_rename(dev, event->name); |
916 | if (r < 0) | |
917 | log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m", | |
918 | udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name); | |
3738cc85 | 919 | else |
9f6445e3 | 920 | log_debug("changed devpath to '%s'", udev_device_get_devpath(dev)); |
912541b0 KS |
921 | } |
922 | } | |
923 | ||
b0a00806 | 924 | if (major(udev_device_get_devnum(dev)) > 0) { |
9a8ae49d KS |
925 | bool apply; |
926 | ||
912541b0 KS |
927 | /* remove/update possible left-over symlinks from old database entry */ |
928 | if (event->dev_db != NULL) | |
929 | udev_node_update_old_links(dev, event->dev_db); | |
930 | ||
1edefa4f KS |
931 | if (!event->owner_set) |
932 | event->uid = udev_device_get_devnode_uid(dev); | |
933 | ||
934 | if (!event->group_set) | |
935 | event->gid = udev_device_get_devnode_gid(dev); | |
936 | ||
912541b0 KS |
937 | if (!event->mode_set) { |
938 | if (udev_device_get_devnode_mode(dev) > 0) { | |
939 | /* kernel supplied value */ | |
940 | event->mode = udev_device_get_devnode_mode(dev); | |
941 | } else if (event->gid > 0) { | |
942 | /* default 0660 if a group is assigned */ | |
943 | event->mode = 0660; | |
944 | } else { | |
945 | /* default 0600 */ | |
946 | event->mode = 0600; | |
947 | } | |
948 | } | |
949 | ||
9a8ae49d | 950 | apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; |
c26547d6 | 951 | udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); |
912541b0 KS |
952 | } |
953 | ||
954 | /* preserve old, or get new initialization timestamp */ | |
1b14c3cf | 955 | udev_device_ensure_usec_initialized(event->dev, event->dev_db); |
912541b0 KS |
956 | |
957 | /* (re)write database file */ | |
912541b0 | 958 | udev_device_tag_index(dev, event->dev_db, true); |
353f6058 | 959 | udev_device_update_db(dev); |
912541b0 KS |
960 | udev_device_set_is_initialized(dev); |
961 | ||
353f6058 | 962 | event->dev_db = udev_device_unref(event->dev_db); |
912541b0 | 963 | } |
d46f37fd | 964 | } |
2d73813e | 965 | |
8314de1d | 966 | void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) { |
912541b0 | 967 | struct udev_list_entry *list_entry; |
912541b0 | 968 | |
912541b0 | 969 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { |
bbf35206 | 970 | char command[UTIL_PATH_SIZE]; |
912541b0 | 971 | const char *cmd = udev_list_entry_get_name(list_entry); |
83cd6b75 KS |
972 | enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry); |
973 | ||
e20a9171 | 974 | udev_event_apply_format(event, cmd, command, sizeof(command), false); |
912541b0 | 975 | |
bbf35206 | 976 | if (builtin_cmd < UDEV_BUILTIN_MAX) |
83cd6b75 | 977 | udev_builtin_run(event->dev, builtin_cmd, command, false); |
bbf35206 | 978 | else { |
912541b0 | 979 | if (event->exec_delay > 0) { |
bbf35206 | 980 | log_debug("delay execution of '%s'", command); |
912541b0 KS |
981 | sleep(event->exec_delay); |
982 | } | |
983 | ||
bbf35206 | 984 | udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0); |
912541b0 KS |
985 | } |
986 | } | |
2d73813e | 987 | } |