timeout handling without alarm()

This commit is contained in:
Kay Sievers
2011-04-20 01:54:38 +02:00
parent 131c9e9240
commit 2181d30a34
12 changed files with 519 additions and 417 deletions
+2
View File
@@ -12,6 +12,8 @@ systemd netlink socket activation
stop socket or mask on rpm update
requires systemd 25
hard event timeout now. hanging programs will be killed
udev 167
========
Bugfixes.
+7
View File
@@ -1,3 +1,10 @@
- kill alarm()
- empty rules files and link to /dev/null are ok
- rename netif with ifindex not devname
- kill tabs?
- remove deprecated trigger --type=failed logic
+4 -17
View File
@@ -69,7 +69,6 @@ struct udev_device {
struct udev_list_node tags_list;
unsigned long long int seqnum;
unsigned long long int usec_initialized;
int event_timeout;
int timeout;
int devlink_priority;
int refcount;
@@ -376,7 +375,7 @@ struct udev_device *udev_device_new(struct udev *udev)
udev_list_init(&udev_device->sysattr_value_list);
udev_list_init(&udev_device->sysattr_list);
udev_list_init(&udev_device->tags_list);
udev_device->event_timeout = -1;
udev_device->timeout = -1;
udev_device->watch_handle = -1;
/* copy global properties */
udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev))
@@ -1164,7 +1163,7 @@ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device
udev_device_read_db(udev_device, NULL);
if (udev_device->usec_initialized == 0)
return 0;
now = usec_monotonic();
now = now_usec();
if (now == 0)
return 0;
return now - udev_device->usec_initialized;
@@ -1689,23 +1688,11 @@ int udev_device_get_timeout(struct udev_device *udev_device)
}
int udev_device_set_timeout(struct udev_device *udev_device, int timeout)
{
udev_device->timeout = timeout;
return 0;
}
int udev_device_get_event_timeout(struct udev_device *udev_device)
{
if (!udev_device->info_loaded)
udev_device_read_db(udev_device, NULL);
return udev_device->event_timeout;
}
int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout)
{
char num[32];
udev_device->event_timeout = event_timeout;
snprintf(num, sizeof(num), "%u", event_timeout);
udev_device->timeout = timeout;
snprintf(num, sizeof(num), "%u", timeout);
udev_device_add_property(udev_device, "TIMEOUT", num);
return 0;
}
+1 -6
View File
@@ -96,8 +96,6 @@ int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename);
int udev_device_get_timeout(struct udev_device *udev_device);
int udev_device_set_timeout(struct udev_device *udev_device, int timeout);
int udev_device_get_event_timeout(struct udev_device *udev_device);
int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout);
int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum);
int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum);
unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device);
@@ -234,12 +232,9 @@ int util_delete_path(struct udev *udev, const char *path);
int util_unlink_secure(struct udev *udev, const char *filename);
uid_t util_lookup_user(struct udev *udev, const char *user);
gid_t util_lookup_group(struct udev *udev, const char *group);
int util_run_program(struct udev *udev, const char *command, char **envp,
char *result, size_t ressize, size_t *reslen,
const sigset_t *sigmask);
int util_resolve_subsys_kernel(struct udev *udev, const char *string,
char *result, size_t maxsize, int read_value);
unsigned long long usec_monotonic(void);
unsigned long long now_usec(void);
/* libudev-selinux-private.c */
#ifndef WITH_SELINUX
-289
View File
@@ -19,9 +19,6 @@
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <sys/prctl.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <sys/param.h>
#include "libudev.h"
@@ -260,289 +257,3 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
udev_device_unref(dev);
return 0;
}
static int run_program_exec(struct udev *udev, int fd_stdout, int fd_stderr,
char *const argv[], char **envp, const sigset_t *sigmask)
{
int err;
int fd;
/* discard child output or connect to pipe */
fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
if (fd_stdout < 0)
dup2(fd, STDOUT_FILENO);
if (fd_stderr < 0)
dup2(fd, STDERR_FILENO);
close(fd);
} else {
err(udev, "open /dev/null failed: %m\n");
}
/* connect pipes to std{out,err} */
if (fd_stdout >= 0) {
dup2(fd_stdout, STDOUT_FILENO);
close(fd_stdout);
}
if (fd_stderr >= 0) {
dup2(fd_stderr, STDERR_FILENO);
close(fd_stderr);
}
/* terminate child in case parent goes away */
prctl(PR_SET_PDEATHSIG, SIGTERM);
/* restore original udev sigmask before exec */
if (sigmask)
sigprocmask(SIG_SETMASK, sigmask, NULL);
execve(argv[0], argv, envp);
/* exec failed */
err = -errno;
if (err == -ENOENT || err == -ENOTDIR) {
/* may be on a filesystem which is not mounted right now */
info(udev, "program '%s' not found\n", argv[0]);
} else {
/* other problems */
err(udev, "exec of program '%s' failed\n", argv[0]);
}
return err;
}
static int run_program_read(struct udev *udev, int fd_stdout, int fd_stderr,
char *const argv[], char *result, size_t ressize, size_t *reslen)
{
size_t respos = 0;
int fd_ep = -1;
struct epoll_event ep_outpipe;
struct epoll_event ep_errpipe;
int err = 0;
/* read from child if requested */
if (fd_stdout < 0 && fd_stderr < 0)
return 0;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
err = -errno;
err(udev, "error creating epoll fd: %m\n");
goto out;
}
if (fd_stdout >= 0) {
memset(&ep_outpipe, 0, sizeof(struct epoll_event));
ep_outpipe.events = EPOLLIN;
ep_outpipe.data.ptr = &fd_stdout;
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
err(udev, "fail to add fd to epoll: %m\n");
goto out;
}
}
if (fd_stderr >= 0) {
memset(&ep_errpipe, 0, sizeof(struct epoll_event));
ep_errpipe.events = EPOLLIN;
ep_errpipe.data.ptr = &fd_stderr;
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
err(udev, "fail to add fd to epoll: %m\n");
goto out;
}
}
/* read child output */
while (fd_stdout >= 0 || fd_stderr >= 0) {
int fdcount;
struct epoll_event ev[4];
int i;
fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
if (fdcount < 0) {
if (errno == EINTR)
continue;
err(udev, "failed to poll: %m\n");
err = -errno;
goto out;
}
for (i = 0; i < fdcount; i++) {
int *fd = (int *)ev[i].data.ptr;
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 {
err(udev, "ressize %zd too short\n", ressize);
err = -ENOBUFS;
}
}
/* 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')
info(udev, "'%s'(%s) '%s'\n", argv[0], *fd == fd_stdout ? "out" : "err" , line);
}
}
} else if (ev[i].events & EPOLLHUP) {
if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
err = -errno;
err(udev, "failed to remove fd from epoll: %m\n");
goto out;
}
*fd = -1;
}
}
}
/* return the child's stdout string */
if (result != NULL) {
result[respos] = '\0';
dbg(udev, "result='%s'\n", result);
if (reslen != NULL)
*reslen = respos;
}
out:
if (fd_ep >= 0)
close(fd_ep);
return err;
}
int util_run_program(struct udev *udev, const char *command, char **envp,
char *result, size_t ressize, size_t *reslen,
const sigset_t *sigmask)
{
int status;
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
pid_t pid;
char arg[UTIL_PATH_SIZE];
char program[UTIL_PATH_SIZE];
char *argv[((sizeof(arg) + 1) / 2) + 1];
int i;
int err = 0;
info(udev, "'%s' started\n", command);
/* build argv from command */
util_strscpy(arg, sizeof(arg), command);
i = 0;
if (strchr(arg, ' ') != NULL) {
char *pos = arg;
while (pos != NULL && pos[0] != '\0') {
if (pos[0] == '\'') {
/* do not separate quotes */
pos++;
argv[i] = strsep(&pos, "\'");
if (pos != NULL)
while (pos[0] == ' ')
pos++;
} else {
argv[i] = strsep(&pos, " ");
if (pos != NULL)
while (pos[0] == ' ')
pos++;
}
dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
i++;
}
argv[i] = NULL;
} else {
argv[0] = arg;
argv[1] = NULL;
}
/* pipes from child to parent */
if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
if (pipe2(outpipe, O_NONBLOCK) != 0) {
err = -errno;
err(udev, "pipe failed: %m\n");
goto out;
}
}
if (udev_get_log_priority(udev) >= LOG_INFO) {
if (pipe2(errpipe, O_NONBLOCK) != 0) {
err = -errno;
err(udev, "pipe failed: %m\n");
goto out;
}
}
/* allow programs in /lib/udev/ to be called without the path */
if (argv[0][0] != '/') {
util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
argv[0] = program;
}
pid = fork();
switch(pid) {
case 0:
/* child closes parent's ends of pipes */
if (outpipe[READ_END] >= 0) {
close(outpipe[READ_END]);
outpipe[READ_END] = -1;
}
if (errpipe[READ_END] >= 0) {
close(errpipe[READ_END]);
errpipe[READ_END] = -1;
}
err = run_program_exec(udev, outpipe[WRITE_END], errpipe[WRITE_END], argv, envp, sigmask);
_exit(1);
case -1:
err(udev, "fork of '%s' failed: %m\n", argv[0]);
err = -1;
goto out;
default:
/* parent closed child's ends of pipes */
if (outpipe[WRITE_END] >= 0) {
close(outpipe[WRITE_END]);
outpipe[WRITE_END] = -1;
}
if (errpipe[WRITE_END] >= 0) {
close(errpipe[WRITE_END]);
errpipe[WRITE_END] = -1;
}
err = run_program_read(udev, outpipe[READ_END], errpipe[READ_END], argv, result, ressize, reslen);
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
info(udev, "'%s' returned with exitcode %i\n", command, WEXITSTATUS(status));
if (WEXITSTATUS(status) != 0)
err = -1;
} else {
err(udev, "'%s' unexpected exit with status 0x%04x\n", command, status);
err = -1;
}
}
out:
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;
}
+1 -1
View File
@@ -557,7 +557,7 @@ uint64_t util_string_bloom64(const char *str)
#define USEC_PER_SEC 1000000ULL
#define NSEC_PER_USEC 1000ULL
unsigned long long usec_monotonic(void)
unsigned long long now_usec(void)
{
struct timespec ts;
+19 -42
View File
@@ -23,36 +23,24 @@
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <grp.h>
#include <sys/signalfd.h>
#include "udev.h"
static void sig_handler(int signum)
{
switch (signum) {
case SIGALRM:
_exit(1);
case SIGINT:
case SIGTERM:
_exit(20 + signum);
}
}
int main(int argc, char *argv[])
{
struct udev *udev;
struct udev_event *event;
struct udev_device *dev;
struct udev_rules *rules;
struct udev_event *event = NULL;
struct udev_device *dev = NULL;
struct udev_rules *rules = NULL;
char syspath[UTIL_PATH_SIZE];
const char *devpath;
const char *action;
const char *subsystem;
struct sigaction act;
sigset_t mask;
sigset_t mask, sigmask_orig;
int err = -EINVAL;
udev = udev_new();
@@ -61,22 +49,7 @@ int main(int argc, char *argv[])
info(udev, "version %s\n", VERSION);
udev_selinux_init(udev);
/* set signal handlers */
memset(&act, 0x00, sizeof(act));
act.sa_handler = sig_handler;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGALRM, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
/* trigger timeout to prevent hanging processes */
alarm(UDEV_EVENT_TIMEOUT);
sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
action = getenv("ACTION");
devpath = getenv("DEVPATH");
@@ -84,7 +57,7 @@ int main(int argc, char *argv[])
if (action == NULL || subsystem == NULL || devpath == NULL) {
err(udev, "action, subsystem or devpath missing\n");
goto exit;
goto out;
}
rules = udev_rules_new(udev, 1);
@@ -93,7 +66,7 @@ int main(int argc, char *argv[])
dev = udev_device_new_from_syspath(udev, syspath);
if (dev == NULL) {
info(udev, "unknown device '%s'\n", devpath);
goto fail;
goto out;
}
/* skip reading of db, but read kernel parameters */
@@ -102,20 +75,24 @@ int main(int argc, char *argv[])
udev_device_set_action(dev, action);
event = udev_event_new(dev);
err = udev_event_execute_rules(event, rules);
/* rules may change/disable the timeout */
if (udev_device_get_event_timeout(dev) >= 0)
alarm(udev_device_get_event_timeout(dev));
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (event->fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");
goto out;
}
err = udev_event_execute_rules(event, rules, &sigmask_orig);
if (err == 0)
udev_event_execute_run(event, NULL);
out:
if (event != NULL && event->fd_signal >= 0)
close(event->fd_signal);
udev_event_unref(event);
udev_device_unref(dev);
fail:
udev_rules_unref(rules);
exit:
udev_selinux_exit(udev);
udev_unref(udev);
if (err != 0)
+384 -7
View File
@@ -26,7 +26,12 @@
#include <time.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/signalfd.h>
#include <linux/sockios.h>
#include "udev.h"
@@ -42,6 +47,9 @@ struct udev_event *udev_event_new(struct udev_device *dev)
event->dev = dev;
event->udev = udev_device_get_udev(dev);
udev_list_init(&event->run_list);
event->fd_signal = -1;
event->birth_usec = now_usec();
event->timeout_usec = UDEV_EVENT_TIMEOUT_SEC * 1000 * 1000;
dbg(event->udev, "allocated event %p\n", event);
return event;
}
@@ -439,6 +447,374 @@ out:
return l;
}
static int spawn_exec(struct udev_event *event,
const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
int fd_stdout, int fd_stderr)
{
struct udev *udev = event->udev;
int err;
int fd;
/* discard child output or connect to pipe */
fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
if (fd_stdout < 0)
dup2(fd, STDOUT_FILENO);
if (fd_stderr < 0)
dup2(fd, STDERR_FILENO);
close(fd);
} else {
err(udev, "open /dev/null failed: %m\n");
}
/* connect pipes to std{out,err} */
if (fd_stdout >= 0) {
dup2(fd_stdout, STDOUT_FILENO);
close(fd_stdout);
}
if (fd_stderr >= 0) {
dup2(fd_stderr, STDERR_FILENO);
close(fd_stderr);
}
/* terminate child in case parent goes away */
prctl(PR_SET_PDEATHSIG, SIGTERM);
/* restore original udev sigmask before exec */
if (sigmask)
sigprocmask(SIG_SETMASK, sigmask, NULL);
execve(argv[0], argv, envp);
/* exec failed */
err = -errno;
err(udev, "exec of program '%s' failed\n", cmd);
return err;
}
static int spawn_read(struct udev_event *event,
const char *cmd,
int fd_stdout, int fd_stderr,
char *result, size_t ressize)
{
struct udev *udev = event->udev;
size_t respos = 0;
int fd_ep = -1;
struct epoll_event ep_outpipe, ep_errpipe;
int err = 0;
/* read from child if requested */
if (fd_stdout < 0 && fd_stderr < 0)
return 0;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
err = -errno;
err(udev, "error creating epoll fd: %m\n");
goto out;
}
if (fd_stdout >= 0) {
memset(&ep_outpipe, 0, sizeof(struct epoll_event));
ep_outpipe.events = EPOLLIN;
ep_outpipe.data.ptr = &fd_stdout;
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
err(udev, "fail to add fd to epoll: %m\n");
goto out;
}
}
if (fd_stderr >= 0) {
memset(&ep_errpipe, 0, sizeof(struct epoll_event));
ep_errpipe.events = EPOLLIN;
ep_errpipe.data.ptr = &fd_stderr;
if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
err(udev, "fail to add fd to epoll: %m\n");
goto out;
}
}
/* read child output */
while (fd_stdout >= 0 || fd_stderr >= 0) {
int timeout;
int fdcount;
struct epoll_event ev[4];
int i;
if (event->timeout_usec > 0) {
unsigned long long age_usec;
age_usec = now_usec() - event->birth_usec;
if (age_usec >= event->timeout_usec) {
err = -ETIMEDOUT;
err(udev, "timeout '%s'\n", cmd);
goto out;
}
timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
} else {
timeout = -1;
}
fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
if (fdcount < 0) {
if (errno == EINTR)
continue;
err = -errno;
err(udev, "failed to poll: %m\n");
goto out;
}
if (fdcount == 0) {
err = -ETIMEDOUT;
err(udev, "timeout '%s'\n", cmd);
goto out;
}
for (i = 0; i < fdcount; i++) {
int *fd = (int *)ev[i].data.ptr;
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 {
err(udev, "'%s' ressize %zd too short\n", cmd, ressize);
err = -ENOBUFS;
}
}
/* 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')
info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line);
}
}
} else if (ev[i].events & EPOLLHUP) {
if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
err = -errno;
err(udev, "failed to remove fd from epoll: %m\n");
goto out;
}
*fd = -1;
}
}
}
/* return the child's stdout string */
if (result != NULL) {
result[respos] = '\0';
dbg(udev, "result='%s'\n", result);
}
out:
if (fd_ep >= 0)
close(fd_ep);
return err;
}
static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid)
{
struct udev *udev = event->udev;
struct pollfd pfd[1];
int err = 0;
pfd[0].events = POLLIN;
pfd[0].fd = event->fd_signal;
while (pid > 0) {
int timeout;
int fdcount;
if (event->timeout_usec > 0) {
unsigned long long age_usec;
age_usec = now_usec() - event->birth_usec;
if (age_usec >= event->timeout_usec)
timeout = 1000;
else
timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
} else {
timeout = -1;
}
fdcount = poll(pfd, 1, timeout);
if (fdcount < 0) {
if (errno == EINTR)
continue;
err = -errno;
err(udev, "failed to poll: %m\n");
goto out;
}
if (fdcount == 0) {
err(udev, "timeout: killing '%s'[%u]\n", cmd, pid);
kill(pid, SIGKILL);
}
if (pfd[0].revents & POLLIN) {
struct signalfd_siginfo fdsi;
int status;
ssize_t size;
size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
if (size != sizeof(struct signalfd_siginfo))
continue;
switch (fdsi.ssi_signo) {
case SIGTERM:
event->sigterm = true;
break;
case SIGCHLD:
if (waitpid(pid, &status, WNOHANG) < 0)
break;
if (WIFEXITED(status)) {
info(udev, "'%s'[%u] returned with exitcode %i\n", cmd, pid, WEXITSTATUS(status));
if (WEXITSTATUS(status) != 0)
err = -1;
} else {
err(udev, "'%s'[%u] unexpected exit with status 0x%04x\n", cmd, pid, status);
err = -1;
}
pid = 0;
break;
}
}
}
out:
return err;
}
int udev_event_spawn(struct udev_event *event,
const char *cmd, char **envp, const sigset_t *sigmask,
char *result, size_t ressize)
{
struct udev *udev = event->udev;
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
pid_t pid;
char arg[UTIL_PATH_SIZE];
char program[UTIL_PATH_SIZE];
char *argv[((sizeof(arg) + 1) / 2) + 1];
int i;
int err = 0;
/* build argv from command */
util_strscpy(arg, sizeof(arg), cmd);
i = 0;
if (strchr(arg, ' ') != NULL) {
char *pos = arg;
while (pos != NULL && pos[0] != '\0') {
if (pos[0] == '\'') {
/* do not separate quotes */
pos++;
argv[i] = strsep(&pos, "\'");
if (pos != NULL)
while (pos[0] == ' ')
pos++;
} else {
argv[i] = strsep(&pos, " ");
if (pos != NULL)
while (pos[0] == ' ')
pos++;
}
dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
i++;
}
argv[i] = NULL;
} else {
argv[0] = arg;
argv[1] = NULL;
}
/* pipes from child to parent */
if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
if (pipe2(outpipe, O_NONBLOCK) != 0) {
err = -errno;
err(udev, "pipe failed: %m\n");
goto out;
}
}
if (udev_get_log_priority(udev) >= LOG_INFO) {
if (pipe2(errpipe, O_NONBLOCK) != 0) {
err = -errno;
err(udev, "pipe failed: %m\n");
goto out;
}
}
/* allow programs in /lib/udev/ to be called without the path */
if (argv[0][0] != '/') {
util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
argv[0] = program;
}
pid = fork();
switch(pid) {
case 0:
/* child closes parent's ends of pipes */
if (outpipe[READ_END] >= 0) {
close(outpipe[READ_END]);
outpipe[READ_END] = -1;
}
if (errpipe[READ_END] >= 0) {
close(errpipe[READ_END]);
errpipe[READ_END] = -1;
}
info(udev, "starting '%s'\n", cmd);
err = spawn_exec(event, cmd, argv, envp, sigmask,
outpipe[WRITE_END], errpipe[WRITE_END]);
_exit(2 );
case -1:
err(udev, "fork of '%s' failed: %m\n", cmd);
err = -1;
goto out;
default:
/* parent closed child's ends of pipes */
if (outpipe[WRITE_END] >= 0) {
close(outpipe[WRITE_END]);
outpipe[WRITE_END] = -1;
}
if (errpipe[WRITE_END] >= 0) {
close(errpipe[WRITE_END]);
errpipe[WRITE_END] = -1;
}
err = spawn_read(event, cmd,
outpipe[READ_END], errpipe[READ_END],
result, ressize);
err = spawn_wait(event, cmd, pid);
}
out:
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;
}
static void rename_netif_kernel_log(struct ifreq ifr)
{
int klog;
@@ -530,7 +906,7 @@ out:
return err;
}
int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask)
{
struct udev_device *dev = event->dev;
int err = 0;
@@ -546,7 +922,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, dev);
udev_rules_apply_to_event(rules, event);
udev_rules_apply_to_event(rules, event, sigmask);
if (major(udev_device_get_devnum(dev)) != 0)
err = udev_node_remove(dev);
@@ -561,7 +937,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
udev_watch_end(event->udev, event->dev_db);
}
udev_rules_apply_to_event(rules, event);
udev_rules_apply_to_event(rules, event, sigmask);
/* rename a new network interface, if needed */
if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 &&
@@ -647,7 +1023,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0)
udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db));
else
udev_device_set_usec_initialized(event->dev, usec_monotonic());
udev_device_set_usec_initialized(event->dev, now_usec());
/* (re)write database file */
udev_device_update_db(dev);
@@ -682,13 +1058,14 @@ int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask)
char program[UTIL_PATH_SIZE];
char **envp;
udev_event_apply_format(event, cmd, program, sizeof(program));
envp = udev_device_get_properties_envp(event->dev);
if (event->exec_delay > 0) {
info(event->udev, "delay execution of '%s'\n", program);
sleep(event->exec_delay);
}
if (util_run_program(event->udev, program, envp, NULL, 0, NULL, sigmask) != 0) {
udev_event_apply_format(event, cmd, program, sizeof(program));
envp = udev_device_get_properties_envp(event->dev);
if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) {
if (udev_list_entry_get_flags(list_entry))
err = -1;
}
+20 -16
View File
@@ -139,6 +139,7 @@ enum token_type {
TK_M_PARENTS_MAX,
TK_M_TEST, /* val, mode_t */
TK_M_EVENT_TIMEOUT, /* int */
TK_M_PROGRAM, /* val */
TK_M_IMPORT_FILE, /* val */
TK_M_IMPORT_PROG, /* val */
@@ -164,7 +165,6 @@ enum token_type {
TK_A_TAG, /* val */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_EVENT_TIMEOUT, /* int */
TK_A_ATTR, /* val, attr */
TK_A_RUN, /* val, bool */
TK_A_GOTO, /* size_t */
@@ -274,6 +274,7 @@ static const char *token_str(enum token_type type)
[TK_M_PARENTS_MAX] = "M PARENTS_MAX",
[TK_M_TEST] = "M TEST",
[TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT",
[TK_M_PROGRAM] = "M PROGRAM",
[TK_M_IMPORT_FILE] = "M IMPORT_FILE",
[TK_M_IMPORT_PROG] = "M IMPORT_PROG",
@@ -299,7 +300,6 @@ static const char *token_str(enum token_type type)
[TK_A_TAG] = "A ENV",
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
[TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT",
[TK_A_ATTR] = "A ATTR",
[TK_A_RUN] = "A RUN",
[TK_A_GOTO] = "A GOTO",
@@ -397,7 +397,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
case TK_A_STATIC_NODE:
dbg(rules->udev, "%s '%s'\n", token_str(type), value);
break;
case TK_A_EVENT_TIMEOUT:
case TK_M_EVENT_TIMEOUT:
dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout);
break;
case TK_A_GOTO:
@@ -750,17 +750,18 @@ static int import_file_into_properties(struct udev_device *dev, const char *file
return 0;
}
static int import_program_into_properties(struct udev_device *dev, const char *program)
static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask)
{
struct udev *udev = udev_device_get_udev(dev);
struct udev_device *dev = event->dev;
char **envp;
char result[UTIL_LINE_SIZE];
size_t reslen;
char *line;
int err;
envp = udev_device_get_properties_envp(dev);
if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL) != 0)
return -1;
err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result));
if (err < 0)
return err;
line = result;
while (line != NULL) {
@@ -1070,7 +1071,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
case TK_A_STATIC_NODE:
token->key.value_off = add_string(rule_tmp->rules, value);
break;
case TK_A_EVENT_TIMEOUT:
case TK_M_EVENT_TIMEOUT:
token->key.event_timeout = *(int *)data;
break;
case TK_RULE:
@@ -1581,7 +1582,7 @@ static int add_rule(struct udev_rules *rules, char *line,
if (pos != NULL) {
int tout = atoi(&pos[strlen("event_timeout=")]);
rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, op, NULL, &tout);
rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout);
dbg(rules->udev, "event timeout=%i\n", tout);
}
@@ -2081,7 +2082,7 @@ enum escape_type {
ESCAPE_REPLACE,
};
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event)
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask)
{
struct token *cur;
struct token *rule;
@@ -2283,6 +2284,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
goto nomatch;
break;
}
case TK_M_EVENT_TIMEOUT:
info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout);
event->timeout_usec = cur->key.event_timeout * 1000 * 1000;
break;
case TK_M_PROGRAM:
{
char program[UTIL_PATH_SIZE];
@@ -2297,7 +2302,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
program,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL) != 0) {
if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -2335,7 +2341,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
import,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
if (import_program_into_properties(event->dev, import) != 0)
if (import_program_into_properties(event, import, sigmask) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2617,9 +2624,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
}
break;
case TK_A_EVENT_TIMEOUT:
udev_device_set_event_timeout(event->dev, cur->key.event_timeout);
break;
case TK_A_ATTR:
{
const char *key_name = &rules->buf[cur->key.attr_off];
+11 -5
View File
@@ -26,8 +26,7 @@
#include "libudev.h"
#include "libudev-private.h"
#define DEFAULT_FAKE_PARTITIONS_COUNT 15
#define UDEV_EVENT_TIMEOUT 180
#define UDEV_EVENT_TIMEOUT_SEC 120
#define UDEV_CTRL_SOCK_PATH "@/org/kernel/udev/udevd"
@@ -44,6 +43,10 @@ struct udev_event {
gid_t gid;
struct udev_list_node run_list;
int exec_delay;
unsigned long long birth_usec;
unsigned long long timeout_usec;
int fd_signal;
bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_final;
@@ -64,17 +67,20 @@ struct udev_watch {
struct udev_rules;
struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
void udev_rules_unref(struct udev_rules *rules);
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event);
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
void udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
struct udev_event *udev_event_new(struct udev_device *dev);
void udev_event_unref(struct udev_event *event);
int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules);
int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
char *result, size_t maxsize, int read_value);
int udev_event_spawn(struct udev_event *event,
const char *cmd, char **envp, const sigset_t *sigmask,
char *result, size_t ressize);
int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset);
int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
/* udev-watch.c */
int udev_watch_init(struct udev *udev);
+46 -17
View File
@@ -27,23 +27,27 @@
#include <signal.h>
#include <syslog.h>
#include <getopt.h>
#include <sys/signalfd.h>
#include "udev.h"
int udevadm_test(struct udev *udev, int argc, char *argv[])
{
int resolve_names = 1;
char filename[UTIL_PATH_SIZE];
const char *action = "add";
const char *syspath = NULL;
struct udev_event *event;
struct udev_device *dev;
struct udev_event *event = NULL;
struct udev_device *dev = NULL;
struct udev_rules *rules = NULL;
struct udev_list_entry *entry;
sigset_t mask, sigmask_orig;
int err;
int rc = 0;
static const struct option options[] = {
{ "action", required_argument, NULL, 'a' },
{ "resolve-names", required_argument, NULL, 'N' },
{ "help", no_argument, NULL, 'h' },
{}
};
@@ -53,7 +57,7 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
for (;;) {
int option;
option = getopt_long(argc, argv, "a:s:fh", options, NULL);
option = getopt_long(argc, argv, "a:s:N:fh", options, NULL);
if (option == -1)
break;
@@ -62,21 +66,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
case 'a':
action = optarg;
break;
case 'N':
if (strcmp (optarg, "early") == 0) {
resolve_names = 1;
} else if (strcmp (optarg, "late") == 0) {
resolve_names = 0;
} else if (strcmp (optarg, "never") == 0) {
resolve_names = -1;
} else {
fprintf(stderr, "resolve-names must be early, late or never\n");
err(udev, "resolve-names must be early, late or never\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
printf("Usage: udevadm test OPTIONS <syspath>\n"
" --action=<string> set action string\n"
" --help\n\n");
exit(0);
exit(EXIT_SUCCESS);
default:
exit(1);
exit(EXIT_FAILURE);
}
}
syspath = argv[optind];
if (syspath == NULL) {
fprintf(stderr, "syspath parameter missing\n");
rc = 1;
goto exit;
rc = 2;
goto out;
}
printf("This program is for debugging only, it does not run any program,\n"
@@ -84,11 +101,13 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
"some values may be different, or not available at a simulation run.\n"
"\n");
rules = udev_rules_new(udev, 1);
sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
fprintf(stderr, "error reading rules\n");
rc = 1;
goto exit;
rc = 3;
goto out;
}
/* add /sys if needed */
@@ -101,8 +120,8 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
dev = udev_device_new_from_syspath(udev, filename);
if (dev == NULL) {
fprintf(stderr, "unable to open device '%s'\n", filename);
rc = 2;
goto exit;
rc = 4;
goto out;
}
/* skip reading of db, but read kernel parameters */
@@ -111,24 +130,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
udev_device_set_action(dev, action);
event = udev_event_new(dev);
err = udev_event_execute_rules(event, rules);
if (udev_device_get_event_timeout(dev) >= 0)
printf("custom event timeout: %i\n", udev_device_get_event_timeout(dev));
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (event->fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");
rc = 5;
goto out;
}
err = udev_event_execute_rules(event, rules, &sigmask_orig);
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
if (err == 0)
if (err == 0) {
udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
char program[UTIL_PATH_SIZE];
udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
printf("run: '%s'\n", program);
}
}
out:
if (event != NULL && event->fd_signal >= 0)
close(event->fd_signal);
udev_event_unref(event);
udev_device_unref(dev);
exit:
udev_rules_unref(rules);
return rc;
}
+24 -17
View File
@@ -81,7 +81,7 @@ static bool reload_config;
static int children;
static int children_max;
static int exec_delay;
static sigset_t orig_sigmask;
static sigset_t sigmask_orig;
static UDEV_LIST(event_list);
static UDEV_LIST(worker_list);
static bool udev_exit;
@@ -286,9 +286,9 @@ static void worker_new(struct event *event)
for (;;) {
struct udev_event *udev_event;
struct worker_message msg = {};
int err;
struct worker_message msg;
int failed = 0;
int err;
info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
@@ -297,23 +297,17 @@ static void worker_new(struct event *event)
goto out;
}
/* set timeout to prevent hanging processes */
alarm(UDEV_EVENT_TIMEOUT);
/* needed for SIGCHLD/SIGTERM in spawn() */
udev_event->fd_signal = fd_signal;
if (exec_delay > 0)
udev_event->exec_delay = exec_delay;
/* apply rules, create node, symlinks */
err = udev_event_execute_rules(udev_event, rules);
/* rules may change/disable the timeout */
if (udev_device_get_event_timeout(dev) >= 0)
alarm(udev_device_get_event_timeout(dev));
err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
if (err == 0)
failed = udev_event_execute_run(udev_event, &orig_sigmask);
alarm(0);
failed = udev_event_execute_run(udev_event, &sigmask_orig);
/* apply/restore inotify watch */
if (err == 0 && udev_event->inotify_watch) {
@@ -325,6 +319,7 @@ static void worker_new(struct event *event)
udev_monitor_send_device(worker_monitor, NULL, dev);
/* send udevd the result of the event execution */
memset(&msg, 0, sizeof(struct worker_message));
if (err != 0)
msg.exitcode = err;
else if (failed != 0)
@@ -333,10 +328,17 @@ static void worker_new(struct event *event)
send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
udev_event_unref(udev_event);
udev_device_unref(dev);
dev = NULL;
if (udev_event->sigterm) {
udev_event_unref(udev_event);
goto out;
}
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */
while (dev == NULL) {
struct epoll_event ev[4];
@@ -344,8 +346,13 @@ static void worker_new(struct event *event)
int i;
fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
if (fdcount < 0)
continue;
if (fdcount < 0) {
if (errno == EINTR)
continue;
err = -errno;
err(udev, "failed to poll: %m\n");
goto out;
}
for (i = 0; i < fdcount; i++) {
if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
@@ -1478,7 +1485,7 @@ int main(int argc, char *argv[])
/* block and listen to all signals on signalfd */
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (fd_signal < 0) {
fprintf(stderr, "error creating signalfd\n");