/* 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;
}