From: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
To: linux-hotplug@vger.kernel.org
Subject: [PATCH 2/4] Start external programs from a separate process
Date: Wed, 22 Oct 2008 14:16:45 +0000 [thread overview]
Message-ID: <48FF35CD.4060601@tuffmail.co.uk> (raw)
Do-not-merge: The timeout for hanging processes is untested
diff --git a/udev/Makefile.am b/udev/Makefile.am
index c374942..2b35bd9 100644
--- a/udev/Makefile.am
+++ b/udev/Makefile.am
@@ -18,6 +18,7 @@ common_files = \
udev-event.c \
udev-node.c \
udev-rules.c \
+ udev-exec.c \
udev-util.c \
lib/libudev.h \
lib/libudev-private.h \
diff --git a/udev/test-udev.c b/udev/test-udev.c
index 7604513..929f752 100644
--- a/udev/test-udev.c
+++ b/udev/test-udev.c
@@ -60,6 +60,9 @@ int main(int argc, char *argv[])
info(udev, "version %s\n", VERSION);
udev_selinux_init(udev);
+ /* fork exec daemon before opening fds or changing signal handling */
+ udev_exec_init(udev);
+
/* set signal handlers */
memset(&act, 0x00, sizeof(act));
act.sa_handler = (void (*)(int)) sig_handler;
@@ -69,9 +72,6 @@ int main(int argc, char *argv[])
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
- /* trigger timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
-
action = getenv("ACTION");
devpath = getenv("DEVPATH");
subsystem = getenv("SUBSYSTEM");
@@ -99,10 +99,6 @@ int main(int argc, char *argv[])
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));
-
if (err = 0 && !event->ignore_device && udev_get_run(udev))
udev_event_execute_run(event);
diff --git a/udev/udev-event.c b/udev/udev-event.c
index 05bb022..99e2995 100644
--- a/udev/udev-event.c
+++ b/udev/udev-event.c
@@ -731,7 +731,7 @@ int udev_event_execute_run(struct udev_event *event)
util_strlcpy(program, cmd, sizeof(program));
udev_event_apply_format(event, program, sizeof(program));
envp = udev_device_get_properties_envp(event->dev);
- if (util_run_program(event->udev, program, envp, NULL, 0, NULL) != 0) {
+ if (udev_exec(event->dev, program, NULL, 0, NULL) != 0) {
if (!udev_list_entry_get_flag(list_entry))
err = -1;
}
diff --git a/udev/udev-exec.c b/udev/udev-exec.c
new file mode 100644
index 0000000..5bc0fc1
--- /dev/null
+++ b/udev/udev-exec.c
@@ -0,0 +1,594 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include "udev.h"
+
+/*
+ * This daemon avoids problems with fork+exec from a multithreaded process -
+ * performance, and the need to close irrelevant file descriptors.
+ *
+ * TODO: Timeouts have not been tested
+ */
+
+#define MSG_SIZE 2048
+
+struct child {
+ struct udev_list_node node; /* must be first member */
+ pid_t pid;
+ int statuspipe; /* Pipe to write exit status to */
+};
+
+static int exec_msg(struct udev *udev, int sock, struct child **childp)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ char msgbuf[MSG_SIZE];
+ int retval;
+ size_t count;
+ struct cmsghdr *cmsg;
+ char fdbuf[CMSG_SPACE(sizeof(int) * 4)]; /* ancillary data buffer */
+ int *fdptr = NULL;
+ int statuspipe;
+ int inpipe, outpipe, errpipe;
+ char *cmd, *env;
+ size_t cmdlen, envlen;
+ char program[UTIL_PATH_SIZE];
+ char *argv[(MSG_SIZE / 2) + 1];
+ char *envp[(MSG_SIZE / 4) + 1];
+ struct child *child;
+ pid_t pid;
+ int i;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = fdbuf;
+ msg.msg_controllen = sizeof(fdbuf);
+
+ iov[0].iov_base = msgbuf;
+ iov[0].iov_len = sizeof(msgbuf);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ retval = recvmsg(sock, &msg, 0);
+ if (retval < 0) {
+ err(udev, "read error on socket (quitting): %m\n");
+ return -1;
+ }
+ count = retval;
+ if (count = 0) {
+ info(udev, "socket closed by udevd (quitting)\n");
+ return -1;
+ }
+
+ /* get file descriptors from message */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+ if (cmsg->cmsg_level = SOL_SOCKET && cmsg->cmsg_type = SCM_RIGHTS) {
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(int) * 4)) {
+ err(udev, "message has too few pipes\n");
+ return 0;
+ }
+
+ fdptr = (int *) CMSG_DATA(cmsg);
+ inpipe = fdptr[0];
+ outpipe = fdptr[1];
+ errpipe = fdptr[2];
+ statuspipe = fdptr[3];
+ break;
+ }
+ }
+ if (fdptr = NULL) {
+ err(udev, "message lacks pipes\n");
+ return 0;
+ }
+
+ /* get command string and environment buffer from message */
+ cmd = &msgbuf[0];
+ cmdlen = strnlen(msgbuf, count);
+ if (cmdlen = count) {
+ err(udev, "message command lacks nul terminator\n");
+ return 0;
+ }
+ count -= (cmdlen + 1);
+ env = &msgbuf[cmdlen + 1];
+ envlen = count;
+
+ /* build argv from command */
+ info(udev, "'%s'\n", cmd);
+ i = 0;
+ if (strchr(cmd, ' ') != NULL) {
+ char *pos = cmd;
+
+ while (pos != NULL && pos[0] != '\0') {
+ if (pos[0] = '\'') {
+ /* do not separate quotes */
+ pos++;
+ argv[i] = strsep(&pos, "\'");
+ while (pos != NULL && pos[0] = ' ')
+ pos++;
+ } else {
+ argv[i] = strsep(&pos, " ");
+ }
+ dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
+ i++;
+ }
+ argv[i] = NULL;
+ } else {
+ argv[0] = cmd;
+ argv[1] = NULL;
+ }
+
+ /* allow programs in /lib/udev/ to be called without the path */
+ if (strchr(argv[0], '/') = NULL) {
+ util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program));
+ util_strlcat(program, argv[0], sizeof(program));
+ argv[0] = program;
+ }
+
+ /* build envp from environment buffer */
+ i = 0;
+ if (envlen > 0) {
+ size_t off = 0;
+
+ while (off < envlen) {
+ envp[i] = &env[off];
+ off += strlen(&env[off]);
+ off++;
+ i++;
+ }
+ envp[i] = NULL;
+ } else {
+ envp[0] = NULL;
+ }
+
+
+ child = malloc(sizeof(struct child));
+ if (child = NULL)
+ return 0;
+
+ /* CAUTION. Child may (or may not) share memory - including stack.
+ Child should do the minimum necessary before exec() */
+ pid = vfork();
+ switch(pid) {
+ case 0:
+ close(sock);
+ close(statuspipe);
+
+ dup2(inpipe, STDIN_FILENO);
+ close(inpipe);
+ dup2(outpipe, STDOUT_FILENO);
+ close(outpipe);
+ dup2(errpipe, STDERR_FILENO);
+ close(errpipe);
+
+ execve(argv[0], argv, envp);
+ if (errno = ENOENT || errno = ENOTDIR) {
+ /* may be on a filesytem 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]);
+ }
+ _exit(1);
+ case -1:
+ err(udev, "fork of '%s' failed: %m\n", argv[0]);
+ free(child);
+ return 0;
+ default:
+ close(inpipe);
+ close(outpipe);
+ close(errpipe);
+
+ child->pid = pid;
+ child->statuspipe = statuspipe;
+
+ *childp = child;
+ return 0;
+ }
+}
+
+static int signal_pipe[2] = {-1, -1};
+
+static void asmlinkage sig_handler(int signum)
+{
+ /* write to pipe, which will wakeup select() */
+ write(signal_pipe[WRITE_END], "", 1);
+}
+
+static void udev_exec_daemon(struct udev *udev, int sock)
+{
+ struct udev_list_node child_list; /* Oldest first */
+ struct udev_list_node *child_loop;
+ struct sigaction act;
+ fd_set fds;
+ int maxfd;
+ int retval;
+
+ udev_list_init(&child_list);
+
+ /* setup signal handler pipe */
+ retval = pipe(signal_pipe);
+ if (retval < 0) {
+ err(udev, "error getting pipes: %m\n");
+ goto exit;
+ }
+
+ retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0);
+ if (retval < 0) {
+ err(udev, "error fcntl on pipe: %m\n");
+ goto exit;
+ }
+ retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK);
+ if (retval < 0) {
+ err(udev, "error fcntl on pipe: %m\n");
+ goto exit;
+ }
+
+ /* reset handlers to default */
+ memset(&act, 0x00, sizeof(struct sigaction));
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+
+ /* set SIGCHLD handler */
+ act.sa_handler = sig_handler;
+ act.sa_flags = SA_RESTART | SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &act, NULL);
+
+ maxfd = sock;
+ maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]);
+ while(1) {
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ FD_SET(signal_pipe[READ_END], &fds);
+
+ retval = select(maxfd+1, &fds, NULL, NULL, NULL);
+ if (retval < 0) {
+ if (errno = EINTR)
+ continue;
+
+ err(udev, "error in select: %m\n");
+ goto exit;
+ }
+
+ /* received a signal, clear our notification pipe */
+ if (FD_ISSET(signal_pipe[READ_END], &fds)) {
+ char buf[256];
+
+ read(signal_pipe[READ_END], &buf, sizeof(buf));
+ }
+
+ /* handle finished children */
+ while (1) {
+ pid_t pid;
+ int status;
+
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid <= 0)
+ break;
+
+ udev_list_node_foreach(child_loop, &child_list) {
+ struct child *child = (struct child *) child_loop;
+
+ if (child->pid = pid) {
+ write(child->statuspipe, &status, sizeof(status));
+ close(child->statuspipe);
+ udev_list_node_remove(&child->node);
+ free(child);
+ break;
+ }
+ }
+ }
+
+ /* read command and fork child */
+ if (FD_ISSET(sock, &fds)) {
+ struct child *new_child;
+
+ retval = exec_msg(udev, sock, &new_child);
+ if (retval = -1)
+ goto exit; /* read from socket failed - udevd has been terminated */
+
+ if (new_child)
+ udev_list_node_append(&new_child->node, &child_list);
+ }
+ }
+
+exit:
+ if (signal_pipe[READ_END] >= 0)
+ close(signal_pipe[READ_END]);
+ if (signal_pipe[WRITE_END] >= 0)
+ close(signal_pipe[WRITE_END]);
+ close(sock);
+
+ udev_unref(udev);
+ exit(1);
+}
+
+static int udev_exec_sock = -1;
+
+/* Use saved fd in case we re-create /dev/null */
+static int devnull = -1;
+
+int udev_exec_init(struct udev *udev)
+{
+ int sock[2];
+ int retval;
+ pid_t pid;
+
+ /* connection-oriented datagram socket -
+ daemon will die when we close it */
+ retval = socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, sock);
+ if (retval != 0) {
+ err(udev, "error getting socket pair: %m\n");
+ return -1;
+ }
+
+ retval = fflush(NULL);
+ if (retval != 0) {
+ err(udev, "error flushing buffers before fork: %m\n");
+ return -1;
+ }
+
+ pid = fork();
+ switch(pid) {
+ case 0:
+ close(sock[0]);
+ logging_close();
+ logging_init("udev-exec");
+ setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
+
+ udev_exec_daemon(udev, sock[1]);
+
+ /* not reached */
+ exit(1);
+ case -1:
+ err(udev, "failed to fork udev-exec daemon: %m\n");
+ return -1;
+ default:
+ udev_exec_sock = sock[0];
+ close(sock[1]);
+ }
+
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ err(udev, "open /dev/null failed: %m\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void udev_exec_cleanup(struct udev *udev)
+{
+ close(udev_exec_sock);
+ close(devnull);
+}
+
+int udev_exec(struct udev_device *dev, const char *command,
+ char *result, size_t ressize, size_t *reslen)
+{
+ struct udev *udev = udev_device_get_udev(dev);
+ char **envp = udev_device_get_properties_envp(dev);
+ int outpipe[2] = {-1, -1};
+ int errpipe[2] = {-1, -1};
+ int statuspipe[2] = {-1, -1};
+ int status;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char fdbuf[CMSG_SPACE(sizeof(int) * 4)]; /* ancillary data buffer */
+ int *fdptr;
+ struct iovec iov[2];
+ char envbuf[2048];
+ size_t bufpos = 0;
+ int i;
+ ssize_t count;
+ int maxfd;
+ struct timeval timeout;
+ size_t respos = 0;
+ int err = 0;
+
+ /* prepare pipes from child to parent */
+ if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
+ if (pipe(outpipe) != 0) {
+ err(udev, "pipe failed: %m\n");
+ return -1;
+ }
+ } else
+ outpipe[WRITE_END] = dup(devnull);
+
+ if (udev_get_log_priority(udev) >= LOG_INFO) {
+ if (pipe(errpipe) != 0) {
+ err(udev, "pipe failed: %m\n");
+ return -1;
+ }
+ } else
+ errpipe[WRITE_END] = dup(devnull);
+
+ if (pipe(statuspipe) != 0) {
+ err(udev, "pipe failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* child end of pipes are passed in ancillary data */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = fdbuf;
+ msg.msg_controllen = sizeof(fdbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 4);
+ fdptr = (int *) CMSG_DATA(cmsg);
+ fdptr[0] = devnull;
+ fdptr[1] = outpipe[WRITE_END];
+ fdptr[2] = errpipe[WRITE_END];
+ fdptr[3] = statuspipe[WRITE_END];
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ for (i = 0; envp[i] != NULL && bufpos < (sizeof(envbuf)); i++) {
+ bufpos += util_strlcpy(&envbuf[bufpos], envp[i], sizeof(envbuf) - bufpos);
+ bufpos++;
+ }
+ if (bufpos > sizeof(envbuf))
+ bufpos = sizeof(envbuf);
+
+ /* prepare message payload: command + environment */
+ iov[0].iov_base = (char *) command;
+ iov[0].iov_len = strlen(command) + 1;
+ iov[1].iov_base = envbuf;
+ iov[1].iov_len = bufpos;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ /* dispatch request to daemon */
+ count = sendmsg(udev_exec_sock, &msg, 0);
+ if (count < 0) {
+ /* report error, but don't return without closing pipes */
+ err(udev, "failed to send message to exec daemon: %s\n", strerror(errno));
+ }
+
+ /* parent closes child ends of pipes */
+ close(outpipe[WRITE_END]);
+ close(errpipe[WRITE_END]);
+ close(statuspipe[WRITE_END]);
+
+ /* read child output */
+ if (udev_device_get_event_timeout(dev) >= 0)
+ timeout.tv_sec = udev_device_get_event_timeout(dev);
+ else
+ timeout.tv_sec = UDEV_EVENT_TIMEOUT;
+ timeout.tv_usec = 0;
+
+ maxfd = statuspipe[READ_END];
+ maxfd = UDEV_MAX(maxfd, outpipe[READ_END]);
+ maxfd = UDEV_MAX(maxfd, errpipe[READ_END]);
+ while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0 || statuspipe[READ_END] > 0) {
+ int fdcount;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ if (outpipe[READ_END] > 0)
+ FD_SET(outpipe[READ_END], &readfds);
+ if (errpipe[READ_END] > 0)
+ FD_SET(errpipe[READ_END], &readfds);
+ if (statuspipe[READ_END] > 0)
+ FD_SET(statuspipe[READ_END], &readfds);
+ fdcount = select(maxfd+1, &readfds, NULL, NULL, &timeout);
+ if (fdcount < 0) {
+ if (errno = EINTR)
+ continue;
+ err = -1;
+ break;
+ }
+
+ if (timeout.tv_sec = 0 && timeout.tv_usec = 0) {
+ err = -1;
+ break;
+ }
+
+ /* get stdout */
+ if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
+ char inbuf[1024];
+ char *pos;
+ char *line;
+
+ count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
+ if (count <= 0) {
+ close(outpipe[READ_END]);
+ outpipe[READ_END] = -1;
+ if (count < 0) {
+ err(udev, "stdin read failed: %m\n");
+ err = -1;
+ }
+ continue;
+ }
+ inbuf[count] = '\0';
+
+ /* store result for rule processing */
+ if (result) {
+ if (respos + count < ressize) {
+ memcpy(&result[respos], inbuf, count);
+ respos += count;
+ } else {
+ err(udev, "ressize %ld too short\n", (long)ressize);
+ err = -1;
+ }
+ }
+ pos = inbuf;
+ while ((line = strsep(&pos, "\n")))
+ if (pos || line[0] != '\0')
+ info(udev, "'%s' (stdout) '%s'\n", command, line);
+ }
+
+ /* get stderr */
+ if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
+ char errbuf[1024];
+ char *pos;
+ char *line;
+
+ count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
+ if (count <= 0) {
+ close(errpipe[READ_END]);
+ errpipe[READ_END] = -1;
+ if (count < 0)
+ err(udev, "stderr read failed: %m\n");
+ continue;
+ }
+ errbuf[count] = '\0';
+ pos = errbuf;
+ while ((line = strsep(&pos, "\n")))
+ if (pos || line[0] != '\0')
+ info(udev, "'%s' (stderr) '%s'\n", command, line);
+ }
+
+ /* get exitstatus */
+ if (statuspipe[READ_END] > 0 && FD_ISSET(statuspipe[READ_END], &readfds)) {
+ count = read(statuspipe[READ_END], &status, sizeof(status));
+
+ if (count <= 0) {
+ if (count < 0)
+ err(udev, "'%s' error reading exit status: %m\n", command);
+ else
+ err(udev, "'%s' EOF on status pipe\n", command);
+ err = -1;
+
+ close(statuspipe[READ_END]);
+ statuspipe[READ_END] = -1;
+ continue;
+ }
+
+ if (WIFEXITED(status))
+ info(udev, "'%s' returned with status %i\n", command, WEXITSTATUS(status));
+ else
+ err(udev, "'%s' abnormal exit\n", command);
+ err = status;
+
+ close(statuspipe[READ_END]);
+ statuspipe[READ_END] = -1;
+ }
+ }
+ if (outpipe[READ_END] > 0)
+ close(outpipe[READ_END]);
+ if (outpipe[READ_END] > 0)
+ close(errpipe[READ_END]);
+ if (statuspipe[READ_END] > 0)
+ close(statuspipe[READ_END]);
+
+ /* return the childs stdout string */
+ if (result) {
+ result[respos] = '\0';
+ dbg(udev, "result='%s'\n", result);
+ if (reslen)
+ *reslen = respos;
+ }
+
+ return err;
+}
diff --git a/udev/udev-rules.c b/udev/udev-rules.c
index 32eed78..14f25f5 100644
--- a/udev/udev-rules.c
+++ b/udev/udev-rules.c
@@ -284,13 +284,11 @@ static int import_file_into_env(struct udev_device *dev, const char *filename)
static int import_program_into_env(struct udev_device *dev, const char *program)
{
struct udev *udev = udev_device_get_udev(dev);
- char **envp;
char result[2048];
size_t reslen;
char *line;
- envp = udev_device_get_properties_envp(dev);
- if (util_run_program(udev, program, envp, result, sizeof(result), &reslen) != 0)
+ if (udev_exec(dev, program, result, sizeof(result), &reslen) != 0)
return -1;
line = result;
@@ -684,13 +682,11 @@ try_parent:
/* execute external program */
if (rule->program.operation != KEY_OP_UNSET) {
char program[UTIL_PATH_SIZE];
- char **envp;
char result[UTIL_PATH_SIZE];
util_strlcpy(program, key_val(rule, &rule->program), sizeof(program));
udev_event_apply_format(event, program, sizeof(program));
- envp = udev_device_get_properties_envp(dev);
- if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL) != 0) {
+ if (udev_exec(event->dev, program, result, sizeof(result), NULL) != 0) {
dbg(event->udev, "PROGRAM is false\n");
event->program_result[0] = '\0';
if (rule->program.operation != KEY_OP_NOMATCH)
diff --git a/udev/udev-util.c b/udev/udev-util.c
index 204c096..f7ff369 100644
--- a/udev/udev-util.c
+++ b/udev/udev-util.c
@@ -25,7 +25,6 @@
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
-#include <sys/wait.h>
#include "udev.h"
@@ -168,215 +167,3 @@ extern gid_t util_lookup_group(struct udev *udev, const char *group)
gid = gr->gr_gid;
return gid;
}
-
-int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen)
-{
- 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) / 2) + 1];
- int devnull;
- int i;
- int err = 0;
-
- /* build argv from command */
- util_strlcpy(arg, command, sizeof(arg));
- 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, "\'");
- while (pos != NULL && pos[0] = ' ')
- pos++;
- } else {
- argv[i] = strsep(&pos, " ");
- }
- dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
- i++;
- }
- argv[i] = NULL;
- } else {
- argv[0] = arg;
- argv[1] = NULL;
- }
- info(udev, "'%s'\n", command);
-
- /* prepare pipes from child to parent */
- if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(outpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
- if (udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(errpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
-
- /* allow programs in /lib/udev/ to be called without the path */
- if (strchr(argv[0], '/') = NULL) {
- util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program));
- util_strlcat(program, argv[0], sizeof(program));
- argv[0] = program;
- }
-
- pid = fork();
- switch(pid) {
- case 0:
- /* child closes parent ends of pipes */
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* discard child output or connect to pipe */
- devnull = open("/dev/null", O_RDWR);
- if (devnull > 0) {
- dup2(devnull, STDIN_FILENO);
- if (outpipe[WRITE_END] < 0)
- dup2(devnull, STDOUT_FILENO);
- if (errpipe[WRITE_END] < 0)
- dup2(devnull, STDERR_FILENO);
- close(devnull);
- } else
- err(udev, "open /dev/null failed: %m\n");
- if (outpipe[WRITE_END] > 0) {
- dup2(outpipe[WRITE_END], STDOUT_FILENO);
- close(outpipe[WRITE_END]);
- }
- if (errpipe[WRITE_END] > 0) {
- dup2(errpipe[WRITE_END], STDERR_FILENO);
- close(errpipe[WRITE_END]);
- }
- execve(argv[0], argv, envp);
- if (errno = ENOENT || errno = ENOTDIR) {
- /* may be on a filesytem 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]);
- }
- _exit(1);
- case -1:
- err(udev, "fork of '%s' failed: %m\n", argv[0]);
- return -1;
- default:
- /* read from child if requested */
- if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- ssize_t count;
- size_t respos = 0;
-
- /* parent closes child ends of pipes */
- if (outpipe[WRITE_END] > 0)
- close(outpipe[WRITE_END]);
- if (errpipe[WRITE_END] > 0)
- close(errpipe[WRITE_END]);
-
- /* read child output */
- while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- int fdcount;
- fd_set readfds;
-
- FD_ZERO(&readfds);
- if (outpipe[READ_END] > 0)
- FD_SET(outpipe[READ_END], &readfds);
- if (errpipe[READ_END] > 0)
- FD_SET(errpipe[READ_END], &readfds);
- fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
- if (fdcount < 0) {
- if (errno = EINTR)
- continue;
- err = -1;
- break;
- }
-
- /* get stdout */
- if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
- char inbuf[1024];
- char *pos;
- char *line;
-
- count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
- if (count <= 0) {
- close(outpipe[READ_END]);
- outpipe[READ_END] = -1;
- if (count < 0) {
- err(udev, "stdin read failed: %m\n");
- err = -1;
- }
- continue;
- }
- inbuf[count] = '\0';
-
- /* store result for rule processing */
- if (result) {
- if (respos + count < ressize) {
- memcpy(&result[respos], inbuf, count);
- respos += count;
- } else {
- err(udev, "ressize %ld too short\n", (long)ressize);
- err = -1;
- }
- }
- pos = inbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stdout) '%s'\n", argv[0], line);
- }
-
- /* get stderr */
- if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
- char errbuf[1024];
- char *pos;
- char *line;
-
- count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
- if (count <= 0) {
- close(errpipe[READ_END]);
- errpipe[READ_END] = -1;
- if (count < 0)
- err(udev, "stderr read failed: %m\n");
- continue;
- }
- errbuf[count] = '\0';
- pos = errbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stderr) '%s'\n", argv[0], line);
- }
- }
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* return the childs stdout string */
- if (result) {
- result[respos] = '\0';
- dbg(udev, "result='%s'\n", result);
- if (reslen)
- *reslen = respos;
- }
- }
- waitpid(pid, &status, 0);
- if (WIFEXITED(status)) {
- info(udev, "'%s' returned with status %i\n", argv[0], WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else {
- err(udev, "'%s' abnormal exit\n", argv[0]);
- err = -1;
- }
- }
- return err;
-}
diff --git a/udev/udev.h b/udev/udev.h
index 89f6f11..fd3c73d 100644
--- a/udev/udev.h
+++ b/udev/udev.h
@@ -104,14 +104,17 @@ extern int udev_node_add(struct udev_device *dev, mode_t mode, const char *owner
extern int udev_node_remove(struct udev_device *dev, int test);
extern void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old, int test);
+/* udev-exec.c */
+extern int udev_exec_init(struct udev *udev);
+extern int udev_exec(struct udev_device *dev, const char *command,
+ char *result, size_t ressize, size_t *reslen);
+
/* udev-util.c */
extern int util_create_path(struct udev *udev, const char *path);
extern int util_delete_path(struct udev *udev, const char *path);
extern int util_unlink_secure(struct udev *udev, const char *filename);
extern uid_t util_lookup_user(struct udev *udev, const char *user);
extern gid_t util_lookup_group(struct udev *udev, const char *group);
-extern int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen);
/* udev-selinux.c */
#ifndef USE_SELINUX
diff --git a/udev/udevd.c b/udev/udevd.c
index 21fd6f8..a2c4230 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -208,16 +208,9 @@ static void event_fork(struct udev_event *event)
sigaction(SIGCHLD, &act, NULL);
sigaction(SIGHUP, &act, NULL);
- /* set timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
-
/* apply rules, create node, symlinks */
err = udev_event_execute_rules(event, rules);
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(event->dev) >= 0)
- alarm(udev_device_get_event_timeout(event->dev));
-
/* execute RUN= */
if (err = 0 && !event->ignore_device && udev_get_run(event->udev))
udev_event_execute_run(event);
@@ -699,6 +692,13 @@ int main(int argc, char *argv[])
if (write(STDERR_FILENO, 0, 0) < 0)
dup2(fd, STDERR_FILENO);
+ chdir("/");
+ umask(022);
+ setsid();
+
+ /* fork exec daemon before opening more fds or changing signal handling */
+ udev_exec_init(udev);
+
/* init control socket, bind() ensures, that only one udevd instance is running */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
if (udev_ctrl = NULL) {
@@ -791,10 +791,6 @@ int main(int argc, char *argv[])
/* set scheduling priority for the daemon */
setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
- chdir("/");
- umask(022);
- setsid();
-
/* OOM_DISABLE = -17 */
fd = open("/proc/self/oom_adj", O_RDWR);
if (fd < 0)
reply other threads:[~2008-10-22 14:16 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=48FF35CD.4060601@tuffmail.co.uk \
--to=alan-jenkins@tuffmail.co.uk \
--cc=linux-hotplug@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).