#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "exitcode.h"
#include "exec.h"
#include "lock.h"
#include "hosts.h"
#include "versinfo.h"
static void dcc_inside_child(char **argv,
const char *stdin_file,
const char *stdout_file,
const char *stderr_file) NORETURN;
static void dcc_execvp(char **argv) NORETURN;
pid_t dcc_retry_fork()
{
pid_t pid;
int sleepCount;
pid = fork();
if (pid == -1 && errno == EAGAIN) {
for (sleepCount = 1; sleepCount < 10 && pid == -1 && errno == EAGAIN; sleepCount++) {
rs_log_warning("failed to fork, retry in %d seconds: %s", sleepCount, strerror(errno));
sleep(sleepCount);
pid = fork();
}
}
if (pid == -1)
rs_log_error("failed to fork, giving up: %s", strerror(errno));
return pid;
}
void dcc_note_execution(struct dcc_hostdef *host, char **argv)
{
char *astr;
astr = dcc_argv_tostr(argv);
rs_log(RS_LOG_INFO|RS_LOG_NONAME, "exec on %s: %s",
host->hostdef_string, astr);
free(astr);
}
int dcc_redirect_fds(const char *stdin_file,
const char *stdout_file,
const char *stderr_file)
{
int ret;
if (stdin_file)
if ((ret = dcc_redirect_fd(STDIN_FILENO, stdin_file, O_RDONLY)))
return ret;
if (stdout_file) {
if ((ret = dcc_redirect_fd(STDOUT_FILENO, stdout_file,
O_WRONLY | O_CREAT | O_TRUNC)))
return ret;
}
if (stderr_file) {
if ((ret = dcc_redirect_fd(STDERR_FILENO, stderr_file,
O_WRONLY | O_CREAT | O_APPEND)))
return ret;
}
return 0;
}
static void dcc_execvp(char **argv)
{
char *slash;
execvp(argv[0], argv);
slash = strrchr(argv[0], '/');
if (slash)
execvp(slash + 1, argv);
rs_log_error("failed to exec %s: %s", argv[0], strerror(errno));
dcc_exit(EXIT_COMPILER_MISSING);
}
static void dcc_inside_child(char **argv,
const char *stdin_file,
const char *stdout_file,
const char *stderr_file)
{
int ret;
if ((ret = dcc_ignore_sigpipe(0)))
goto fail;
dcc_increment_safeguard();
if ((ret = dcc_redirect_fds(stdin_file, stdout_file, stderr_file)))
goto fail;
dcc_execvp(argv);
ret = EXIT_DISTCC_FAILED;
fail:
dcc_exit(ret);
}
int dcc_new_pgrp(void)
{
if (getpgrp() == getpid()) {
rs_trace("already a process group leader");
return 0;
}
if (setpgid(0, 0) == 0) {
rs_trace("entered process group");
return 0;
} else {
rs_trace("setpgid(0, 0) failed: %s", strerror(errno));
return EXIT_DISTCC_FAILED;
}
}
int dcc_spawn_child(char **argv, pid_t *pidptr,
const char *stdin_file,
const char *stdout_file,
const char *stderr_file,
dcc_indirection *indirect)
{
pid_t pid;
dcc_trace_argv("forking to execute", argv);
if (indirect)
dcc_prepare_indirect(indirect);
char *compilerPath = dcc_get_allowed_compiler_for_path(argv[0]);
if (!compilerPath || strcmp(compilerPath, argv[0]) != 0) {
rs_log_error("attempt to use unknown compiler aborted: %s", argv[0]);
if (indirect)
dcc_indirect_parent(indirect);
return EXIT_DISTCC_FAILED;
}
pid = dcc_retry_fork();
if (pid == -1) {
rs_log_error("failed to fork: %s", strerror(errno));
return EXIT_OUT_OF_MEMORY;
} else if (pid == 0) {
if (indirect)
dcc_indirect_child(indirect);
dcc_inside_child(argv, stdin_file, stdout_file, stderr_file);
} else {
*pidptr = pid;
rs_trace("child started as pid%d", (int) pid);
if (indirect)
dcc_indirect_parent(indirect);
return 0;
}
}
void dcc_reset_signal(int whichsig)
{
struct sigaction act_dfl;
memset(&act_dfl, 0, sizeof act_dfl);
act_dfl.sa_handler = SIG_DFL;
sigaction(whichsig, &act_dfl, NULL);
}
static int sys_wait4(pid_t pid, int *status, int options, struct rusage *rusage)
{
#ifdef HAVE_WAIT4
return wait4(pid, status, options, rusage);
#elif HAVE_WAITPID
memset(rusage, 0, sizeof *rusage);
return waitpid(pid, status, options);
#else
#error Please port this
#endif
}
int dcc_collect_child(const char *what, pid_t pid,
int *wait_status)
{
struct rusage ru;
pid_t ret_pid;
while (1) {
if ((ret_pid = sys_wait4(pid, wait_status, 0, &ru)) != -1) {
rs_trace("%s child %ld terminated with status %#x",
what, (long) ret_pid, *wait_status);
rs_log_info("%s times: user %ld.%06lds, system %ld.%06lds, "
"%ld minflt, %ld majflt",
what,
ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
ru.ru_minflt, ru.ru_majflt);
return 0;
} else if (errno == EINTR) {
rs_trace("wait4 was interrupted; retrying");
continue;
} else {
rs_log_error("sys_wait4(pid=%d) borked: %s", (int) pid, strerror(errno));
return EXIT_DISTCC_FAILED;
}
}
}
int dcc_critique_status(int status,
const char *command,
const char *input_fname,
struct dcc_hostdef *host,
int verbose)
{
int logmode;
if (verbose)
logmode = RS_LOG_ERR | RS_LOG_NONAME;
else
logmode = RS_LOG_INFO | RS_LOG_NONAME;
if (WIFSIGNALED(status)) {
#ifdef HAVE_STRSIGNAL
rs_log(logmode,
"%s %s on %s:%s %s",
command, input_fname, host->hostdef_string,
strsignal(WTERMSIG(status)),
WCOREDUMP(status) ? " (core dumped)" : "");
#else
rs_log(logmode,
"%s %s on %s terminated by signal %d%s",
command, input_fname, host->hostdef_string,
WTERMSIG(status),
WCOREDUMP(status) ? " (core dumped)" : "");
#endif
return 128 + WTERMSIG(status);
} else if (WEXITSTATUS(status) == 1) {
rs_log(logmode, "%s %s on %s failed", command, input_fname, host->hostdef_string);
return WEXITSTATUS(status);
} else if (WEXITSTATUS(status)) {
rs_log(logmode,
"%s %s on %s failed with exit code %d",
command, input_fname, host->hostdef_string, WEXITSTATUS(status));
return WEXITSTATUS(status);
} else {
rs_log(RS_LOG_INFO|RS_LOG_NONAME,
"%s %s on %s completed ok", command, input_fname, host->hostdef_string);
return 0;
}
}