* [PATCH 2/4] Start external programs from a separate process
@ 2008-10-22 14:16 Alan Jenkins
0 siblings, 0 replies; only message in thread
From: Alan Jenkins @ 2008-10-22 14:16 UTC (permalink / raw)
To: linux-hotplug
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)
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2008-10-22 14:16 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-22 14:16 [PATCH 2/4] Start external programs from a separate process Alan Jenkins
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.