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>
|
2019-05-16 21:09:46 +02:00
|
|
|
#include <sys/wait.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#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"
|
2019-04-25 01:21:11 +02:00
|
|
|
#include "fs-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "libudev-util.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"
|
2018-11-26 16:06:26 +01:00
|
|
|
#include "rlimit-util.h"
|
2015-05-29 20:14:11 +02:00
|
|
|
#include "signal-util.h"
|
2018-10-25 06:59:03 +02:00
|
|
|
#include "stdio-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "strv.h"
|
|
|
|
#include "strxcpyx.h"
|
2018-08-29 09:12:22 +02:00
|
|
|
#include "udev-builtin.h"
|
2019-04-25 01:21:11 +02:00
|
|
|
#include "udev-event.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "udev-node.h"
|
2019-03-09 02:08:03 +01:00
|
|
|
#include "udev-util.h"
|
2018-09-14 21:13:29 +02:00
|
|
|
#include "udev-watch.h"
|
2019-04-25 01:21:11 +02:00
|
|
|
#include "user-util.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
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl) {
|
|
|
|
UdevEvent *event;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-25 00:02:03 +02:00
|
|
|
assert(dev);
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
event = new(UdevEvent, 1);
|
2018-10-25 00:02:03 +02:00
|
|
|
if (!event)
|
2012-01-10 01:34:15 +01:00
|
|
|
return NULL;
|
2018-10-25 00:02:03 +02:00
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
*event = (UdevEvent) {
|
2018-10-29 08:27:13 +01:00
|
|
|
.dev = sd_device_ref(dev),
|
2018-10-25 00:02:03 +02:00
|
|
|
.birth_usec = now(CLOCK_MONOTONIC),
|
2018-10-25 07:56:12 +02:00
|
|
|
.exec_delay_usec = exec_delay_usec,
|
2018-11-01 14:19:31 +01:00
|
|
|
.rtnl = sd_netlink_ref(rtnl),
|
2019-04-25 01:21:11 +02:00
|
|
|
.uid = UID_INVALID,
|
|
|
|
.gid = GID_INVALID,
|
|
|
|
.mode = MODE_INVALID,
|
2018-10-25 00:02:03 +02:00
|
|
|
};
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
return event;
|
2008-10-16 17:16:58 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
UdevEvent *udev_event_free(UdevEvent *event) {
|
2018-10-25 00:04:59 +02:00
|
|
|
if (!event)
|
|
|
|
return NULL;
|
|
|
|
|
2018-10-29 08:27:13 +01:00
|
|
|
sd_device_unref(event->dev);
|
2018-10-29 07:01:39 +01:00
|
|
|
sd_device_unref(event->dev_db_clone);
|
2015-06-12 16:31:33 +02:00
|
|
|
sd_netlink_unref(event->rtnl);
|
2019-03-04 20:01:34 +01:00
|
|
|
ordered_hashmap_free_free_key(event->run_list);
|
|
|
|
ordered_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
|
|
|
}
|
|
|
|
|
2019-04-30 19:13:58 +02:00
|
|
|
typedef enum {
|
|
|
|
FORMAT_SUBST_DEVNODE,
|
|
|
|
FORMAT_SUBST_ATTR,
|
|
|
|
FORMAT_SUBST_ENV,
|
|
|
|
FORMAT_SUBST_KERNEL,
|
|
|
|
FORMAT_SUBST_KERNEL_NUMBER,
|
|
|
|
FORMAT_SUBST_DRIVER,
|
|
|
|
FORMAT_SUBST_DEVPATH,
|
|
|
|
FORMAT_SUBST_ID,
|
|
|
|
FORMAT_SUBST_MAJOR,
|
|
|
|
FORMAT_SUBST_MINOR,
|
|
|
|
FORMAT_SUBST_RESULT,
|
|
|
|
FORMAT_SUBST_PARENT,
|
|
|
|
FORMAT_SUBST_NAME,
|
|
|
|
FORMAT_SUBST_LINKS,
|
|
|
|
FORMAT_SUBST_ROOT,
|
|
|
|
FORMAT_SUBST_SYS,
|
|
|
|
_FORMAT_SUBST_TYPE_MAX,
|
|
|
|
_FORMAT_SUBST_TYPE_INVALID = -1
|
|
|
|
} FormatSubstitutionType;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
2018-10-25 06:07:09 +02:00
|
|
|
struct subst_map_entry {
|
|
|
|
const char *name;
|
|
|
|
const char fmt;
|
2019-04-30 19:13:58 +02:00
|
|
|
FormatSubstitutionType type;
|
2018-10-25 06:07:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct subst_map_entry map[] = {
|
2019-04-30 19:13:58 +02:00
|
|
|
{ .name = "devnode", .fmt = 'N', .type = FORMAT_SUBST_DEVNODE },
|
2019-04-30 19:23:44 +02:00
|
|
|
{ .name = "tempnode", .fmt = 'N', .type = FORMAT_SUBST_DEVNODE }, /* deprecated */
|
2019-04-30 19:13:58 +02:00
|
|
|
{ .name = "attr", .fmt = 's', .type = FORMAT_SUBST_ATTR },
|
2019-04-30 19:23:44 +02:00
|
|
|
{ .name = "sysfs", .fmt = 's', .type = FORMAT_SUBST_ATTR }, /* deprecated */
|
2019-04-30 19:13:58 +02:00
|
|
|
{ .name = "env", .fmt = 'E', .type = FORMAT_SUBST_ENV },
|
|
|
|
{ .name = "kernel", .fmt = 'k', .type = FORMAT_SUBST_KERNEL },
|
|
|
|
{ .name = "number", .fmt = 'n', .type = FORMAT_SUBST_KERNEL_NUMBER },
|
|
|
|
{ .name = "driver", .fmt = 'd', .type = FORMAT_SUBST_DRIVER },
|
|
|
|
{ .name = "devpath", .fmt = 'p', .type = FORMAT_SUBST_DEVPATH },
|
|
|
|
{ .name = "id", .fmt = 'b', .type = FORMAT_SUBST_ID },
|
|
|
|
{ .name = "major", .fmt = 'M', .type = FORMAT_SUBST_MAJOR },
|
|
|
|
{ .name = "minor", .fmt = 'm', .type = FORMAT_SUBST_MINOR },
|
|
|
|
{ .name = "result", .fmt = 'c', .type = FORMAT_SUBST_RESULT },
|
|
|
|
{ .name = "parent", .fmt = 'P', .type = FORMAT_SUBST_PARENT },
|
|
|
|
{ .name = "name", .fmt = 'D', .type = FORMAT_SUBST_NAME },
|
|
|
|
{ .name = "links", .fmt = 'L', .type = FORMAT_SUBST_LINKS },
|
|
|
|
{ .name = "root", .fmt = 'r', .type = FORMAT_SUBST_ROOT },
|
|
|
|
{ .name = "sys", .fmt = 'S', .type = FORMAT_SUBST_SYS },
|
2018-10-25 06:07:09 +02:00
|
|
|
};
|
|
|
|
|
2019-04-30 19:18:33 +02:00
|
|
|
static ssize_t udev_event_subst_format(
|
|
|
|
UdevEvent *event,
|
|
|
|
FormatSubstitutionType type,
|
|
|
|
const char *attr,
|
|
|
|
char *dest,
|
|
|
|
size_t l) {
|
2018-10-29 08:27:13 +01:00
|
|
|
sd_device *parent, *dev = event->dev;
|
2018-10-25 06:59:03 +02:00
|
|
|
const char *val = NULL;
|
2017-01-26 20:18:10 +01:00
|
|
|
char *s = dest;
|
2018-10-25 06:59:03 +02:00
|
|
|
dev_t devnum;
|
|
|
|
int r;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
2019-04-30 19:18:33 +02:00
|
|
|
switch (type) {
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_DEVPATH:
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_devpath(dev, &val);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_KERNEL:
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_sysname(dev, &val);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_KERNEL_NUMBER:
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_sysnum(dev, &val);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENOENT)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_ID:
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!event->dev_parent)
|
2019-02-16 23:56:28 +01:00
|
|
|
goto null_terminate;
|
2018-10-29 07:16:00 +01:00
|
|
|
r = sd_device_get_sysname(event->dev_parent, &val);
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_DRIVER:
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!event->dev_parent)
|
2019-02-16 23:56:28 +01:00
|
|
|
goto null_terminate;
|
2018-10-29 07:16:00 +01:00
|
|
|
r = sd_device_get_driver(event->dev_parent, &val);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENOENT)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_MAJOR:
|
|
|
|
case FORMAT_SUBST_MINOR: {
|
2018-10-25 06:59:03 +02:00
|
|
|
char buf[DECIMAL_STR_MAX(unsigned)];
|
2017-01-26 02:06:54 +01:00
|
|
|
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_devnum(dev, &devnum);
|
|
|
|
if (r < 0 && r != -ENOENT)
|
|
|
|
return r;
|
2019-04-30 19:18:33 +02:00
|
|
|
xsprintf(buf, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum));
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, buf);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_RESULT: {
|
2017-01-26 02:06:54 +01:00
|
|
|
char *rest;
|
|
|
|
int i;
|
|
|
|
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!event->program_result)
|
2019-02-16 23:56:28 +01:00
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
|
2017-01-26 02:06:54 +01:00
|
|
|
/* get part of the result string */
|
|
|
|
i = 0;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (attr)
|
2017-01-26 02:06:54 +01:00
|
|
|
i = strtoul(attr, &rest, 10);
|
|
|
|
if (i > 0) {
|
2018-10-25 06:59:03 +02:00
|
|
|
char result[UTIL_PATH_SIZE], tmp[UTIL_PATH_SIZE], *cpos;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
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);
|
2018-10-25 06:59:03 +02:00
|
|
|
} else
|
2017-01-26 02:06:54 +01:00
|
|
|
l = strpcpy(&s, l, event->program_result);
|
|
|
|
break;
|
|
|
|
}
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_ATTR: {
|
2017-01-26 02:06:54 +01:00
|
|
|
char vbuf[UTIL_NAME_SIZE];
|
|
|
|
size_t len;
|
|
|
|
int count;
|
|
|
|
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!attr)
|
|
|
|
return -EINVAL;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
/* try to read the value specified by "[dmi/id]product_name" */
|
2018-11-21 09:27:38 +01:00
|
|
|
if (util_resolve_subsys_kernel(attr, vbuf, sizeof(vbuf), true) == 0)
|
2018-10-25 06:59:03 +02:00
|
|
|
val = vbuf;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
/* try to read the attribute the device */
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!val)
|
|
|
|
(void) sd_device_get_sysattr_value(dev, attr, &val);
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
/* try to read the attribute of the parent device, other matches have selected */
|
2018-10-29 07:16:00 +01:00
|
|
|
if (!val && event->dev_parent && event->dev_parent != dev)
|
|
|
|
(void) sd_device_get_sysattr_value(event->dev_parent, attr, &val);
|
2017-01-26 02:06:54 +01:00
|
|
|
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!val)
|
2019-02-16 23:56:28 +01:00
|
|
|
goto null_terminate;
|
2017-01-26 02:06:54 +01:00
|
|
|
|
|
|
|
/* strip trailing whitespace, and replace unwanted characters */
|
2018-10-25 06:59:03 +02:00
|
|
|
if (val != vbuf)
|
|
|
|
strscpy(vbuf, sizeof(vbuf), val);
|
2017-01-26 02:06:54 +01:00
|
|
|
len = strlen(vbuf);
|
|
|
|
while (len > 0 && isspace(vbuf[--len]))
|
|
|
|
vbuf[len] = '\0';
|
|
|
|
count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
|
|
|
|
if (count > 0)
|
2018-10-25 06:59:03 +02:00
|
|
|
log_device_debug(dev, "%i character(s) replaced", count);
|
2017-01-26 02:06:54 +01:00
|
|
|
l = strpcpy(&s, l, vbuf);
|
|
|
|
break;
|
|
|
|
}
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_PARENT:
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_parent(dev, &parent);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENODEV)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_devname(parent, &val);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENOENT)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, val + STRLEN("/dev/"));
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_DEVNODE:
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_devname(dev, &val);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENOENT)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, val);
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_NAME:
|
2018-10-25 06:59:03 +02:00
|
|
|
if (event->name)
|
2017-01-26 02:06:54 +01:00
|
|
|
l = strpcpy(&s, l, event->name);
|
2018-10-25 06:59:03 +02:00
|
|
|
else if (sd_device_get_devname(dev, &val) >= 0)
|
|
|
|
l = strpcpy(&s, l, val + STRLEN("/dev/"));
|
|
|
|
else {
|
|
|
|
r = sd_device_get_sysname(dev, &val);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
l = strpcpy(&s, l, val);
|
|
|
|
}
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_LINKS:
|
2018-10-25 06:59:03 +02:00
|
|
|
FOREACH_DEVICE_DEVLINK(dev, val)
|
|
|
|
if (s == dest)
|
|
|
|
l = strpcpy(&s, l, val + STRLEN("/dev/"));
|
|
|
|
else
|
|
|
|
l = strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (s == dest)
|
|
|
|
goto null_terminate;
|
2017-01-26 02:06:54 +01:00
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_ROOT:
|
2017-01-26 02:06:54 +01:00
|
|
|
l = strpcpy(&s, l, "/dev");
|
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_SYS:
|
2017-01-26 02:06:54 +01:00
|
|
|
l = strpcpy(&s, l, "/sys");
|
|
|
|
break;
|
2019-04-30 19:13:58 +02:00
|
|
|
case FORMAT_SUBST_ENV:
|
2018-10-25 06:59:03 +02:00
|
|
|
if (!attr)
|
2019-02-16 23:56:28 +01:00
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
r = sd_device_get_property_value(dev, attr, &val);
|
2019-02-16 23:56:28 +01:00
|
|
|
if (r == -ENOENT)
|
|
|
|
goto null_terminate;
|
2018-10-25 06:59:03 +02:00
|
|
|
if (r < 0)
|
2019-02-16 23:56:28 +01:00
|
|
|
return r;
|
2018-10-25 06:59:03 +02:00
|
|
|
l = strpcpy(&s, l, val);
|
|
|
|
break;
|
2017-01-26 02:06:54 +01:00
|
|
|
default:
|
2018-10-25 06:07:09 +02:00
|
|
|
assert_not_reached("Unknown format substitution type");
|
2017-01-26 02:06:54 +01:00
|
|
|
}
|
|
|
|
|
2017-01-26 20:18:10 +01:00
|
|
|
return s - dest;
|
2019-02-16 23:56:28 +01:00
|
|
|
|
|
|
|
null_terminate:
|
|
|
|
*s = '\0';
|
|
|
|
return 0;
|
2017-01-26 02:06:54 +01:00
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
ssize_t udev_event_apply_format(UdevEvent *event,
|
2018-10-25 06:59:03 +02:00
|
|
|
const char *src, char *dest, size_t size,
|
|
|
|
bool replace_whitespace) {
|
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 (;;) {
|
2018-10-25 06:07:09 +02:00
|
|
|
const struct subst_map_entry *entry = NULL;
|
2018-10-25 06:59:03 +02:00
|
|
|
char attrbuf[UTIL_PATH_SIZE], *attr;
|
|
|
|
bool format_dollar = false;
|
|
|
|
ssize_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)) {
|
2018-10-25 06:07:09 +02:00
|
|
|
entry = &map[i];
|
2012-01-10 01:34:15 +01:00
|
|
|
from += strlen(map[i].name)+1;
|
2018-10-25 06:59:03 +02:00
|
|
|
format_dollar = true;
|
2012-01-10 01:34:15 +01:00
|
|
|
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) {
|
2018-10-25 06:07:09 +02:00
|
|
|
entry = &map[i];
|
2012-01-10 01:34:15 +01:00
|
|
|
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;
|
2018-10-25 06:59:03 +02:00
|
|
|
} else
|
2012-01-10 01:34:15 +01:00
|
|
|
attr = NULL;
|
|
|
|
|
2019-04-30 19:18:33 +02:00
|
|
|
subst_len = udev_event_subst_format(event, entry->type, attr, s, l);
|
2018-10-25 06:59:03 +02:00
|
|
|
if (subst_len < 0) {
|
|
|
|
if (format_dollar)
|
2018-10-29 08:27:13 +01:00
|
|
|
log_device_warning_errno(event->dev, subst_len, "Failed to substitute variable '$%s', ignoring: %m", entry->name);
|
2018-10-25 06:59:03 +02:00
|
|
|
else
|
2018-10-29 08:27:13 +01:00
|
|
|
log_device_warning_errno(event->dev, subst_len, "Failed to apply format '%%%c', ignoring: %m", entry->fmt);
|
2018-10-25 06:59:03 +02:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2017-01-03 20:37:59 +01:00
|
|
|
|
2019-04-30 19:13:58 +02:00
|
|
|
/* FORMAT_SUBST_RESULT handles spaces itself */
|
|
|
|
if (replace_whitespace && entry->type != FORMAT_SUBST_RESULT)
|
2017-01-26 20:18:10 +01:00
|
|
|
/* 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;
|
2018-10-29 14:37:14 +01:00
|
|
|
char timeout[FORMAT_TIMESPAN_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-29 14:37:14 +01:00
|
|
|
format_timespan(timeout, sizeof(timeout), spawn->timeout_usec, USEC_PER_SEC));
|
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;
|
2018-10-29 14:37:14 +01:00
|
|
|
char timeout[FORMAT_TIMESPAN_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-29 14:37:14 +01:00
|
|
|
format_timespan(timeout, sizeof(timeout), spawn->timeout_warn_usec, USEC_PER_SEC));
|
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;
|
2019-01-06 22:17:00 +01:00
|
|
|
int ret = -EIO;
|
2015-05-15 11:35:15 +02:00
|
|
|
|
|
|
|
assert(spawn);
|
|
|
|
|
|
|
|
switch (si->si_code) {
|
|
|
|
case CLD_EXITED:
|
2019-01-06 22:17:00 +01:00
|
|
|
if (si->si_status == 0)
|
2015-06-10 15:20:02 +02:00
|
|
|
log_debug("Process '%s' succeeded.", spawn->cmd);
|
2019-01-06 22:17:00 +01:00
|
|
|
else
|
|
|
|
log_full(spawn->accept_failure ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
"Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
|
|
|
|
ret = si->si_status;
|
2015-05-15 11:35:15 +02:00
|
|
|
break;
|
|
|
|
case CLD_KILLED:
|
|
|
|
case CLD_DUMPED:
|
2019-01-06 22:17:00 +01:00
|
|
|
log_error("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
|
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
|
|
|
|
2019-01-06 22:17:00 +01:00
|
|
|
sd_event_exit(sd_event_source_get_event(s), ret);
|
2015-05-15 11:35:15 +02:00
|
|
|
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;
|
2019-01-06 22:17:00 +01:00
|
|
|
int r;
|
2015-05-15 11:35:15 +02:00
|
|
|
|
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-12-25 16:36:55 +01:00
|
|
|
if (spawn->fd_stdout >= 0) {
|
|
|
|
r = sd_event_add_io(e, NULL, spawn->fd_stdout, EPOLLIN, on_spawn_io, spawn);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
2018-10-23 06:52:57 +02:00
|
|
|
|
2018-12-25 16:36:55 +01:00
|
|
|
if (spawn->fd_stderr >= 0) {
|
|
|
|
r = sd_event_add_io(e, NULL, spawn->fd_stderr, EPOLLIN, on_spawn_io, spawn);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
2018-10-23 06:52:57 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-01-06 22:17:00 +01:00
|
|
|
return sd_event_loop(e);
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
int udev_event_spawn(UdevEvent *event,
|
2014-07-29 15:18:27 +02:00
|
|
|
usec_t timeout_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-11-20 23:40:44 +01:00
|
|
|
if (isempty(argv[0]))
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Invalid command '%s'", cmd);
|
2018-10-24 23:35:16 +02:00
|
|
|
|
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;
|
|
|
|
|
2018-11-30 10:43:57 +01:00
|
|
|
program = path_join(UDEVLIBEXECDIR, argv[0]);
|
2018-09-23 08:41:39 +02:00
|
|
|
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-29 08:27:13 +01:00
|
|
|
r = device_get_properties_strv(event->dev, &envp);
|
2018-10-22 22:47:21 +02:00
|
|
|
if (r < 0)
|
2018-10-29 08:27:13 +01:00
|
|
|
return log_device_error_errno(event->dev, 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);
|
2018-11-26 16:06:26 +01:00
|
|
|
(void) rlimit_nofile_safe();
|
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,
|
2018-11-13 10:37:05 +01:00
|
|
|
.timeout_warn_usec = udev_warn_timeout(timeout_usec),
|
2018-10-23 06:52:57 +02:00
|
|
|
.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)
|
2019-01-06 22:17:00 +01:00
|
|
|
return log_error_errno(r, "Failed to wait for 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';
|
|
|
|
|
2019-01-06 22:17:00 +01:00
|
|
|
return r; /* 0 for success, and positive if the program failed */
|
2011-04-20 01:53:03 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
static int rename_netif(UdevEvent *event) {
|
2018-10-29 08:27:13 +01:00
|
|
|
sd_device *dev = event->dev;
|
2019-03-09 02:08:03 +01:00
|
|
|
const char *oldname;
|
2018-10-25 00:45:48 +02:00
|
|
|
int ifindex, r;
|
|
|
|
|
|
|
|
if (!event->name)
|
|
|
|
return 0; /* No new name is requested. */
|
|
|
|
|
|
|
|
r = sd_device_get_sysname(dev, &oldname);
|
|
|
|
if (r < 0)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to get sysname: %m");
|
|
|
|
|
|
|
|
if (streq(event->name, oldname))
|
|
|
|
return 0; /* The interface name is already requested name. */
|
|
|
|
|
2019-03-09 02:08:03 +01:00
|
|
|
if (!device_for_action(dev, DEVICE_ACTION_ADD))
|
2018-10-25 00:45:48 +02:00
|
|
|
return 0; /* Rename the interface only when it is added. */
|
2013-10-29 20:03:26 +01:00
|
|
|
|
2018-10-25 00:45:48 +02:00
|
|
|
r = sd_device_get_ifindex(dev, &ifindex);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
return 0; /* Device is not a network interface. */
|
|
|
|
if (r < 0)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-03-04 03:26:23 +01:00
|
|
|
r = rtnl_set_link_name(&event->rtnl, ifindex, event->name);
|
2018-10-25 00:45:48 +02:00
|
|
|
if (r < 0)
|
2019-03-04 03:26:23 +01:00
|
|
|
return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m",
|
|
|
|
ifindex, oldname, event->name);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-03-04 03:45:58 +01:00
|
|
|
/* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */
|
|
|
|
r = device_add_property(dev, "ID_RENAMING", "1");
|
|
|
|
if (r < 0)
|
|
|
|
log_device_warning_errno(dev, r, "Failed to add 'ID_RENAMING' property: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-25 00:45:48 +02:00
|
|
|
r = device_rename(dev, event->name);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
2019-03-04 03:45:58 +01:00
|
|
|
log_device_warning_errno(dev, r, "Failed to update properties with new name '%s': %m", event->name);
|
2013-10-29 20:03:26 +01:00
|
|
|
|
2019-03-04 03:26:23 +01:00
|
|
|
log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, event->name);
|
2013-10-29 20:03:26 +01:00
|
|
|
|
2018-10-25 00:45:48 +02:00
|
|
|
return 1;
|
2008-08-29 20:32:05 +02:00
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
static int update_devnode(UdevEvent *event) {
|
2018-10-29 08:27:13 +01:00
|
|
|
sd_device *dev = event->dev;
|
2018-10-25 03:32:35 +02:00
|
|
|
bool apply;
|
|
|
|
int r;
|
|
|
|
|
2018-10-29 14:45:14 +01:00
|
|
|
r = sd_device_get_devnum(dev, NULL);
|
2018-10-25 03:32:35 +02:00
|
|
|
if (r == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
if (r < 0)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to get devnum: %m");
|
|
|
|
|
|
|
|
/* remove/update possible left-over symlinks from old database entry */
|
2018-10-29 07:01:39 +01:00
|
|
|
if (event->dev_db_clone)
|
|
|
|
(void) udev_node_update_old_links(dev, event->dev_db_clone);
|
2018-10-25 03:32:35 +02:00
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
if (!uid_is_valid(event->uid)) {
|
2018-10-25 03:32:35 +02:00
|
|
|
r = device_get_devnode_uid(dev, &event->uid);
|
2019-04-25 01:21:11 +02:00
|
|
|
if (r == -ENOENT)
|
|
|
|
event->uid = 0;
|
|
|
|
else if (r < 0)
|
2018-10-25 03:32:35 +02:00
|
|
|
return log_device_error_errno(dev, r, "Failed to get devnode UID: %m");
|
|
|
|
}
|
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
if (!gid_is_valid(event->gid)) {
|
2018-10-25 03:32:35 +02:00
|
|
|
r = device_get_devnode_gid(dev, &event->gid);
|
2019-04-25 01:21:11 +02:00
|
|
|
if (r == -ENOENT)
|
|
|
|
event->gid = 0;
|
|
|
|
else if (r < 0)
|
2018-10-25 03:32:35 +02:00
|
|
|
return log_device_error_errno(dev, r, "Failed to get devnode GID: %m");
|
|
|
|
}
|
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
if (event->mode == MODE_INVALID) {
|
2018-10-25 03:32:35 +02:00
|
|
|
r = device_get_devnode_mode(dev, &event->mode);
|
|
|
|
if (r < 0 && r != -ENOENT)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to get devnode mode: %m");
|
|
|
|
if (r == -ENOENT) {
|
|
|
|
if (event->gid > 0)
|
|
|
|
/* default 0660 if a group is assigned */
|
|
|
|
event->mode = 0660;
|
|
|
|
else
|
|
|
|
/* default 0600 */
|
|
|
|
event->mode = 0600;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
apply = device_for_action(dev, DEVICE_ACTION_ADD) ||
|
|
|
|
uid_is_valid(event->uid) ||
|
|
|
|
gid_is_valid(event->gid) ||
|
|
|
|
event->mode != MODE_INVALID;
|
2018-10-25 03:32:35 +02:00
|
|
|
return udev_node_add(dev, apply, event->mode, event->uid, event->gid, event->seclabel_list);
|
|
|
|
}
|
|
|
|
|
2018-10-25 04:14:11 +02:00
|
|
|
static void event_execute_rules_on_remove(
|
2018-12-14 10:27:24 +01:00
|
|
|
UdevEvent *event,
|
2018-11-13 10:37:05 +01:00
|
|
|
usec_t timeout_usec,
|
2018-10-25 04:14:11 +02:00
|
|
|
Hashmap *properties_list,
|
2018-12-14 10:24:40 +01:00
|
|
|
UdevRules *rules) {
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-29 08:27:13 +01:00
|
|
|
sd_device *dev = event->dev;
|
2018-10-25 04:14:11 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
sd-device: reduce the number of implementations of device_read_db() we keep around
We had two very similar functions: device_read_db_aux and device_read_db,
and a number of wrappers for them:
device_read_db_aux
← device_read_db (in sd-device.c)
← all functions in sd-device.c, including sd_device_is_initialized
← device_read_db_force
← event_execute_rules_on_remove (in udev-event.c)
device_read_db (in device-private.c)
← functions in device_private.c (but not device_read_db_force):
device_get_devnode_{mode,uid,gid}
device_get_devlink_priority
device_get_watch_handle
device_clone_with_db
← called from udevadm, udev-{node,event,watch}.c
Before 7141e4f62c3f220872df3114c42d9e4b9525e43e (sd-device: don't retry loading
uevent/db files more than once), the two implementations were the same. In that
commit, device_read_db_aux was changed. Those changes were reverted in the parent
commit, so the two implementations are now again the same except for superficial
differences. This commit removes device_read_db (in sd-device.c), and renames
device_read_db_aux to device_read_db_internal and makes everyone use this one
implementation. There should be no functional change.
2018-12-13 15:49:49 +01:00
|
|
|
r = device_read_db_internal(dev, true);
|
2018-10-25 04:14:11 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to read database under /run/udev/data/: %m");
|
2015-04-23 15:19:13 +02:00
|
|
|
|
2018-10-25 04:14:11 +02:00
|
|
|
r = device_tag_index(dev, NULL, false);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to remove corresponding tag files under /run/udev/tag/, ignoring: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-25 04:14:11 +02:00
|
|
|
r = device_delete_db(dev);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to delete database under /run/udev/data/, ignoring: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-29 04:38:40 +01:00
|
|
|
if (sd_device_get_devnum(dev, NULL) >= 0)
|
2018-10-25 04:14:11 +02:00
|
|
|
(void) udev_watch_end(dev);
|
|
|
|
|
2018-11-13 10:37:05 +01:00
|
|
|
(void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
|
2018-10-25 04:14:11 +02:00
|
|
|
|
2018-10-29 04:38:40 +01:00
|
|
|
if (sd_device_get_devnum(dev, NULL) >= 0)
|
2018-10-25 04:14:11 +02:00
|
|
|
(void) udev_node_remove(dev);
|
|
|
|
}
|
|
|
|
|
2019-03-04 03:45:58 +01:00
|
|
|
static int udev_event_on_move(UdevEvent *event) {
|
|
|
|
sd_device *dev = event->dev;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (event->dev_db_clone &&
|
|
|
|
sd_device_get_devnum(dev, NULL) < 0) {
|
|
|
|
r = device_copy_properties(dev, event->dev_db_clone);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Drop previously added property */
|
|
|
|
r = device_add_property(dev, "ID_RENAMING", NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property, ignoring: %m");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
int udev_event_execute_rules(UdevEvent *event,
|
2018-11-13 10:37:05 +01:00
|
|
|
usec_t timeout_usec,
|
2018-10-25 04:14:11 +02:00
|
|
|
Hashmap *properties_list,
|
2018-12-14 10:24:40 +01:00
|
|
|
UdevRules *rules) {
|
2019-03-09 02:08:03 +01:00
|
|
|
const char *subsystem;
|
|
|
|
DeviceAction action;
|
2019-03-05 02:31:20 +01:00
|
|
|
sd_device *dev;
|
2018-10-25 04:14:11 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(event);
|
|
|
|
assert(rules);
|
|
|
|
|
2019-03-05 02:31:20 +01:00
|
|
|
dev = event->dev;
|
|
|
|
|
2018-10-25 04:14:11 +02:00
|
|
|
r = sd_device_get_subsystem(dev, &subsystem);
|
|
|
|
if (r < 0)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to get subsystem: %m");
|
|
|
|
|
2019-03-09 02:08:03 +01:00
|
|
|
r = device_get_action(dev, &action);
|
2018-10-25 04:14:11 +02:00
|
|
|
if (r < 0)
|
2019-03-09 02:08:03 +01:00
|
|
|
return log_device_error_errno(dev, r, "Failed to get ACTION: %m");
|
2014-09-09 12:23:19 +02:00
|
|
|
|
2019-03-09 02:08:03 +01:00
|
|
|
if (action == DEVICE_ACTION_REMOVE) {
|
2018-11-13 10:37:05 +01:00
|
|
|
event_execute_rules_on_remove(event, timeout_usec, properties_list, rules);
|
2018-10-25 04:14:11 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-29 07:01:39 +01:00
|
|
|
r = device_clone_with_db(dev, &event->dev_db_clone);
|
2018-10-25 04:14:11 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to clone sd_device object, ignoring: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-03-04 03:45:58 +01:00
|
|
|
if (event->dev_db_clone && sd_device_get_devnum(dev, NULL) >= 0)
|
|
|
|
/* Disable watch during event processing. */
|
|
|
|
(void) udev_watch_end(event->dev_db_clone);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-03-09 02:08:03 +01:00
|
|
|
if (action == DEVICE_ACTION_MOVE)
|
2019-03-04 03:45:58 +01:00
|
|
|
(void) udev_event_on_move(event);
|
2018-10-25 04:14:11 +02:00
|
|
|
|
2018-11-13 10:37:05 +01:00
|
|
|
(void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
|
2018-10-25 04:14:11 +02:00
|
|
|
|
|
|
|
(void) rename_netif(event);
|
|
|
|
(void) update_devnode(event);
|
|
|
|
|
|
|
|
/* preserve old, or get new initialization timestamp */
|
2018-10-29 07:01:39 +01:00
|
|
|
r = device_ensure_usec_initialized(dev, event->dev_db_clone);
|
2018-10-25 04:14:11 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to set initialization timestamp, ignoring: %m");
|
|
|
|
|
|
|
|
/* (re)write database file */
|
2018-10-29 07:01:39 +01:00
|
|
|
r = device_tag_index(dev, event->dev_db_clone, true);
|
2018-10-25 04:14:11 +02:00
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to update tags under /run/udev/tag/, ignoring: %m");
|
|
|
|
|
|
|
|
r = device_update_db(dev);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/, ignoring: %m");
|
|
|
|
|
|
|
|
device_set_is_initialized(dev);
|
|
|
|
|
2018-10-29 07:01:39 +01:00
|
|
|
event->dev_db_clone = sd_device_unref(event->dev_db_clone);
|
2018-10-25 04:14:11 +02:00
|
|
|
|
|
|
|
return 0;
|
2008-08-29 20:32:05 +02:00
|
|
|
}
|
2008-10-18 15:50:16 +02:00
|
|
|
|
2018-12-14 10:27:24 +01:00
|
|
|
void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
|
2018-10-16 23:11:33 +02:00
|
|
|
const char *cmd;
|
|
|
|
void *val;
|
|
|
|
Iterator i;
|
2019-04-25 01:21:11 +02:00
|
|
|
int r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-03-04 20:01:34 +01:00
|
|
|
ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) {
|
2019-04-25 01:21:11 +02:00
|
|
|
UdevBuiltinCommand builtin_cmd = PTR_TO_UDEV_BUILTIN_CMD(val);
|
2015-06-28 23:42:52 +02:00
|
|
|
char command[UTIL_PATH_SIZE];
|
2012-04-09 16:37:54 +02:00
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
(void) udev_event_apply_format(event, cmd, command, sizeof(command), false);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
if (builtin_cmd != _UDEV_BUILTIN_INVALID) {
|
|
|
|
log_device_debug(event->dev, "Running built-in command \"%s\"", command);
|
|
|
|
r = udev_builtin_run(event->dev, builtin_cmd, command, false);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
|
|
|
|
} else {
|
2018-10-25 07:56:12 +02:00
|
|
|
if (event->exec_delay_usec > 0) {
|
2019-04-25 01:21:11 +02:00
|
|
|
char buf[FORMAT_TIMESPAN_MAX];
|
|
|
|
|
|
|
|
log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
|
|
|
|
command, format_timespan(buf, sizeof(buf), event->exec_delay_usec, USEC_PER_SEC));
|
2018-10-25 07:56:12 +02:00
|
|
|
(void) usleep(event->exec_delay_usec);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2019-04-25 01:21:11 +02:00
|
|
|
log_device_debug(event->dev, "Running command \"%s\"", command);
|
|
|
|
r = udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
|
|
|
|
if (r > 0) /* returned value is positive when program fails */
|
|
|
|
log_device_debug(event->dev, "Command \"%s\" returned %d (error), ignoring.", command, r);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
2008-10-18 15:50:16 +02:00
|
|
|
}
|