/* wydawca - automatic release submission daemon Copyright (C) 2007, 2009-2013 Sergey Poznyakoff Wydawca is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Wydawca is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with wydawca. If not, see . */ #include "wydawca.h" #include #include #include #include /* Execute a program from ARGC/ARGV. Return PID in PPID. Return FILE connected to the program's stdout and stderr. */ static FILE * start_prog(int argc, const char **argv, pid_t * ppid) { int p[2]; FILE *fp; pid_t pid; int i; if (pipe(p)) { wy_log(LOG_CRIT, "pipe: %s", strerror(errno)); return NULL; } switch (pid = fork()) { case 0: /* Child process */ if (p[1] != 1 && dup2(p[1], 1) == -1) { wy_log(LOG_CRIT, "dup2: %s", strerror(errno)); _exit(EX_UNAVAILABLE); } if (p[1] != 1 && dup2(p[1], 2) == -1) { wy_log(LOG_CRIT, "dup2: %s", strerror(errno)); _exit(EX_UNAVAILABLE); } close(p[0]); /* Close unneded descripitors */ for (i = getdtablesize(); i > 2; i--) close(i); execvp(argv[0], (char **)argv); wy_log(LOG_CRIT, _("cannot run %s: %s"), argv[0], strerror(errno)); exit(EX_UNAVAILABLE); case -1: wy_log(LOG_CRIT, _("cannot run `%s': fork failed: %s"), argv[0], strerror(errno)); return NULL; default: /* Master process */ close(p[1]); fp = fdopen(p[0], "r"); if (!fp) wy_log(LOG_ERR, _("cannot fdopen: %s"), strerror(errno)); *ppid = pid; } return fp; } /* Log everything read from FP as the output from the program PROG, using syslog priority PRIO. */ void log_output(int prio, const char *prog, FILE *fp) { size_t size = 0; char *buf = NULL; wy_log(prio, _("%s output follows:"), prog); while (grecs_getline(&buf, &size, fp) > 0) wy_log(prio, "%s", buf); wy_log(prio, _("end of %s output"), prog); free(buf); } /* Execute ARGC/ARGV. Return the exit code in RETCODE. */ enum exec_result wydawca_exec(int argc, const char **argv, int *retcode) { FILE *fp; pid_t pid, npid; int status; int i; enum exec_result res; fp = start_prog(5, argv, &pid); if (!fp) { wy_log(LOG_CRIT, _("cannot start %s"), argv[0]); return exec_error; } for (i = 0; i < 5 && (npid = waitpid(pid, &status, WNOHANG)) == 0; i++) sleep(1); switch (npid) { case -1: wy_log(LOG_CRIT, _("cannot execute %s: waitpid failed: %s"), argv[0], strerror(errno)); fclose(fp); return exec_error; case 0: wy_log(LOG_CRIT, _("cannot execute %s: the process did not respond " "within 5 seconds: %s"), argv[0], strerror(errno)); kill(pid, SIGKILL); fclose(fp); return exec_error; default: break; } if (WIFEXITED(status)) { int rc = WEXITSTATUS(status); if (rc) { res = exec_fail; wy_log(LOG_ERR, _("command %s returned %d"), argv[0], rc); log_output(LOG_ERR, argv[0], fp); } else { res = exec_success; if (wy_debug_level > 1) log_output(LOG_DEBUG, argv[0], fp); } if (retcode) *retcode = rc; } else { res = exec_error; if (WIFSIGNALED(status)) wy_log(LOG_ERR, _("%s terminated on signal %d"), argv[0], WTERMSIG(status)); else if (WIFSTOPPED(status)) wy_log(LOG_ERR, _("%s stopped on signal %d"), argv[0], WTERMSIG(status)); else wy_log(LOG_ERR, _("%s terminated with unrecognized status"), argv[0]); } fclose(fp); return res; }