[PATCH 2/4] Start external programs from a separate process

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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)


--
To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux