udev-event.c revision 9f6445e34a57c270f013c9416c123e56261553dd
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * Copyright (C) 2003-2013 Kay Sievers <kay@vrfy.org>
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * This program is free software: you can redistribute it and/or modify
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * it under the terms of the GNU General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * the Free Software Foundation, either version 2 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * This program is distributed in the hope that it will be useful,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * but WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * GNU General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * You should have received a copy of the GNU General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * along with this program. If not, see <http://www.gnu.org/licenses/>.
178cc7700c23ac088cd7190d7854282075028d91Lennart Poetteringstruct udev_event *udev_event_new(struct udev_device *dev)
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek struct udev *udev = udev_device_get_udev(dev);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek event = calloc(1, sizeof(struct udev_event));
46b131574fdd7d77c15a0919ca9010cad7aa6ac7Lennart Poettering udev_list_init(udev, &event->run_list, false);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek udev_list_init(udev, &event->seclabel_list, false);
b92bea5d2a9481de69bb627a7b442a9f58fca43dZbigniew Jędrzejewski-Szmek event->birth_usec = now(CLOCK_MONOTONIC);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringvoid udev_event_unref(struct udev_event *event)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering udev_list_cleanup(&event->seclabel_list);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringsize_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering static const struct subst_map {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "devnode", .fmt = 'N', .type = SUBST_DEVNODE },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "tempnode", .fmt = 'N', .type = SUBST_DEVNODE },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "attr", .fmt = 's', .type = SUBST_ATTR },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "env", .fmt = 'E', .type = SUBST_ENV },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH },
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering { .name = "id", .fmt = 'b', .type = SUBST_ID },
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering { .name = "major", .fmt = 'M', .type = SUBST_MAJOR },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "minor", .fmt = 'm', .type = SUBST_MINOR },
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering { .name = "result", .fmt = 'c', .type = SUBST_RESULT },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "parent", .fmt = 'P', .type = SUBST_PARENT },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "name", .fmt = 'D', .type = SUBST_NAME },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "links", .fmt = 'L', .type = SUBST_LINKS },
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt { .name = "root", .fmt = 'r', .type = SUBST_ROOT },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering { .name = "sys", .fmt = 'S', .type = SUBST_SYS },
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* substitute named variable */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unsigned int i;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* substitute format char */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unsigned int i;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* copy char */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* extract possible $format{attr} */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering unsigned int i;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error("missing closing brace for format '%s'", src);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (i >= sizeof(attrbuf))
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering l = strpcpy(&s, l, udev_device_get_devpath(dev));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering l = strpcpy(&s, l, udev_device_get_sysname(dev));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering l = strpcpy(&s, l, udev_device_get_sysnum(dev));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering driver = udev_device_get_driver(event->dev_parent);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering sprintf(num, "%d", major(udev_device_get_devnum(dev)));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering sprintf(num, "%d", minor(udev_device_get_devnum(dev)));
e3bfb7be07d9b1f4ebb12eb22c4c8bcd2a988d51Zbigniew Jędrzejewski-Szmek /* get part part of the result string */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering strscpy(result, sizeof(result), event->program_result);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering while (cpos[0] != '\0' && !isspace(cpos[0]))
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_error("requested part of result string not found");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* %{2+}c copies the whole string from the second part on */
ac50788b0f5aeee09e7d45db28ae8ab7f39cd52eZbigniew Jędrzejewski-Szmek l = strpcpy(&s, l, event->program_result);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_error("missing file parameter for attr");
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* try to read the value specified by "[dmi/id]product_name" */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* try to read the attribute the device */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering value = udev_device_get_sysattr_value(event->dev, attr);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* try to read the attribute of the parent device, other matches have selected */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering value = udev_device_get_sysattr_value(event->dev_parent, attr);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering /* strip trailing whitespace, and replace unwanted characters */
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering log_debug("%i character(s) replaced" , count);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering dev_parent = udev_device_get_parent(event->dev);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering devnode = udev_device_get_devnode(dev_parent);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpy(&s, l, devnode + strlen("/dev/"));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (udev_device_get_devnode(dev) != NULL)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpy(&s, l, udev_device_get_devnode(dev));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering else if (udev_device_get_devnode(dev) != NULL)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/"));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpy(&s, l, udev_device_get_sysname(dev));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering list_entry = udev_device_get_devlinks_list_entry(dev);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/"));
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL);
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering value = udev_device_get_property_value(event->dev, attr);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error("unknown substitution type=%i", type);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int spawn_exec(struct udev_event *event,
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* discard child output or connect to pipe */
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering /* connect pipes to std{out,err} */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* terminate child in case parent goes away */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* restore original udev sigmask before exec */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* exec failed */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error("failed to execute '%s' '%s': %m", argv[0], cmd);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic void spawn_read(struct udev_event *event,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering struct epoll_event ep_outpipe, ep_errpipe;
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt /* read from child if requested */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering memset(&ep_outpipe, 0, sizeof(struct epoll_event));
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt memset(&ep_errpipe, 0, sizeof(struct epoll_event));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering log_error("fail to add fd to epoll: %m");
23bbb0de4e3f85d9704a5c12a5afa2dfa0159e41Michal Schmidt /* read child output */
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
goto out;
if (fdcount == 0) {
goto out;
for (i = 0; i < fdcount; i++) {
if (count <= 0)
if (fd_stderr >= 0) {
char *pos;
char *line;
goto out;
out:
if (fd_ep >= 0)
int err = 0;
while (pid > 0) {
int timeout;
int fdcount;
if (fdcount < 0) {
goto out;
if (fdcount == 0) {
int status;
case SIGTERM:
case SIGCHLD:
log_error("'%s' [%u] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
pid = 0;
out:
return err;
char *pos;
goto out;
pos++;
pos++;
pos++;
out:
if (argc)
*argc = i;
int err = 0;
goto out;
goto out;
switch(pid) {
goto out;
out:
return err;
const char *oldname;
int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask)
int err = 0;
char *pos;
if (err == 0) {
pos++;
bool apply;
apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
return err;
char **envp;