static const char dcc_safeguard_name[] = "_DISTCC_SAFEGUARD";
static char dcc_safeguard_set[] = "_DISTCC_SAFEGUARD=1";
static int dcc_safeguard_level;
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.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 "io.h"
#include "util.h"
#include "exitcode.h"
#include "exec.h"
#include "lock.h"
#include "indirect_server.h"
#ifndef WCOREDUMP
# define WCOREDUMP(status) 0
#endif
void dcc_note_execution(const char *hostname, char **argv)
{
char *astr;
astr = dcc_argv_tostr(argv);
rs_log(RS_LOG_INFO|RS_LOG_NONAME, "exec on %s: %s", hostname, 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;
}
int dcc_remove_if_exists(const char *fname)
{
if (unlink(fname) && errno != ENOENT) {
rs_log_warning("failed to unlink %s: %s", fname,
strerror(errno));
return -1;
}
return 0;
}
void dcc_execvp(char **argv)
{
execvp(argv[0], argv);
rs_log_error("failed to exec %s: %s", argv[0], strerror(errno));
dcc_exit(EXIT_COMPILER_MISSING);
}
int dcc_recursion_safeguard(void)
{
char *env = getenv(dcc_safeguard_name);
if (env) {
rs_trace("safeguard: %s", env);
if (!(dcc_safeguard_level = atoi(env)))
dcc_safeguard_level = 1;
}
else
dcc_safeguard_level = 0;
rs_trace("safeguard level=%d", dcc_safeguard_level);
return dcc_safeguard_level;
}
static int 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)))
return ret;
if (dcc_safeguard_level > 0)
dcc_safeguard_set[sizeof dcc_safeguard_set-2] = dcc_safeguard_level+'1';
rs_trace("setting safeguard: %s", dcc_safeguard_set);
if ((putenv(strdup(dcc_safeguard_set)) == -1)) {
rs_log_error("putenv failed");
}
if ((ret = dcc_redirect_fds(stdin_file, stdout_file, stderr_file)))
return ret;
dcc_execvp(argv);
}
void dcc_setpgid(pid_t pid, pid_t pgid)
{
if (setpgid(pid, pgid) == 0)
rs_trace("setpgid(%ld, %ld)", (long) pid, (long) pgid);
else
rs_trace("setpgid(%ld, %ld) failed: %s",
(long) pid, (long) pgid, strerror(errno));
}
int dcc_spawn_child(char **argv, pid_t *pidptr,
const char *stdin_file,
const char *stdout_file,
const char *stderr_file)
{
pid_t pid;
dcc_trace_argv("forking to execute", argv);
pid = fork();
if (pid == -1) {
rs_log_error("failed to fork: (%d) %s", errno, strerror(errno));
return -1;
} else if (pid == 0) {
#if defined(DARWIN)
dcc_close_pipe_end_child();
#endif // DARWIN
exit(dcc_inside_child(argv, stdin_file, stdout_file, stderr_file));
} else {
*pidptr = pid;
rs_trace("child started as pid%d", (int) pid);
#if defined(DARWIN)
dcc_close_pipe_end_parent();
#endif // DARWIN
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(pid_t pid, int *wait_status, long *u_us, long *s_us)
{
struct rusage ru;
pid_t ret_pid;
while (1) {
if ((ret_pid = sys_wait4(pid, wait_status, 0, &ru)) != -1) {
rs_trace("child %ld terminated with status %#x",
(long) ret_pid, *wait_status);
*u_us = ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec;
*s_us = ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec;
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 -1;
}
}
}
int dcc_report_rusage(const char *command,
long utime_usec,
long stime_usec)
{
rs_log_info("%s resource usage: %ld.%06lds user, %ld.%06lds system",
command, utime_usec/1000000, utime_usec%1000000,
stime_usec/1000000, stime_usec%1000000);
return 0;
}
int dcc_critique_status(int s, const char *command,
const char *hostname)
{
if (WIFSIGNALED(s)) {
rs_log_error("%s on %s died with signal %d%s",
command, hostname,
WTERMSIG(s),
WCOREDUMP(s) ? " (core dumped)" : "");
return 128 + WTERMSIG(s);
} else if (WEXITSTATUS(s)) {
rs_log_notice("%s on %s failed with exit code %d",
command, hostname, WEXITSTATUS(s));
return WEXITSTATUS(s);
} else {
rs_log(RS_LOG_INFO|RS_LOG_NONAME,
"%s on %s completed ok", command, hostname);
return 0;
}
}