2017-11-18 17:14:42 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
2008-08-29 20:32:05 +02:00
|
|
|
|
|
|
|
#include <ctype.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2009-08-30 23:58:57 +02:00
|
|
|
#include <net/if.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2008-08-29 20:32:05 +02:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
#include "sd-event.h"
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2018-10-22 22:47:21 +02:00
|
|
|
#include "device-private.h"
|
2018-10-24 23:33:26 +02:00
|
|
|
#include "device-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2018-09-14 21:13:29 +02:00
|
|
|
#include "libudev-device-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "netlink-util.h"
|
2018-09-23 08:41:39 +02:00
|
|
|
#include "path-util.h"
|
2015-05-15 11:35:15 +02:00
|
|
|
#include "process-util.h"
|
2015-05-29 20:14:11 +02:00
|
|
|
#include "signal-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2018-08-29 09:12:22 +02:00
|
|
|
#include "udev-builtin.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "udev-node.h"
|
2018-09-14 21:13:29 +02:00
|
|
|
#include "udev-watch.h"
|
2015-05-29 20:14:11 +02:00
|
|
|
#include "udev.h"
|
2015-05-15 11:35:15 +02:00
|
|
|
|
|
|
|
typedef struct Spawn {
|
|
|
|
const char *cmd;
|
|
|
|
pid_t pid;
|
2018-10-23 06:52:57 +02:00
|
|
|
usec_t timeout_warn_usec;
|
|
|
|
usec_t timeout_usec;
|
|
|
|
usec_t event_birth_usec;
|
2015-06-10 15:20:02 +02:00
|
|
|
bool accept_failure;
|
2018-10-23 06:52:57 +02:00
|
|
|
int fd_stdout;
|
|
|
|
int fd_stderr;
|
|
|
|
char *result;
|
|
|
|
size_t result_size;
|
|
|
|
size_t result_len;
|
2015-05-15 11:35:15 +02:00
|
|
|
} Spawn;
|
2008-08-29 20:32:05 +02:00
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
struct udev_event *udev_event_new(struct udev_device *dev) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct udev_event *event;
|
|
|
|
|
2018-10-25 00:02:03 +02:00
|
|
|
assert(dev);
|
|
|
|
|
|
|
|
event = new(struct udev_event, 1);
|
|
|
|
if (!event)
|
2012-01-10 01:34:15 +01:00
|
|
|
return NULL;
|
2018-10-25 00:02:03 +02:00
|
|
|
|
|
|
|
*event = (struct udev_event) {
|
|
|
|
.dev = dev,
|
|
|
|
.birth_usec = now(CLOCK_MONOTONIC),
|
|
|
|
};
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
return event;
|
2008-10-16 17:16:58 +02:00
|
|
|
}
|
|
|
|
|
2018-10-25 00:04:59 +02:00
|
|
|
struct udev_event *udev_event_free(struct udev_event *event) {
|
2018-10-16 23:11:33 +02:00
|
|
|
void *p;
|
|
|
|
|
2018-10-25 00:04:59 +02:00
|
|
|
if (!event)
|
|
|
|
return NULL;
|
|
|
|
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_unref(event->rtnl);
|
2018-10-16 23:11:33 +02:00
|
|
|
while ((p = hashmap_steal_first_key(event->run_list)))
|
|
|
|
free(p);
|
2018-10-25 00:03:24 +02:00
|
|
|
hashmap_free(event->run_list);
|
2018-10-16 22:37:34 +02:00
|
|
|
hashmap_free_free_free(event->seclabel_list);
|
2012-01-10 01:34:15 +01:00
|
|
|
free(event->program_result);
|
|
|
|
free(event->name);
|
2018-10-25 00:04:59 +02:00
|
|
|
|
|
|
|
return mfree(event);
|
2008-10-16 17:16:58 +02:00
|
|
|
}
|
|
|
|
|
2017-01-26 02:06:54 +01:00
|
|
|
enum subst_type {
|
|
|
|
SUBST_UNKNOWN,
|
|
|
|
SUBST_DEVNODE,
|
|
|
|
SUBST_ATTR,
|
|
|
|
SUBST_ENV,
|
|
|
|
SUBST_KERNEL,
|
|
|
|
SUBST_KERNEL_NUMBER,
|
|
|
|
SUBST_DRIVER,
|
|
|
|
SUBST_DEVPATH,
|
|
|
|
SUBST_ID,
|
|
|
|
SUBST_MAJOR,
|
|
|
|
SUBST_MINOR,
|
|
|
|
SUBST_RESULT,
|
|
|
|
SUBST_PARENT,
|
|
|
|
SUBST_NAME,
|
|
|
|
SUBST_LINKS,
|
|
|
|
SUBST_ROOT,
|
|
|
|
SUBST_SYS,
|
|
|
|
};
|
|
|
|
|
2018-10-24 23:39:48 +02:00
|
|
|
static size_t subst_format_var(struct udev_event *event,
|
2017-01-26 02:06:54 +01:00
|
|
|
enum subst_type type, char *attr,
|
2017-01-26 20:18:10 +01:00
|
|
|
char *dest, size_t l) {
|
2018-10-24 23:39:48 +02:00
|
|
|
struct udev_device *dev = event->dev;
|
2017-01-26 20:18:10 +01:00
|
|
|
char *s = dest;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case SUBST_DEVPATH:
|
|
|
|
l = strpcpy(&s, l, udev_device_get_devpath(dev));
|
|
|
|
break;
|
|
|
|
case SUBST_KERNEL:
|
|
|
|
l = strpcpy(&s, l, udev_device_get_sysname(dev));
|
|
|
|
break;
|
|
|
|
case SUBST_KERNEL_NUMBER:
|
|
|
|
if (udev_device_get_sysnum(dev) == NULL)
|
|
|
|
break;
|
|
|
|
l = strpcpy(&s, l, udev_device_get_sysnum(dev));
|
|
|
|
break;
|
|
|
|
case SUBST_ID:
|
|
|
|
if (event->dev_parent == NULL)
|
|
|
|
break;
|
|
|
|
l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
|
|
|
|
break;
|
|
|
|
case SUBST_DRIVER: {
|
|
|
|
const char *driver;
|
|
|
|
|
|
|
|
if (event->dev_parent == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
driver = udev_device_get_driver(event->dev_parent);
|
|
|
|
if (driver == NULL)
|
|
|
|
break;
|
|
|
|
l = strpcpy(&s, l, driver);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_MAJOR: {
|
|
|
|
char num[UTIL_PATH_SIZE];
|
|
|
|
|
|
|
|
sprintf(num, "%u", major(udev_device_get_devnum(dev)));
|
|
|
|
l = strpcpy(&s, l, num);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_MINOR: {
|
|
|
|
char num[UTIL_PATH_SIZE];
|
|
|
|
|
|
|
|
sprintf(num, "%u", minor(udev_device_get_devnum(dev)));
|
|
|
|
l = strpcpy(&s, l, num);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_RESULT: {
|
|
|
|
char *rest;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (event->program_result == NULL)
|
|
|
|
break;
|
|
|
|
/* get part of the result string */
|
|
|
|
i = 0;
|
|
|
|
if (attr != NULL)
|
|
|
|
i = strtoul(attr, &rest, 10);
|
|
|
|
if (i > 0) {
|
|
|
|
char result[UTIL_PATH_SIZE];
|
|
|
|
char tmp[UTIL_PATH_SIZE];
|
|
|
|
char *cpos;
|
|
|
|
|
|
|
|
strscpy(result, sizeof(result), event->program_result);
|
|
|
|
cpos = result;
|
|
|
|
while (--i) {
|
|
|
|
while (cpos[0] != '\0' && !isspace(cpos[0]))
|
|
|
|
cpos++;
|
|
|
|
while (isspace(cpos[0]))
|
|
|
|
cpos++;
|
|
|
|
if (cpos[0] == '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i > 0) {
|
|
|
|
log_error("requested part of result string not found");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strscpy(tmp, sizeof(tmp), cpos);
|
|
|
|
/* %{2+}c copies the whole string from the second part on */
|
|
|
|
if (rest[0] != '+') {
|
|
|
|
cpos = strchr(tmp, ' ');
|
|
|
|
if (cpos)
|
|
|
|
cpos[0] = '\0';
|
|
|
|
}
|
|
|
|
l = strpcpy(&s, l, tmp);
|
|
|
|
} else {
|
|
|
|
l = strpcpy(&s, l, event->program_result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_ATTR: {
|
|
|
|
const char *value = NULL;
|
|
|
|
char vbuf[UTIL_NAME_SIZE];
|
|
|
|
size_t len;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
if (attr == NULL) {
|
|
|
|
log_error("missing file parameter for attr");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try to read the value specified by "[dmi/id]product_name" */
|
2018-08-22 12:46:37 +02:00
|
|
|
if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), 1) == 0)
|
2017-01-26 02:06:54 +01:00
|
|
|
value = vbuf;
|
|
|
|
|
|
|
|
/* try to read the attribute the device */
|
|
|
|
if (value == NULL)
|
|
|
|
value = udev_device_get_sysattr_value(event->dev, attr);
|
|
|
|
|
|
|
|
/* try to read the attribute of the parent device, other matches have selected */
|
|
|
|
if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
|
|
|
|
value = udev_device_get_sysattr_value(event->dev_parent, attr);
|
|
|
|
|
|
|
|
if (value == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* strip trailing whitespace, and replace unwanted characters */
|
|
|
|
if (value != vbuf)
|
|
|
|
strscpy(vbuf, sizeof(vbuf), value);
|
|
|
|
len = strlen(vbuf);
|
|
|
|
while (len > 0 && isspace(vbuf[--len]))
|
|
|
|
vbuf[len] = '\0';
|
|
|
|
count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
|
|
|
|
if (count > 0)
|
|
|
|
log_debug("%i character(s) replaced" , count);
|
|
|
|
l = strpcpy(&s, l, vbuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_PARENT: {
|
|
|
|
struct udev_device *dev_parent;
|
|
|
|
const char *devnode;
|
|
|
|
|
|
|
|
dev_parent = udev_device_get_parent(event->dev);
|
|
|
|
if (dev_parent == NULL)
|
|
|
|
break;
|
|
|
|
devnode = udev_device_get_devnode(dev_parent);
|
|
|
|
if (devnode != NULL)
|
2017-12-14 19:02:29 +01:00
|
|
|
l = strpcpy(&s, l, devnode + STRLEN("/dev/"));
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_DEVNODE:
|
|
|
|
if (udev_device_get_devnode(dev) != NULL)
|
|
|
|
l = strpcpy(&s, l, udev_device_get_devnode(dev));
|
|
|
|
break;
|
|
|
|
case SUBST_NAME:
|
|
|
|
if (event->name != NULL)
|
|
|
|
l = strpcpy(&s, l, event->name);
|
|
|
|
else if (udev_device_get_devnode(dev) != NULL)
|
2017-12-14 19:02:29 +01:00
|
|
|
l = strpcpy(&s, l,
|
|
|
|
udev_device_get_devnode(dev) + STRLEN("/dev/"));
|
2017-01-26 02:06:54 +01:00
|
|
|
else
|
|
|
|
l = strpcpy(&s, l, udev_device_get_sysname(dev));
|
|
|
|
break;
|
|
|
|
case SUBST_LINKS: {
|
|
|
|
struct udev_list_entry *list_entry;
|
|
|
|
|
|
|
|
list_entry = udev_device_get_devlinks_list_entry(dev);
|
|
|
|
if (list_entry == NULL)
|
|
|
|
break;
|
2017-12-14 19:02:29 +01:00
|
|
|
l = strpcpy(&s, l,
|
|
|
|
udev_list_entry_get_name(list_entry) + STRLEN("/dev/"));
|
2017-01-26 02:06:54 +01:00
|
|
|
udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
|
2017-12-14 19:02:29 +01:00
|
|
|
l = strpcpyl(&s, l, " ",
|
|
|
|
udev_list_entry_get_name(list_entry) + STRLEN("/dev/"),
|
|
|
|
NULL);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBST_ROOT:
|
|
|
|
l = strpcpy(&s, l, "/dev");
|
|
|
|
break;
|
|
|
|
case SUBST_SYS:
|
|
|
|
l = strpcpy(&s, l, "/sys");
|
|
|
|
break;
|
|
|
|
case SUBST_ENV:
|
|
|
|
if (attr == NULL) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
value = udev_device_get_property_value(event->dev, attr);
|
|
|
|
if (value == NULL)
|
|
|
|
break;
|
|
|
|
l = strpcpy(&s, l, value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
log_error("unknown substitution type=%i", type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-01-26 20:18:10 +01:00
|
|
|
return s - dest;
|
2017-01-26 02:06:54 +01:00
|
|
|
}
|
|
|
|
|
2017-01-03 20:37:59 +01:00
|
|
|
size_t udev_event_apply_format(struct udev_event *event,
|
|
|
|
const char *src, char *dest, size_t size,
|
|
|
|
bool replace_whitespace) {
|
2012-01-10 01:34:15 +01:00
|
|
|
static const struct subst_map {
|
2012-04-10 16:41:52 +02:00
|
|
|
const char *name;
|
|
|
|
const char fmt;
|
2012-01-10 01:34:15 +01:00
|
|
|
enum subst_type type;
|
|
|
|
} map[] = {
|
2012-11-20 18:07:14 +01:00
|
|
|
{ .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE },
|
|
|
|
{ .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE },
|
|
|
|
{ .name = "attr", .fmt = 's', .type = SUBST_ATTR },
|
|
|
|
{ .name = "sysfs", .fmt = 's', .type = SUBST_ATTR },
|
|
|
|
{ .name = "env", .fmt = 'E', .type = SUBST_ENV },
|
|
|
|
{ .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL },
|
|
|
|
{ .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
|
|
|
|
{ .name = "driver", .fmt = 'd', .type = SUBST_DRIVER },
|
|
|
|
{ .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH },
|
|
|
|
{ .name = "id", .fmt = 'b', .type = SUBST_ID },
|
|
|
|
{ .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
|
|
|
|
{ .name = "minor", .fmt = 'm', .type = SUBST_MINOR },
|
|
|
|
{ .name = "result", .fmt = 'c', .type = SUBST_RESULT },
|
|
|
|
{ .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
|
|
|
|
{ .name = "name", .fmt = 'D', .type = SUBST_NAME },
|
|
|
|
{ .name = "links", .fmt = 'L', .type = SUBST_LINKS },
|
|
|
|
{ .name = "root", .fmt = 'r', .type = SUBST_ROOT },
|
|
|
|
{ .name = "sys", .fmt = 'S', .type = SUBST_SYS },
|
2012-01-10 01:34:15 +01:00
|
|
|
};
|
|
|
|
const char *from;
|
|
|
|
char *s;
|
|
|
|
size_t l;
|
|
|
|
|
2018-10-24 23:39:48 +02:00
|
|
|
assert(event);
|
|
|
|
assert(event->dev);
|
|
|
|
assert(src);
|
|
|
|
assert(dest);
|
|
|
|
assert(size > 0);
|
2015-06-02 16:52:07 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
from = src;
|
|
|
|
s = dest;
|
|
|
|
l = size;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
enum subst_type type = SUBST_UNKNOWN;
|
2017-01-26 20:18:10 +01:00
|
|
|
char attrbuf[UTIL_PATH_SIZE];
|
|
|
|
char *attr = NULL;
|
|
|
|
size_t subst_len;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
while (from[0] != '\0') {
|
|
|
|
if (from[0] == '$') {
|
|
|
|
/* substitute named variable */
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned i;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (from[1] == '$') {
|
|
|
|
from++;
|
|
|
|
goto copy;
|
|
|
|
}
|
|
|
|
|
2012-04-16 03:13:22 +02:00
|
|
|
for (i = 0; i < ELEMENTSOF(map); i++) {
|
2012-04-16 20:27:44 +02:00
|
|
|
if (startswith(&from[1], map[i].name)) {
|
2012-01-10 01:34:15 +01:00
|
|
|
type = map[i].type;
|
|
|
|
from += strlen(map[i].name)+1;
|
|
|
|
goto subst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (from[0] == '%') {
|
|
|
|
/* substitute format char */
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned i;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (from[1] == '%') {
|
|
|
|
from++;
|
|
|
|
goto copy;
|
|
|
|
}
|
|
|
|
|
2012-04-16 03:13:22 +02:00
|
|
|
for (i = 0; i < ELEMENTSOF(map); i++) {
|
2012-01-10 01:34:15 +01:00
|
|
|
if (from[1] == map[i].fmt) {
|
|
|
|
type = map[i].type;
|
|
|
|
from += 2;
|
|
|
|
goto subst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-20 17:57:52 +02:00
|
|
|
copy:
|
2012-01-10 01:34:15 +01:00
|
|
|
/* copy char */
|
2017-09-16 08:38:28 +02:00
|
|
|
if (l < 2) /* need space for this char and the terminating NUL */
|
2012-01-10 01:34:15 +01:00
|
|
|
goto out;
|
|
|
|
s[0] = from[0];
|
|
|
|
from++;
|
|
|
|
s++;
|
|
|
|
l--;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto out;
|
2009-05-20 17:57:52 +02:00
|
|
|
subst:
|
2012-01-10 01:34:15 +01:00
|
|
|
/* extract possible $format{attr} */
|
|
|
|
if (from[0] == '{') {
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned i;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
from++;
|
2017-09-16 08:38:28 +02:00
|
|
|
for (i = 0; from[i] != '}'; i++)
|
2012-01-10 01:34:15 +01:00
|
|
|
if (from[i] == '\0') {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("missing closing brace for format '%s'", src);
|
2012-01-10 01:34:15 +01:00
|
|
|
goto out;
|
|
|
|
}
|
2017-09-16 08:38:28 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (i >= sizeof(attrbuf))
|
|
|
|
goto out;
|
|
|
|
memcpy(attrbuf, from, i);
|
|
|
|
attrbuf[i] = '\0';
|
|
|
|
from += i+1;
|
|
|
|
attr = attrbuf;
|
|
|
|
} else {
|
|
|
|
attr = NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-24 23:39:48 +02:00
|
|
|
subst_len = subst_format_var(event, type, attr, s, l);
|
2017-01-03 20:37:59 +01:00
|
|
|
|
2017-01-26 20:18:10 +01:00
|
|
|
/* SUBST_RESULT handles spaces itself */
|
|
|
|
if (replace_whitespace && type != SUBST_RESULT)
|
|
|
|
/* util_replace_whitespace can replace in-place,
|
|
|
|
* and does nothing if subst_len == 0
|
|
|
|
*/
|
|
|
|
subst_len = util_replace_whitespace(s, s, subst_len);
|
2017-01-03 20:37:59 +01:00
|
|
|
|
2017-01-26 20:18:10 +01:00
|
|
|
s += subst_len;
|
|
|
|
l -= subst_len;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2009-05-20 17:57:52 +02:00
|
|
|
|
|
|
|
out:
|
2017-09-16 08:38:28 +02:00
|
|
|
assert(l >= 1);
|
2012-01-10 01:34:15 +01:00
|
|
|
s[0] = '\0';
|
|
|
|
return l;
|
2008-10-18 19:19:56 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
|
|
|
Spawn *spawn = userdata;
|
|
|
|
char buf[4096], *p;
|
|
|
|
size_t size;
|
|
|
|
ssize_t l;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
assert(spawn);
|
|
|
|
assert(fd == spawn->fd_stdout || fd == spawn->fd_stderr);
|
|
|
|
assert(!spawn->result || spawn->result_len < spawn->result_size);
|
|
|
|
|
|
|
|
if (fd == spawn->fd_stdout && spawn->result) {
|
|
|
|
p = spawn->result + spawn->result_len;
|
|
|
|
size = spawn->result_size - spawn->result_len;
|
|
|
|
} else {
|
|
|
|
p = buf;
|
|
|
|
size = sizeof(buf);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
l = read(fd, p, size - 1);
|
|
|
|
if (l < 0) {
|
|
|
|
if (errno != EAGAIN)
|
|
|
|
log_error_errno(errno, "Failed to read stdout of '%s': %m", spawn->cmd);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
p[l] = '\0';
|
|
|
|
if (fd == spawn->fd_stdout && spawn->result)
|
|
|
|
spawn->result_len += l;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
/* Log output only if we watch stderr. */
|
|
|
|
if (l > 0 && spawn->fd_stderr >= 0) {
|
|
|
|
_cleanup_strv_free_ char **v = NULL;
|
|
|
|
char **q;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
v = strv_split_newlines(p);
|
|
|
|
if (!v)
|
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
STRV_FOREACH(q, v)
|
|
|
|
log_debug("'%s'(%s) '%s'", spawn->cmd,
|
|
|
|
fd == spawn->fd_stdout ? "out" : "err", *q);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
return 0;
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
|
|
|
Spawn *spawn = userdata;
|
|
|
|
char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
assert(spawn);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
kill_and_sigcont(spawn->pid, SIGKILL);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 05:23:44 +02:00
|
|
|
log_error("Spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
|
2018-10-23 06:52:57 +02:00
|
|
|
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout_usec));
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2014-09-11 18:49:04 +02:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
|
|
|
|
Spawn *spawn = userdata;
|
|
|
|
char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
assert(spawn);
|
2014-09-11 18:49:04 +02:00
|
|
|
|
2018-10-23 05:23:44 +02:00
|
|
|
log_warning("Spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
|
2018-10-23 06:52:57 +02:00
|
|
|
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout_warn_usec));
|
2015-05-15 11:35:15 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
|
|
|
|
Spawn *spawn = userdata;
|
|
|
|
|
|
|
|
assert(spawn);
|
|
|
|
|
|
|
|
switch (si->si_code) {
|
|
|
|
case CLD_EXITED:
|
2015-06-10 15:20:02 +02:00
|
|
|
if (si->si_status == 0) {
|
|
|
|
log_debug("Process '%s' succeeded.", spawn->cmd);
|
2015-05-15 11:35:15 +02:00
|
|
|
sd_event_exit(sd_event_source_get_event(s), 0);
|
|
|
|
|
|
|
|
return 1;
|
2018-10-23 05:25:17 +02:00
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 05:25:17 +02:00
|
|
|
log_full(spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
"Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
|
2015-05-15 11:35:15 +02:00
|
|
|
break;
|
|
|
|
case CLD_KILLED:
|
|
|
|
case CLD_DUMPED:
|
2015-06-10 15:20:02 +02:00
|
|
|
log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
break;
|
|
|
|
default:
|
2015-06-10 15:20:02 +02:00
|
|
|
log_error("Process '%s' failed due to unknown reason.", spawn->cmd);
|
2015-05-15 11:35:15 +02:00
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
sd_event_exit(sd_event_source_get_event(s), -EIO);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
static int spawn_wait(Spawn *spawn) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
2015-05-15 11:35:15 +02:00
|
|
|
int r, ret;
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
assert(spawn);
|
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
r = sd_event_new(&e);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
if (spawn->timeout_usec > 0) {
|
2015-05-15 11:35:15 +02:00
|
|
|
usec_t usec, age_usec;
|
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
usec = now(CLOCK_MONOTONIC);
|
2018-10-23 06:52:57 +02:00
|
|
|
age_usec = usec - spawn->event_birth_usec;
|
|
|
|
if (age_usec < spawn->timeout_usec) {
|
|
|
|
if (spawn->timeout_warn_usec > 0 &&
|
|
|
|
spawn->timeout_warn_usec < spawn->timeout_usec &&
|
|
|
|
spawn->timeout_warn_usec > age_usec) {
|
|
|
|
spawn->timeout_warn_usec -= age_usec;
|
2015-05-15 11:35:15 +02:00
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
|
2018-10-23 06:52:57 +02:00
|
|
|
usec + spawn->timeout_warn_usec, USEC_PER_SEC,
|
|
|
|
on_spawn_timeout_warning, spawn);
|
2015-05-15 11:35:15 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2015-05-15 11:35:15 +02:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
spawn->timeout_usec -= age_usec;
|
2015-05-15 11:35:15 +02:00
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
|
2018-10-23 06:52:57 +02:00
|
|
|
usec + spawn->timeout_usec, USEC_PER_SEC, on_spawn_timeout, spawn);
|
2015-05-15 11:35:15 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
2015-05-15 11:35:15 +02:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
r = sd_event_add_io(e, NULL, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_event_add_io(e, NULL, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_event_add_child(e, NULL, spawn->pid, WEXITED, on_spawn_sigchld, spawn);
|
2015-05-15 11:35:15 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_event_loop(e);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_event_get_exit_code(e, &ret);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return ret;
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int udev_event_spawn(struct udev_event *event,
|
2014-07-29 15:18:27 +02:00
|
|
|
usec_t timeout_usec,
|
2014-09-11 18:49:04 +02:00
|
|
|
usec_t timeout_warn_usec,
|
2015-06-10 15:20:02 +02:00
|
|
|
bool accept_failure,
|
2015-06-28 23:42:52 +02:00
|
|
|
const char *cmd,
|
2014-07-29 15:18:27 +02:00
|
|
|
char *result, size_t ressize) {
|
2018-09-23 08:41:39 +02:00
|
|
|
_cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
|
|
|
|
_cleanup_strv_free_ char **argv = NULL;
|
2018-10-22 22:47:21 +02:00
|
|
|
char **envp = NULL;
|
2018-10-23 06:52:57 +02:00
|
|
|
Spawn spawn;
|
2012-01-10 01:34:15 +01:00
|
|
|
pid_t pid;
|
2018-09-23 08:41:39 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-24 23:35:16 +02:00
|
|
|
assert(event);
|
|
|
|
assert(event->dev);
|
2018-10-23 06:52:57 +02:00
|
|
|
assert(result || ressize == 0);
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
/* pipes from child to parent */
|
2018-10-23 05:18:33 +02:00
|
|
|
if (result || log_get_max_level() >= LOG_INFO)
|
2018-10-23 03:18:36 +02:00
|
|
|
if (pipe2(outpipe, O_NONBLOCK|O_CLOEXEC) != 0)
|
2018-09-23 08:41:39 +02:00
|
|
|
return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-09-23 08:41:39 +02:00
|
|
|
if (log_get_max_level() >= LOG_INFO)
|
2018-10-23 03:18:36 +02:00
|
|
|
if (pipe2(errpipe, O_NONBLOCK|O_CLOEXEC) != 0)
|
2018-09-23 08:41:39 +02:00
|
|
|
return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd);
|
|
|
|
|
|
|
|
argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
|
|
|
|
if (!argv)
|
|
|
|
return log_oom();
|
|
|
|
|
2018-10-24 23:35:16 +02:00
|
|
|
if (isempty(argv[0])) {
|
|
|
|
log_error("Invalid command '%s'", cmd);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-09-23 08:41:39 +02:00
|
|
|
/* allow programs in /usr/lib/udev/ to be called without the path */
|
|
|
|
if (!path_is_absolute(argv[0])) {
|
|
|
|
char *program;
|
|
|
|
|
|
|
|
program = path_join(NULL, UDEVLIBEXECDIR, argv[0]);
|
|
|
|
if (!program)
|
|
|
|
return log_oom();
|
2015-06-28 23:42:52 +02:00
|
|
|
|
2018-09-23 08:41:39 +02:00
|
|
|
free_and_replace(argv[0], program);
|
|
|
|
}
|
|
|
|
|
2018-10-22 22:47:21 +02:00
|
|
|
r = device_get_properties_strv(event->dev->device, &envp);
|
|
|
|
if (r < 0)
|
2018-10-24 23:33:26 +02:00
|
|
|
return log_device_error_errno(event->dev->device, r, "Failed to get device properties");
|
2018-10-22 22:47:21 +02:00
|
|
|
|
2018-10-22 11:21:42 +02:00
|
|
|
log_debug("Starting '%s'", cmd);
|
|
|
|
|
2018-10-22 11:27:57 +02:00
|
|
|
r = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
|
2018-09-23 08:41:39 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to fork() to execute command '%s': %m", cmd);
|
|
|
|
if (r == 0) {
|
2018-10-22 11:29:05 +02:00
|
|
|
if (rearrange_stdio(-1, outpipe[WRITE_END], errpipe[WRITE_END]) < 0)
|
|
|
|
_exit(EXIT_FAILURE);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-22 11:29:05 +02:00
|
|
|
(void) close_all_fds(NULL, 0);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-22 22:47:21 +02:00
|
|
|
execve(argv[0], argv, envp);
|
2018-10-22 11:29:05 +02:00
|
|
|
_exit(EXIT_FAILURE);
|
2015-06-28 23:42:52 +02:00
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
/* parent closed child's ends of pipes */
|
|
|
|
outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
|
|
|
|
errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
spawn = (Spawn) {
|
|
|
|
.cmd = cmd,
|
|
|
|
.pid = pid,
|
|
|
|
.accept_failure = accept_failure,
|
|
|
|
.timeout_warn_usec = timeout_warn_usec,
|
|
|
|
.timeout_usec = timeout_usec,
|
|
|
|
.event_birth_usec = event->birth_usec,
|
|
|
|
.fd_stdout = outpipe[READ_END],
|
|
|
|
.fd_stderr = errpipe[READ_END],
|
|
|
|
.result = result,
|
|
|
|
.result_size = ressize,
|
|
|
|
};
|
|
|
|
r = spawn_wait(&spawn);
|
2018-09-23 08:41:39 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to wait spawned command '%s': %m", cmd);
|
2011-04-20 01:53:03 +02:00
|
|
|
|
2018-10-23 06:52:57 +02:00
|
|
|
if (result)
|
|
|
|
result[spawn.result_len] = '\0';
|
|
|
|
|
2018-09-23 08:41:39 +02:00
|
|
|
return r;
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
static int rename_netif(struct udev_event *event) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct udev_device *dev = event->dev;
|
2013-10-29 20:03:26 +01:00
|
|
|
char name[IFNAMSIZ];
|
|
|
|
const char *oldname;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
oldname = udev_device_get_sysname(dev);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2013-10-29 20:03:26 +01:00
|
|
|
strscpy(name, IFNAMSIZ, event->name);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2014-09-09 11:15:37 +02:00
|
|
|
r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name);
|
2013-10-29 20:03:26 +01:00
|
|
|
|
2014-11-28 14:26:31 +01:00
|
|
|
log_debug("renamed network interface '%s' to '%s'", oldname, name);
|
2013-10-29 20:03:26 +01:00
|
|
|
|
2014-09-09 11:15:37 +02:00
|
|
|
return 0;
|
2008-08-29 20:32:05 +02:00
|
|
|
}
|
|
|
|
|
2014-07-29 15:18:27 +02:00
|
|
|
void udev_event_execute_rules(struct udev_event *event,
|
2014-11-13 20:35:06 +01:00
|
|
|
usec_t timeout_usec, usec_t timeout_warn_usec,
|
2018-10-16 23:47:16 +02:00
|
|
|
Hashmap *properties_list,
|
2015-06-02 17:07:21 +02:00
|
|
|
struct udev_rules *rules) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct udev_device *dev = event->dev;
|
|
|
|
|
|
|
|
if (udev_device_get_subsystem(dev) == NULL)
|
2014-05-14 00:34:49 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2013-02-13 18:13:22 +01:00
|
|
|
if (streq(udev_device_get_action(dev), "remove")) {
|
2015-04-23 15:19:13 +02:00
|
|
|
udev_device_read_db(dev);
|
|
|
|
udev_device_tag_index(dev, NULL, false);
|
|
|
|
udev_device_delete_db(dev);
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (major(udev_device_get_devnum(dev)) != 0)
|
2018-09-14 21:13:29 +02:00
|
|
|
udev_watch_end(dev->device);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2014-11-13 20:35:06 +01:00
|
|
|
udev_rules_apply_to_event(rules, event,
|
|
|
|
timeout_usec, timeout_warn_usec,
|
2015-06-02 17:07:21 +02:00
|
|
|
properties_list);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (major(udev_device_get_devnum(dev)) != 0)
|
2018-08-29 08:54:56 +02:00
|
|
|
udev_node_remove(dev->device);
|
2012-01-10 01:34:15 +01:00
|
|
|
} else {
|
2015-03-06 15:22:30 +01:00
|
|
|
event->dev_db = udev_device_clone_with_db(dev);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (event->dev_db != NULL) {
|
|
|
|
/* disable watch during event processing */
|
|
|
|
if (major(udev_device_get_devnum(dev)) != 0)
|
2018-09-14 21:13:29 +02:00
|
|
|
udev_watch_end(event->dev_db->device);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-12-07 06:10:15 +01:00
|
|
|
if (major(udev_device_get_devnum(dev)) == 0 &&
|
|
|
|
streq(udev_device_get_action(dev), "move"))
|
|
|
|
udev_device_copy_properties(dev, event->dev_db);
|
|
|
|
}
|
2014-09-09 12:23:19 +02:00
|
|
|
|
2014-11-13 20:35:06 +01:00
|
|
|
udev_rules_apply_to_event(rules, event,
|
|
|
|
timeout_usec, timeout_warn_usec,
|
2015-06-02 17:07:21 +02:00
|
|
|
properties_list);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* rename a new network interface, if needed */
|
2013-02-13 18:13:22 +01:00
|
|
|
if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
|
|
|
|
event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) {
|
2014-05-14 00:34:49 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2014-05-14 00:34:49 +02:00
|
|
|
r = rename_netif(event);
|
2015-01-26 13:33:00 +01:00
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "could not rename interface '%d' from '%s' to '%s': %m", udev_device_get_ifindex(dev),
|
|
|
|
udev_device_get_sysname(dev), event->name);
|
|
|
|
else {
|
|
|
|
r = udev_device_rename(dev, event->name);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m",
|
|
|
|
udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name);
|
2015-03-05 17:44:12 +01:00
|
|
|
else
|
2013-12-24 16:39:37 +01:00
|
|
|
log_debug("changed devpath to '%s'", udev_device_get_devpath(dev));
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-23 04:44:35 +01:00
|
|
|
if (major(udev_device_get_devnum(dev)) > 0) {
|
2013-03-21 23:11:51 +01:00
|
|
|
bool apply;
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
/* remove/update possible left-over symlinks from old database entry */
|
|
|
|
if (event->dev_db != NULL)
|
2018-08-29 08:54:56 +02:00
|
|
|
udev_node_update_old_links(dev->device, event->dev_db->device);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2012-12-31 04:48:44 +01:00
|
|
|
if (!event->owner_set)
|
|
|
|
event->uid = udev_device_get_devnode_uid(dev);
|
|
|
|
|
|
|
|
if (!event->group_set)
|
|
|
|
event->gid = udev_device_get_devnode_gid(dev);
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (!event->mode_set) {
|
|
|
|
if (udev_device_get_devnode_mode(dev) > 0) {
|
|
|
|
/* kernel supplied value */
|
|
|
|
event->mode = udev_device_get_devnode_mode(dev);
|
|
|
|
} else if (event->gid > 0) {
|
|
|
|
/* default 0660 if a group is assigned */
|
|
|
|
event->mode = 0660;
|
|
|
|
} else {
|
|
|
|
/* default 0600 */
|
|
|
|
event->mode = 0600;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-21 23:11:51 +01:00
|
|
|
apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
|
2018-10-16 22:37:34 +02:00
|
|
|
udev_node_add(dev->device, apply, event->mode, event->uid, event->gid, event->seclabel_list);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* preserve old, or get new initialization timestamp */
|
2015-03-13 18:43:00 +01:00
|
|
|
udev_device_ensure_usec_initialized(event->dev, event->dev_db);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* (re)write database file */
|
|
|
|
udev_device_tag_index(dev, event->dev_db, true);
|
2015-04-17 15:46:37 +02:00
|
|
|
udev_device_update_db(dev);
|
2012-01-10 01:34:15 +01:00
|
|
|
udev_device_set_is_initialized(dev);
|
|
|
|
|
2015-04-17 15:46:37 +02:00
|
|
|
event->dev_db = udev_device_unref(event->dev_db);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2008-08-29 20:32:05 +02:00
|
|
|
}
|
2008-10-18 15:50:16 +02:00
|
|
|
|
2015-06-02 17:07:21 +02:00
|
|
|
void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
|
2018-10-16 23:11:33 +02:00
|
|
|
const char *cmd;
|
|
|
|
void *val;
|
|
|
|
Iterator i;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-16 23:11:33 +02:00
|
|
|
HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
|
|
|
|
enum udev_builtin_cmd builtin_cmd = PTR_TO_INT(val);
|
2015-06-28 23:42:52 +02:00
|
|
|
char command[UTIL_PATH_SIZE];
|
2012-04-09 16:37:54 +02:00
|
|
|
|
2017-01-03 20:37:59 +01:00
|
|
|
udev_event_apply_format(event, cmd, command, sizeof(command), false);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-13 20:09:13 +02:00
|
|
|
if (builtin_cmd >= 0 && builtin_cmd < _UDEV_BUILTIN_MAX)
|
2018-10-13 18:38:54 +02:00
|
|
|
udev_builtin_run(event->dev->device, builtin_cmd, command, false);
|
2015-06-28 23:42:52 +02:00
|
|
|
else {
|
2012-01-10 01:34:15 +01:00
|
|
|
if (event->exec_delay > 0) {
|
2015-06-28 23:42:52 +02:00
|
|
|
log_debug("delay execution of '%s'", command);
|
2012-01-10 01:34:15 +01:00
|
|
|
sleep(event->exec_delay);
|
|
|
|
}
|
|
|
|
|
2015-06-28 23:42:52 +02:00
|
|
|
udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
2008-10-18 15:50:16 +02:00
|
|
|
}
|