* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
#include <ctype.h>
-#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
#include <net/if.h>
-#include <sys/prctl.h>
#include <poll.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/epoll.h>
-#include <sys/wait.h>
+#include <sys/prctl.h>
#include <sys/signalfd.h>
+#include <sys/wait.h>
+#include <unistd.h>
-#include "udev.h"
-#include "rtnl-util.h"
#include "event-util.h"
#include "formats-util.h"
+#include "netlink-util.h"
#include "process-util.h"
+#include "signal-util.h"
+#include "string-util.h"
+#include "udev.h"
typedef struct Spawn {
const char *cmd;
pid_t pid;
usec_t timeout_warn;
usec_t timeout;
+ bool accept_failure;
} Spawn;
struct udev_event *udev_event_new(struct udev_device *dev) {
void udev_event_unref(struct udev_event *event) {
if (event == NULL)
return;
- sd_rtnl_unref(event->rtnl);
+ sd_netlink_unref(event->rtnl);
udev_list_cleanup(&event->run_list);
udev_list_cleanup(&event->seclabel_list);
free(event->program_result);
char *s;
size_t l;
+ assert(dev);
+
from = src;
s = dest;
l = size;
}
static int spawn_exec(struct udev_event *event,
- const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
+ const char *cmd, char *const argv[], char **envp,
int fd_stdout, int fd_stderr) {
_cleanup_close_ int fd = -1;
+ int r;
/* 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);
+ r = dup2(fd, STDIN_FILENO);
+ if (r < 0)
+ log_warning_errno(errno, "redirecting stdin failed: %m");
+
+ if (fd_stdout < 0) {
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0)
+ log_warning_errno(errno, "redirecting stdout failed: %m");
+ }
+
+ if (fd_stderr < 0) {
+ r = dup2(fd, STDERR_FILENO);
+ if (r < 0)
+ log_warning_errno(errno, "redirecting stderr failed: %m");
+ }
} else
- log_error_errno(errno, "open /dev/null failed: %m");
+ log_warning_errno(errno, "open /dev/null failed: %m");
/* connect pipes to std{out,err} */
if (fd_stdout >= 0) {
- dup2(fd_stdout, STDOUT_FILENO);
- safe_close(fd_stdout);
+ r = dup2(fd_stdout, STDOUT_FILENO);
+ if (r < 0)
+ log_warning_errno(errno, "redirecting stdout failed: %m");
+
+ fd_stdout = safe_close(fd_stdout);
}
+
if (fd_stderr >= 0) {
- dup2(fd_stderr, STDERR_FILENO);
- safe_close(fd_stderr);
+ r = dup2(fd_stderr, STDERR_FILENO);
+ if (r < 0)
+ log_warning_errno(errno, "redirecting stdout failed: %m");
+
+ fd_stderr = safe_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);
+ /* restore sigmask before exec */
+ (void) reset_signal_mask();
execve(argv[0], argv, envp);
switch (si->si_code) {
case CLD_EXITED:
- if (si->si_status != 0)
- log_warning("process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
- else {
- log_debug("process '%s' succeeded.", spawn->cmd);
+ if (si->si_status == 0) {
+ log_debug("Process '%s' succeeded.", spawn->cmd);
sd_event_exit(sd_event_source_get_event(s), 0);
return 1;
- }
+ } else if (spawn->accept_failure)
+ log_debug("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+ else
+ log_warning("Process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
break;
case CLD_KILLED:
case CLD_DUMPED:
- log_warning("process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
+ log_warning("Process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
break;
default:
- log_error("process '%s' failed due to unknown reason.", spawn->cmd);
+ log_error("Process '%s' failed due to unknown reason.", spawn->cmd);
}
sd_event_exit(sd_event_source_get_event(s), -EIO);
static int spawn_wait(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, pid_t pid) {
+ const char *cmd, pid_t pid,
+ bool accept_failure) {
Spawn spawn = {
.cmd = cmd,
.pid = pid,
+ .accept_failure = accept_failure,
};
_cleanup_event_unref_ sd_event *e = NULL;
int r, ret;
if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
spawn.timeout_warn = timeout_warn_usec - age_usec;
- r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
- usec + spawn.timeout_warn, USEC_PER_SEC,
- on_spawn_timeout_warning, &spawn);
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout_warn, USEC_PER_SEC,
+ on_spawn_timeout_warning, &spawn);
if (r < 0)
return r;
}
int udev_event_spawn(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
- const char *cmd, char **envp, const sigset_t *sigmask,
+ bool accept_failure,
+ const char *cmd,
char *result, size_t ressize) {
int outpipe[2] = {-1, -1};
int errpipe[2] = {-1, -1};
pid_t pid;
- char arg[UTIL_PATH_SIZE];
- char *argv[128];
- char program[UTIL_PATH_SIZE];
int err = 0;
- strscpy(arg, sizeof(arg), cmd);
- udev_build_argv(event->udev, arg, NULL, argv);
-
/* pipes from child to parent */
if (result != NULL || log_get_max_level() >= LOG_INFO) {
if (pipe2(outpipe, O_NONBLOCK) != 0) {
- err = -errno;
- log_error_errno(errno, "pipe failed: %m");
+ err = log_error_errno(errno, "pipe failed: %m");
goto out;
}
}
if (log_get_max_level() >= LOG_INFO) {
if (pipe2(errpipe, O_NONBLOCK) != 0) {
- err = -errno;
- log_error_errno(errno, "pipe failed: %m");
+ err = log_error_errno(errno, "pipe failed: %m");
goto out;
}
}
- /* allow programs in /usr/lib/udev/ to be called without the path */
- if (argv[0][0] != '/') {
- strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
- argv[0] = program;
- }
-
pid = fork();
switch(pid) {
case 0:
+ {
+ char arg[UTIL_PATH_SIZE];
+ char *argv[128];
+ char program[UTIL_PATH_SIZE];
+
/* 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;
+ outpipe[READ_END] = safe_close(outpipe[READ_END]);
+ errpipe[READ_END] = safe_close(errpipe[READ_END]);
+
+ strscpy(arg, sizeof(arg), cmd);
+ udev_build_argv(event->udev, arg, NULL, argv);
+
+ /* allow programs in /usr/lib/udev/ to be called without the path */
+ if (argv[0][0] != '/') {
+ strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL);
+ argv[0] = program;
}
log_debug("starting '%s'", cmd);
- spawn_exec(event, cmd, argv, envp, sigmask,
+ spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev),
outpipe[WRITE_END], errpipe[WRITE_END]);
- _exit(2 );
+ _exit(2);
+ }
case -1:
log_error_errno(errno, "fork of '%s' failed: %m", 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;
- }
+ outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]);
+ errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]);
spawn_read(event,
timeout_usec,
outpipe[READ_END], errpipe[READ_END],
result, ressize);
- err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid);
+ err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure);
}
out:
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
- struct udev_rules *rules,
- const sigset_t *sigmask) {
+ struct udev_rules *rules) {
struct udev_device *dev = event->dev;
if (udev_device_get_subsystem(dev) == NULL)
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
if (major(udev_device_get_devnum(dev)) != 0)
udev_node_remove(dev);
udev_rules_apply_to_event(rules, event,
timeout_usec, timeout_warn_usec,
- properties_list,
- sigmask);
+ properties_list);
/* rename a new network interface, if needed */
if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") &&
}
}
-void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigmask) {
+void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec) {
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) {
+ char command[UTIL_PATH_SIZE];
const char *cmd = udev_list_entry_get_name(list_entry);
enum udev_builtin_cmd builtin_cmd = udev_list_entry_get_num(list_entry);
- if (builtin_cmd < UDEV_BUILTIN_MAX) {
- char command[UTIL_PATH_SIZE];
+ udev_event_apply_format(event, cmd, command, sizeof(command));
- udev_event_apply_format(event, cmd, command, sizeof(command));
+ if (builtin_cmd < UDEV_BUILTIN_MAX)
udev_builtin_run(event->dev, builtin_cmd, command, false);
- } else {
- char program[UTIL_PATH_SIZE];
- char **envp;
-
+ else {
if (event->exec_delay > 0) {
- log_debug("delay execution of '%s'", program);
+ log_debug("delay execution of '%s'", command);
sleep(event->exec_delay);
}
- udev_event_apply_format(event, cmd, program, sizeof(program));
- envp = udev_device_get_properties_envp(event->dev);
- udev_event_spawn(event, timeout_usec, timeout_warn_usec, program, envp, sigmask, NULL, 0);
+ udev_event_spawn(event, timeout_usec, timeout_warn_usec, false, command, NULL, 0);
}
}
}