#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
#include <sys/param.h>
#include <sys/socket.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "rpc.h"
#include "exitcode.h"
#include "snprintf.h"
#include "dopt.h"
#include "bulk.h"
#include "exec.h"
#include "srvnet.h"
#include "hosts.h"
#include "daemon.h"
#include "versinfo.h"
extern int dcc_ensure_pch_cache_size_mb(int min_size_in_mb);
static int dcc_compile_log_fd = -1;
static int dcc_run_job(int in_fd, int out_fd);
static int dcc_add_log_to_file(const char *err_fname)
{
if (dcc_compile_log_fd != -1) {
rs_log_crit("compile log already open?");
return 0;
}
dcc_compile_log_fd = open(err_fname, O_WRONLY|O_CREAT|O_TRUNC, 0600);
if (dcc_compile_log_fd == -1) {
rs_log_error("failed to open %s: %s", err_fname, strerror(errno));
return EXIT_IO_ERROR;
}
rs_add_logger(rs_logger_file, RS_LOG_WARNING, NULL, dcc_compile_log_fd);
return 0;
}
static int dcc_remove_log_to_file(void)
{
if (dcc_compile_log_fd == -1) {
rs_log_warning("compile log not open?");
return 0;
}
rs_remove_logger(rs_logger_file, RS_LOG_WARNING, NULL,
dcc_compile_log_fd);
dcc_close(dcc_compile_log_fd);
dcc_compile_log_fd = -1;
return 0;
}
int dcc_service_job(int in_fd,
int out_fd,
struct sockaddr *cli_addr,
int cli_len)
{
int ret;
if ((ret = dcc_check_client(cli_addr, cli_len, opt_allowed)) != 0)
goto out;
if ((ret = dcc_ensure_free_space()) != 0)
goto out;
ret = dcc_run_job(in_fd, out_fd);
out:
return ret;
}
static int dcc_input_tmpnam(char * orig_input,
char **tmpnam_ret)
{
const char *input_exten;
rs_trace("input file %s", orig_input);
input_exten = dcc_find_extension(orig_input);
if (input_exten)
input_exten = dcc_preproc_exten(input_exten);
if (!input_exten)
input_exten = ".tmp";
return dcc_make_tmpnam("distccd", input_exten, tmpnam_ret);
}
static int dcc_check_compiler_masq(char *compiler_name)
{
const char *envpath, *p, *n;
char *buf = NULL;
struct stat sb;
int len;
char linkbuf[MAXPATHLEN];
if (compiler_name[0] == '/')
return 0;
if (!(envpath = getenv("PATH"))) {
rs_trace("PATH seems not to be defined");
return 0;
}
for (n = p = envpath; *n; p = n) {
n = strchr(p, ':');
if (n)
len = n++ - p;
else {
len = strlen(p);
n = p + len;
}
if (asprintf(&buf, "%.*s/%s", len, p, compiler_name) == -1) {
rs_log_crit("asnprintf failed");
return EXIT_DISTCC_FAILED;
}
if (lstat(buf, &sb) == -1)
continue;
if (!S_ISLNK(sb.st_mode)) {
rs_trace("%s is not a symlink", buf);
break;
}
if ((len = readlink(buf, linkbuf, sizeof linkbuf)) <= 0)
continue;
linkbuf[len] = '\0';
if (strstr(linkbuf, "distcc")) {
rs_log_warning("%s on distccd's path is %s and really a link to %s",
compiler_name, buf, linkbuf);
break;
} else {
rs_trace("%s is a safe symlink to %s", buf, linkbuf);
break;
}
}
free(buf);
return 0;
}
static int dcc_send_system_info(int out_fd)
{
char *info = dcc_get_system_version();
if (!info)
info = "";
return dcc_x_token_string(out_fd, "SINF", info);
}
static int dcc_send_compiler_version(int out_fd, char *compiler)
{
char *info = dcc_get_compiler_version(compiler);
if (!info)
info = "";
return dcc_x_token_string(out_fd, "CVER", info);
}
int dcc_send_host_info(int out_fd)
{
int ret;
char *sysKey = "SYSTEM=";
char *compilerKey = "COMPILER=";
char *ncpusKey = "CPUS=";
char *cpuSpeedKey = "CPUSPEED=";
char *maxJobsKey = "JOBS=";
char *priorityKey = "PRIORITY=";
char *distcc = "DISTCC=" PACKAGE_VERSION "\n";
char *sysInfo = dcc_get_system_version();
char **compilers = dcc_get_all_compiler_versions();
int ncpus;
unsigned long long cpuSpeed;
if (dcc_ncpus(&ncpus))
ncpus = 0;
if (dcc_cpuspeed(&cpuSpeed))
cpuSpeed = 0;
int i, len = 0;
char *msg;
if (sysInfo) {
len += strlen(sysInfo) + strlen(sysKey) + 1;
}
if (distcc)
len += strlen(distcc);
if (compilers) {
for (i=0; compilers && compilers[i] != NULL; i++) {
len += strlen(compilers[i]) + strlen(compilerKey) + 1;
}
}
if (ncpus > 0) {
len += strlen(ncpusKey) + 8; }
if (cpuSpeed > 0) {
len += strlen(cpuSpeedKey) + 32; }
if (dcc_max_kids > 0) {
len += strlen(maxJobsKey) + 8; }
if (build_machine_priority > 0) {
len += strlen(maxJobsKey) + 32; }
msg = malloc(len+1);
msg[0] = 0;
if (sysInfo) {
strcat(msg, sysKey);
strcat(msg, sysInfo);
strcat(msg, "\n");
}
if (distcc)
strcat(msg, distcc);
if (compilers) {
for (i=0; compilers && compilers[i]!=NULL; i++) {
strcat(msg, compilerKey);
strcat(msg, compilers[i]);
strcat(msg, "\n");
}
free(compilers);
}
if (ncpus > 0) {
strcat(msg, ncpusKey);
sprintf(&msg[strlen(msg)], "%d\n", ncpus);
}
if (cpuSpeed > 0) {
strcat(msg, cpuSpeedKey);
sprintf(&msg[strlen(msg)], "%llu\n", cpuSpeed);
}
if (dcc_max_kids > 0) {
strcat(msg, maxJobsKey);
sprintf(&msg[strlen(msg)], "%d\n", dcc_max_kids);
}
if (build_machine_priority > 0) {
strcat(msg, priorityKey);
sprintf(&msg[strlen(msg)], "%d\n", build_machine_priority);
}
if (out_fd == 1)
ret = write(out_fd, msg, len)==len;
else
ret = dcc_x_token_string(out_fd, "HINF", msg);
free(msg);
return ret;
}
static int dcc_run_job(int in_fd,
int out_fd)
{
char **argv;
int status;
char *temp_i, *temp_o, *err_fname, *out_fname;
int ret, compile_ret;
char *orig_input, *orig_output;
pid_t cc_pid;
enum dcc_protover protover;
enum dcc_compress compr;
dcc_indirection indirect;
if ((ret = dcc_make_tmpnam("distcc", ".stderr", &err_fname)))
goto out_cleanup;
if ((ret = dcc_make_tmpnam("distcc", ".stdout", &out_fname)))
goto out_cleanup;
dcc_remove_if_exists(err_fname);
dcc_remove_if_exists(out_fname);
dcc_add_log_to_file(err_fname);
dcc_ignore_sigpipe(1);
tcp_cork_sock(out_fd, 1);
if ((ret = dcc_r_request_header(in_fd, &protover))
|| (ret = dcc_r_argv(in_fd, &argv))
|| (ret = dcc_scan_args(argv, &orig_input, &orig_output, &argv)))
goto out_cleanup;
if (strcmp(argv[0],"--host-info") == 0) {
if ((ret = dcc_x_result_header(out_fd, protover)) ||
(ret = dcc_send_host_info(out_fd)))
dcc_x_token_int(out_fd, "DOTO", 0);
} else {
rs_trace("output file %s", orig_output);
if ((ret = dcc_input_tmpnam(orig_input, &temp_i)))
goto out_cleanup;
if ((ret = dcc_make_tmpnam("distccd", ".o", &temp_o)))
goto out_cleanup;
compr = (protover == 2) ? DCC_COMPRESS_LZO1X : DCC_COMPRESS_NONE;
if ((ret = dcc_r_token_file(in_fd, "DOTI", temp_i, compr))
|| (ret = dcc_set_input(argv, temp_i))
|| (ret = dcc_set_output(argv, temp_o)))
goto out_cleanup;
if ((ret = dcc_check_compiler_masq(argv[0])))
goto out_cleanup;
indirect.in_fd = in_fd;
indirect.out_fd = out_fd;
if ((compile_ret = dcc_spawn_child(argv, &cc_pid,
"/dev/null", out_fname, err_fname, &indirect))
|| (compile_ret = dcc_collect_child("cc", cc_pid, &status))) {
status = W_EXITCODE(compile_ret, 0);
}
if ((ret = dcc_x_result_header(out_fd, protover))
|| (ret = dcc_send_system_info(out_fd))
|| (ret = dcc_send_compiler_version(out_fd, argv[0]))
|| (ret = dcc_x_cc_status(out_fd, status))
|| (ret = dcc_x_file(out_fd, err_fname, "SERR", compr, NULL))
|| (ret = dcc_x_file(out_fd, out_fname, "SOUT", compr, NULL))
|| WIFSIGNALED(status)
|| WEXITSTATUS(status)) {
dcc_x_token_int(out_fd, "DOTO", 0);
} else {
ret = dcc_x_file(out_fd, temp_o, "DOTO", compr, NULL);
}
dcc_critique_status(status, argv[0], orig_input, dcc_hostdef_local, 0);
}
tcp_cork_sock(out_fd, 0);
rs_log(RS_LOG_INFO|RS_LOG_NONAME, "job complete");
out_cleanup:
dcc_remove_log_to_file();
dcc_cleanup_tempfiles();
return ret;
}