#include #include #include #include #include #include #include #include #include #include #include #include #include #include "exec.h" int log_exec(const char *dir, const char *prog, const char *arg) { pid_t pid; int p[2]; unsigned n = 0; static char buf[256]; int sigfd, r; fd_set fds; daemon_log(LOG_INFO, "Running '%s %s'", prog, arg); if (pipe(p) < 0) { daemon_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); return -1; } if ((pid = fork()) < 0) { daemon_log(LOG_ERR, "fork() failed: %s", strerror(errno)); return -1; } else if (pid == 0) { dup2(p[1], 1); dup2(p[1], 2); if (p[0] > 2) close(p[0]); if (p[1] > 2) close(p[1]); close(0); open("/dev/null", O_RDONLY); umask(0022); // Set up a sane umask if (dir && chdir(dir) < 0) { daemon_log(LOG_WARNING, "Failed to change to directory '%s'", dir); chdir("/"); } execl(prog, prog, arg, 0); daemon_log(LOG_ERR, "execl(%s) failed: %s\n", prog, strerror(errno)); _exit(EXIT_FAILURE); } close(p[1]); FD_ZERO(&fds); FD_SET(p[0], &fds); FD_SET(sigfd = daemon_signal_fd(), &fds); n = 0; for (;;) { fd_set qfds = fds; if (select(FD_SETSIZE, &qfds, NULL, NULL, NULL) < 0) { if (errno == EINTR) continue; daemon_log(LOG_ERR, "select() failed: %s", strerror(errno)); return -1; } if (FD_ISSET(p[0], &qfds)) { char c; if (read(p[0], &c, 1) != 1) break; buf[n] = c; if (c == '\n' || n >= sizeof(buf) - 2) { if (c != '\n') n++; buf[n] = 0; if (buf[0]) daemon_log(LOG_INFO, "client: %s", buf); n = 0; } else n++; } if (FD_ISSET(sigfd, &qfds)) { int sig; if ((sig = daemon_signal_next()) < 0) { daemon_log(LOG_ERR, "daemon_signal_next(): %s", strerror(errno)); break; } if (sig != SIGCHLD) { daemon_log(LOG_WARNING, "Killing child."); kill(pid, SIGTERM); } break; } } if (n > 0) { buf[n] = 0; daemon_log(LOG_WARNING, "client: %s", buf); } waitpid(pid, &r, 0); close(p[0]); if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) { daemon_log(LOG_ERR, "Program execution failed, return value is %i.", WEXITSTATUS(r)); return -1; } else { daemon_log(LOG_INFO, "Program executed successfully."); return 0; } }