2017-11-18 17:14:42 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0+ */
|
2008-08-29 20:32:05 +02:00
|
|
|
/*
|
2018-06-12 17:15:23 +02:00
|
|
|
* Copyright © 2003-2013 Kay Sievers <kay@vrfy.org>
|
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-02-12 14:06:32 +01:00
|
|
|
#include <poll.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2011-04-20 01:53:03 +02:00
|
|
|
#include <sys/epoll.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sys/prctl.h>
|
2011-04-20 01:53:03 +02:00
|
|
|
#include <sys/signalfd.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
2008-08-29 20:32:05 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-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"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "netlink-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"
|
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;
|
|
|
|
usec_t timeout_warn;
|
|
|
|
usec_t timeout;
|
2015-06-10 15:20:02 +02:00
|
|
|
bool accept_failure;
|
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 *udev = udev_device_get_udev(dev);
|
|
|
|
struct udev_event *event;
|
|
|
|
|
2014-02-13 14:45:51 +01:00
|
|
|
event = new0(struct udev_event, 1);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (event == NULL)
|
|
|
|
return NULL;
|
|
|
|
event->dev = dev;
|
|
|
|
event->udev = udev;
|
|
|
|
udev_list_init(udev, &event->run_list, false);
|
2013-10-08 01:59:10 +02:00
|
|
|
udev_list_init(udev, &event->seclabel_list, false);
|
2017-06-27 02:17:39 +02:00
|
|
|
event->birth_usec = now(CLOCK_MONOTONIC);
|
2012-01-10 01:34:15 +01:00
|
|
|
return event;
|
2008-10-16 17:16:58 +02:00
|
|
|
}
|
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
void udev_event_unref(struct udev_event *event) {
|
2012-01-10 01:34:15 +01:00
|
|
|
if (event == NULL)
|
|
|
|
return;
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_unref(event->rtnl);
|
2012-01-10 01:34:15 +01:00
|
|
|
udev_list_cleanup(&event->run_list);
|
2013-10-08 01:59:10 +02:00
|
|
|
udev_list_cleanup(&event->seclabel_list);
|
2012-01-10 01:34:15 +01:00
|
|
|
free(event->program_result);
|
|
|
|
free(event->name);
|
|
|
|
free(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,
|
|
|
|
};
|
|
|
|
|
|
|
|
static size_t subst_format_var(struct udev_event *event, struct udev_device *dev,
|
|
|
|
enum subst_type type, char *attr,
|
2017-01-26 20:18:10 +01:00
|
|
|
char *dest, size_t l) {
|
|
|
|
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" */
|
|
|
|
if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
|
|
|
|
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
|
|
|
struct udev_device *dev = event->dev;
|
|
|
|
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;
|
|
|
|
|
2015-06-02 16:52:07 +02:00
|
|
|
assert(dev);
|
|
|
|
|
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 */
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
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 */
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
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] == '{') {
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-01-26 20:18:10 +01:00
|
|
|
subst_len = subst_format_var(event, dev, 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
|
|
|
}
|
|
|
|
|
2011-04-20 01:53:03 +02:00
|
|
|
static int spawn_exec(struct udev_event *event,
|
2015-06-02 17:07:21 +02:00
|
|
|
const char *cmd, char *const argv[], char **envp,
|
2014-07-29 15:47:41 +02:00
|
|
|
int fd_stdout, int fd_stderr) {
|
2014-09-17 21:53:20 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
2015-06-23 17:03:19 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* discard child output or connect to pipe */
|
|
|
|
fd = open("/dev/null", O_RDWR);
|
|
|
|
if (fd >= 0) {
|
2015-06-23 17:03:19 +02:00
|
|
|
r = dup2(fd, STDIN_FILENO);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "redirecting stdin failed: %m");
|
|
|
|
|
|
|
|
if (fd_stdout < 0) {
|
|
|
|
r = dup2(fd, STDOUT_FILENO);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "redirecting stdout failed: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd_stderr < 0) {
|
|
|
|
r = dup2(fd, STDERR_FILENO);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "redirecting stderr failed: %m");
|
|
|
|
}
|
2014-09-17 21:53:20 +02:00
|
|
|
} else
|
2015-06-23 17:03:19 +02:00
|
|
|
log_warning_errno(errno, "open /dev/null failed: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* connect pipes to std{out,err} */
|
|
|
|
if (fd_stdout >= 0) {
|
2015-06-23 17:03:19 +02:00
|
|
|
r = dup2(fd_stdout, STDOUT_FILENO);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "redirecting stdout failed: %m");
|
|
|
|
|
|
|
|
fd_stdout = safe_close(fd_stdout);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2015-06-23 17:03:19 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (fd_stderr >= 0) {
|
2015-06-23 17:03:19 +02:00
|
|
|
r = dup2(fd_stderr, STDERR_FILENO);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(errno, "redirecting stdout failed: %m");
|
|
|
|
|
|
|
|
fd_stderr = safe_close(fd_stderr);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* terminate child in case parent goes away */
|
|
|
|
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
|
|
|
2015-06-02 17:07:21 +02:00
|
|
|
/* restore sigmask before exec */
|
|
|
|
(void) reset_signal_mask();
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
execve(argv[0], argv, envp);
|
|
|
|
|
|
|
|
/* exec failed */
|
2015-11-05 13:44:20 +01:00
|
|
|
return log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd);
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
2011-12-21 20:47:51 +01:00
|
|
|
static void spawn_read(struct udev_event *event,
|
2014-07-29 15:18:27 +02:00
|
|
|
usec_t timeout_usec,
|
|
|
|
const char *cmd,
|
|
|
|
int fd_stdout, int fd_stderr,
|
2014-07-29 15:47:41 +02:00
|
|
|
char *result, size_t ressize) {
|
2014-09-17 21:43:57 +02:00
|
|
|
_cleanup_close_ int fd_ep = -1;
|
|
|
|
struct epoll_event ep_outpipe = {
|
|
|
|
.events = EPOLLIN,
|
|
|
|
.data.ptr = &fd_stdout,
|
|
|
|
};
|
|
|
|
struct epoll_event ep_errpipe = {
|
|
|
|
.events = EPOLLIN,
|
|
|
|
.data.ptr = &fd_stderr,
|
|
|
|
};
|
2012-01-10 01:34:15 +01:00
|
|
|
size_t respos = 0;
|
2014-09-17 21:43:57 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* read from child if requested */
|
|
|
|
if (fd_stdout < 0 && fd_stderr < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
fd_ep = epoll_create1(EPOLL_CLOEXEC);
|
|
|
|
if (fd_ep < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "error creating epoll fd: %m");
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fd_stdout >= 0) {
|
2014-09-17 21:43:57 +02:00
|
|
|
r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe);
|
|
|
|
if (r < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "fail to add stdout fd to epoll: %m");
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fd_stderr >= 0) {
|
2014-09-17 21:43:57 +02:00
|
|
|
r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe);
|
|
|
|
if (r < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "fail to add stderr fd to epoll: %m");
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read child output */
|
|
|
|
while (fd_stdout >= 0 || fd_stderr >= 0) {
|
|
|
|
int timeout;
|
|
|
|
int fdcount;
|
|
|
|
struct epoll_event ev[4];
|
|
|
|
int i;
|
|
|
|
|
2014-07-29 15:18:27 +02:00
|
|
|
if (timeout_usec > 0) {
|
2012-11-11 20:45:05 +01:00
|
|
|
usec_t age_usec;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
|
2014-07-29 15:18:27 +02:00
|
|
|
if (age_usec >= timeout_usec) {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("timeout '%s'", cmd);
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2014-07-29 15:18:27 +02:00
|
|
|
timeout = ((timeout_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
|
2012-01-10 01:34:15 +01:00
|
|
|
} else {
|
|
|
|
timeout = -1;
|
|
|
|
}
|
|
|
|
|
2012-04-16 03:13:22 +02:00
|
|
|
fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (fdcount < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "failed to poll: %m");
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
|
|
|
} else if (fdcount == 0) {
|
2013-12-24 16:39:37 +01:00
|
|
|
log_error("timeout '%s'", cmd);
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < fdcount; i++) {
|
|
|
|
int *fd = (int *)ev[i].data.ptr;
|
|
|
|
|
2014-09-17 21:44:56 +02:00
|
|
|
if (*fd < 0)
|
|
|
|
continue;
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (ev[i].events & EPOLLIN) {
|
|
|
|
ssize_t count;
|
|
|
|
char buf[4096];
|
|
|
|
|
|
|
|
count = read(*fd, buf, sizeof(buf)-1);
|
|
|
|
if (count <= 0)
|
|
|
|
continue;
|
|
|
|
buf[count] = '\0';
|
|
|
|
|
|
|
|
/* store stdout result */
|
|
|
|
if (result != NULL && *fd == fd_stdout) {
|
|
|
|
if (respos + count < ressize) {
|
|
|
|
memcpy(&result[respos], buf, count);
|
|
|
|
respos += count;
|
|
|
|
} else {
|
2015-01-21 04:22:15 +01:00
|
|
|
log_error("'%s' ressize %zu too short", cmd, ressize);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* log debug output only if we watch stderr */
|
|
|
|
if (fd_stderr >= 0) {
|
|
|
|
char *pos;
|
|
|
|
char *line;
|
|
|
|
|
|
|
|
pos = buf;
|
|
|
|
while ((line = strsep(&pos, "\n"))) {
|
|
|
|
if (pos != NULL || line[0] != '\0')
|
2013-12-24 16:39:37 +01:00
|
|
|
log_debug("'%s'(%s) '%s'", cmd, *fd == fd_stdout ? "out" : "err" , line);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (ev[i].events & EPOLLHUP) {
|
2014-09-17 21:43:57 +02:00
|
|
|
r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL);
|
|
|
|
if (r < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "failed to remove fd from epoll: %m");
|
2014-09-17 21:43:57 +02:00
|
|
|
return;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
*fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* return the child's stdout string */
|
2012-04-08 16:06:20 +02:00
|
|
|
if (result != NULL)
|
2012-01-10 01:34:15 +01:00
|
|
|
result[respos] = '\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
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
|
|
|
|
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
|
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
|
|
|
|
2015-05-15 11:35:15 +02:00
|
|
|
log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
|
|
|
|
format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
|
|
|
|
|
|
|
|
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;
|
2015-06-10 15:20:02 +02:00
|
|
|
} else if (spawn->accept_failure)
|
|
|
|
log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
|
|
|
|
else
|
|
|
|
log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int spawn_wait(struct udev_event *event,
|
|
|
|
usec_t timeout_usec,
|
|
|
|
usec_t timeout_warn_usec,
|
2015-06-10 15:20:02 +02:00
|
|
|
const char *cmd, pid_t pid,
|
|
|
|
bool accept_failure) {
|
2015-05-15 11:35:15 +02:00
|
|
|
Spawn spawn = {
|
|
|
|
.cmd = cmd,
|
|
|
|
.pid = pid,
|
2015-06-10 15:20:02 +02:00
|
|
|
.accept_failure = accept_failure,
|
2015-05-15 11:35:15 +02:00
|
|
|
};
|
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;
|
|
|
|
|
|
|
|
r = sd_event_new(&e);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (timeout_usec > 0) {
|
|
|
|
usec_t usec, age_usec;
|
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
usec = now(CLOCK_MONOTONIC);
|
2015-05-15 11:35:15 +02:00
|
|
|
age_usec = usec - event->birth_usec;
|
|
|
|
if (age_usec < timeout_usec) {
|
|
|
|
if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
|
|
|
|
spawn.timeout_warn = timeout_warn_usec - age_usec;
|
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
|
2015-06-08 20:53:16 +02:00
|
|
|
usec + spawn.timeout_warn, 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
|
|
|
|
|
|
|
spawn.timeout = timeout_usec - age_usec;
|
|
|
|
|
2017-06-27 02:17:39 +02:00
|
|
|
r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC,
|
2015-05-15 11:35:15 +02:00
|
|
|
usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
2015-05-15 11:35:15 +02:00
|
|
|
|
|
|
|
r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-07-29 15:47:41 +02:00
|
|
|
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int i = 0;
|
|
|
|
char *pos;
|
|
|
|
|
|
|
|
if (strchr(cmd, ' ') == NULL) {
|
|
|
|
argv[i++] = cmd;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = cmd;
|
|
|
|
while (pos != NULL && pos[0] != '\0') {
|
2017-09-28 08:53:46 +02:00
|
|
|
if (IN_SET(pos[0], '\'', '"')) {
|
|
|
|
/* do not separate quotes or double quotes */
|
|
|
|
char delim[2] = { pos[0], '\0' };
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
pos++;
|
2017-09-28 08:53:46 +02:00
|
|
|
argv[i] = strsep(&pos, delim);
|
2012-01-10 01:34:15 +01:00
|
|
|
if (pos != NULL)
|
|
|
|
while (pos[0] == ' ')
|
|
|
|
pos++;
|
|
|
|
} else {
|
|
|
|
argv[i] = strsep(&pos, " ");
|
|
|
|
if (pos != NULL)
|
|
|
|
while (pos[0] == ' ')
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2011-12-23 02:02:44 +01:00
|
|
|
out:
|
2012-01-10 01:34:15 +01:00
|
|
|
argv[i] = NULL;
|
|
|
|
if (argc)
|
|
|
|
*argc = i;
|
|
|
|
return 0;
|
2011-12-23 02:02:44 +01:00
|
|
|
}
|
|
|
|
|
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) {
|
2012-01-10 01:34:15 +01:00
|
|
|
int outpipe[2] = {-1, -1};
|
|
|
|
int errpipe[2] = {-1, -1};
|
|
|
|
pid_t pid;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
/* pipes from child to parent */
|
2014-11-13 13:11:04 +01:00
|
|
|
if (result != NULL || log_get_max_level() >= LOG_INFO) {
|
2012-01-10 01:34:15 +01:00
|
|
|
if (pipe2(outpipe, O_NONBLOCK) != 0) {
|
2015-09-08 19:30:45 +02:00
|
|
|
err = log_error_errno(errno, "pipe failed: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2014-11-13 13:11:04 +01:00
|
|
|
if (log_get_max_level() >= LOG_INFO) {
|
2012-01-10 01:34:15 +01:00
|
|
|
if (pipe2(errpipe, O_NONBLOCK) != 0) {
|
2015-09-08 19:30:45 +02:00
|
|
|
err = log_error_errno(errno, "pipe failed: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 21:49:19 +01:00
|
|
|
err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid);
|
|
|
|
if (err < 0)
|
2017-12-22 13:08:14 +01:00
|
|
|
goto out;
|
|
|
|
if (err == 0) {
|
2015-06-28 23:42:52 +02:00
|
|
|
char arg[UTIL_PATH_SIZE];
|
|
|
|
char *argv[128];
|
|
|
|
char program[UTIL_PATH_SIZE];
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
/* child closes parent's ends of pipes */
|
2015-09-08 18:58:28 +02:00
|
|
|
outpipe[READ_END] = safe_close(outpipe[READ_END]);
|
|
|
|
errpipe[READ_END] = safe_close(errpipe[READ_END]);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-06-28 23:42:52 +02:00
|
|
|
strscpy(arg, sizeof(arg), cmd);
|
|
|
|
udev_build_argv(event->udev, arg, NULL, argv);
|
|
|
|
|
|
|
|
/* allow programs in /usr/lib/udev/ to be called without the path */
|
|
|
|
if (argv[0][0] != '/') {
|
|
|
|
strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
|
|
|
|
argv[0] = program;
|
|
|
|
}
|
|
|
|
|
2013-12-24 16:39:37 +01:00
|
|
|
log_debug("starting '%s'", cmd);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-06-28 23:42:52 +02:00
|
|
|
spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev),
|
2012-04-13 15:08:55 +02:00
|
|
|
outpipe[WRITE_END], errpipe[WRITE_END]);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2015-06-28 23:42:52 +02:00
|
|
|
_exit(2);
|
|
|
|
}
|
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
|
|
|
|
2017-12-22 13:08:14 +01:00
|
|
|
spawn_read(event,
|
|
|
|
timeout_usec,
|
|
|
|
cmd,
|
|
|
|
outpipe[READ_END], errpipe[READ_END],
|
|
|
|
result, ressize);
|
|
|
|
|
|
|
|
err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
|
2011-04-20 01:53:03 +02:00
|
|
|
|
|
|
|
out:
|
2012-01-10 01:34:15 +01:00
|
|
|
if (outpipe[READ_END] >= 0)
|
|
|
|
close(outpipe[READ_END]);
|
|
|
|
if (outpipe[WRITE_END] >= 0)
|
|
|
|
close(outpipe[WRITE_END]);
|
|
|
|
if (errpipe[READ_END] >= 0)
|
|
|
|
close(errpipe[READ_END]);
|
|
|
|
if (errpipe[WRITE_END] >= 0)
|
|
|
|
close(errpipe[WRITE_END]);
|
|
|
|
return err;
|
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,
|
|
|
|
struct udev_list *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)
|
|
|
|
udev_watch_end(event->udev, dev);
|
|
|
|
|
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)
|
2012-01-29 05:37:39 +01:00
|
|
|
udev_node_remove(dev);
|
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)
|
|
|
|
udev_watch_end(event->udev, event->dev_db);
|
|
|
|
|
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)
|
|
|
|
udev_node_update_old_links(dev, event->dev_db);
|
|
|
|
|
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;
|
2013-10-08 01:59:10 +02:00
|
|
|
udev_node_add(dev, 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) {
|
2012-01-10 01:34:15 +01:00
|
|
|
struct udev_list_entry *list_entry;
|
|
|
|
|
|
|
|
udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
|
2015-06-28 23:42:52 +02:00
|
|
|
char command[UTIL_PATH_SIZE];
|
2012-01-10 01:34:15 +01:00
|
|
|
const char *cmd = udev_list_entry_get_name(list_entry);
|
2012-04-09 16:37:54 +02:00
|
|
|
enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
|
|
|
|
|
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
|
|
|
|
2015-06-28 23:42:52 +02:00
|
|
|
if (builtin_cmd < UDEV_BUILTIN_MAX)
|
2012-04-09 16:37:54 +02:00
|
|
|
udev_builtin_run(event->dev, 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
|
|
|
}
|