X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=udev_utils_run.c;h=76a704c52cb19b93db15fe517f2b3171043708b8;hp=c2e77cbbcc13c75e0ca1a96572f91291ce50199b;hb=b2885eeecf517e82830f585a69ebc9c2c60cfa5e;hpb=59d6bfefceac1a31b0408e60cd251b5035cf3b50 diff --git a/udev_utils_run.c b/udev_utils_run.c index c2e77cbbc..76a704c52 100644 --- a/udev_utils_run.c +++ b/udev_utils_run.c @@ -29,13 +29,11 @@ #include #include #include +#include -#include "udev_libc_wrapper.h" #include "udev.h" -#include "logging.h" -#include "udev_utils.h" -#include "list.h" +extern char **environ; int pass_env_to_socket(const char *sockname, const char *devpath, const char *action) { @@ -45,13 +43,14 @@ int pass_env_to_socket(const char *sockname, const char *devpath, const char *ac char buf[2048]; size_t bufpos = 0; int i; - int retval; + ssize_t count; + int retval = 0; dbg("pass environment to socket '%s'", sockname); sock = socket(AF_LOCAL, SOCK_DGRAM, 0); memset(&saddr, 0x00, sizeof(struct sockaddr_un)); saddr.sun_family = AF_LOCAL; - /* only abstract namespace is supported */ + /* abstract namespace only */ strcpy(&saddr.sun_path[1], sockname); addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1; @@ -62,39 +61,41 @@ int pass_env_to_socket(const char *sockname, const char *devpath, const char *ac bufpos++; } - retval = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen); - if (retval != -1) - retval = 0; + count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen); + if (count < 0) + retval = -1; + info("passed %zi bytes to socket '%s', ", count, sockname); close(sock); return retval; } -int execute_program(const char *command, const char *subsystem, - char *result, size_t ressize, size_t *reslen) +int run_program(const char *command, const char *subsystem, + char *result, size_t ressize, size_t *reslen, int log) { int retval = 0; - int count; int status; - int pipefds[2]; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; pid_t pid; - char *pos; char arg[PATH_SIZE]; + char program[PATH_SIZE]; char *argv[(sizeof(arg) / 2) + 1]; int devnull; int i; - size_t len; + /* build argv from comand */ strlcpy(arg, command, sizeof(arg)); i = 0; - if (strchr(arg, ' ')) { - pos = arg; + if (strchr(arg, ' ') != NULL) { + char *pos = arg; + while (pos != NULL) { if (pos[0] == '\'') { /* don't separate if in apostrophes */ pos++; argv[i] = strsep(&pos, "\'"); - while (pos && pos[0] == ' ') + while (pos != NULL && pos[0] == ' ') pos++; } else { argv[i] = strsep(&pos, " "); @@ -102,78 +103,174 @@ int execute_program(const char *command, const char *subsystem, dbg("arg[%i] '%s'", i, argv[i]); i++; } - argv[i] = NULL; - dbg("execute '%s' with parsed arguments", arg); + argv[i] = NULL; } else { argv[0] = arg; - argv[1] = (char *) subsystem; - argv[2] = NULL; - dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); + argv[1] = NULL; } + info("'%s'", command); - if (result) { - if (pipe(pipefds) != 0) { - err("pipe failed"); + /* prepare pipes from child to parent */ + if (result || log) { + if (pipe(outpipe) != 0) { + err("pipe failed: %s", strerror(errno)); + return -1; + } + } + if (log) { + if (pipe(errpipe) != 0) { + err("pipe failed: %s", strerror(errno)); return -1; } } + /* allow programs in /lib/udev called without the path */ + if (strchr(argv[0], '/') == NULL) { + strlcpy(program, "/lib/udev/", sizeof(program)); + strlcat(program, argv[0], sizeof(program)); + argv[0] = program; + } + pid = fork(); switch(pid) { case 0: - /* child dup2 write side of pipe to STDOUT */ + /* 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) { + if (devnull > 0) { dup2(devnull, STDIN_FILENO); - if (!result) + if (outpipe[WRITE_END] < 0) dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); + if (errpipe[WRITE_END] < 0) + dup2(devnull, STDERR_FILENO); close(devnull); - } - if (result) - dup2(pipefds[1], STDOUT_FILENO); - execv(arg, argv); - err("exec of program failed"); + } else + err("open /dev/null failed: %s", strerror(errno)); + if (outpipe[WRITE_END] > 0) + dup2(outpipe[WRITE_END], STDOUT_FILENO); + if (errpipe[WRITE_END] > 0) + dup2(errpipe[WRITE_END], STDERR_FILENO); + execv(argv[0], argv); + + /* we should never reach this */ + err("exec of program '%s' failed", argv[0]); _exit(1); case -1: - err("fork of '%s' failed", arg); + err("fork of '%s' failed: %s", argv[0], strerror(errno)); return -1; default: - /* parent reads from pipefds[0] */ - if (result) { - close(pipefds[1]); - len = 0; - while (1) { - count = read(pipefds[0], result + len, ressize - len-1); - if (count < 0) { - err("read failed with '%s'", strerror(errno)); + /* 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; retval = -1; break; } - if (count == 0) - break; + /* get stdout */ + if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { + char inbuf[1024]; + char *pos; + char *line; - len += count; - if (len >= ressize-1) { - err("ressize %ld too short", (long)ressize); - retval = -1; - break; + count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); + if (count <= 0) { + close(outpipe[READ_END]); + outpipe[READ_END] = -1; + if (count < 0) { + err("stdin read failed: %s", strerror(errno)); + retval = -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("ressize %ld too short", (long)ressize); + retval = -1; + } + } + pos = inbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info("'%s' (stdout) '%s'", 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("stderr read failed: %s", strerror(errno)); + continue; + } + errbuf[count] = '\0'; + pos = errbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info("'%s' (stderr) '%s'", argv[0], line); } } - result[len] = '\0'; - close(pipefds[0]); - if (reslen) - *reslen = len; + 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("result='%s'", result); + if (reslen) + *reslen = respos; + } } waitpid(pid, &status, 0); - - if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { - dbg("exec program status 0x%x", status); + if (WIFEXITED(status)) { + info("'%s' returned with status %i", argv[0], WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + retval = -1; + } else { + err("'%s' abnormal exit", argv[0]); retval = -1; } } return retval; } -