#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include "distcc.h"
#include "trace.h"
#include "rpc.h"
#include "exitcode.h"
#include "util.h"
#include "clinet.h"
#include "hosts.h"
#include "exec.h"
#include "lock.h"
#include "compile.h"
#include "bulk.h"
static int dcc_remote_connect(struct dcc_hostdef *host,
int *to_net_fd,
int *from_net_fd,
pid_t *ssh_pid)
{
int ret;
if (host->mode == DCC_MODE_TCP) {
*ssh_pid = 0;
if ((ret = dcc_connect_by_name(host->hostname, host->port,
to_net_fd)) != 0)
return ret;
*from_net_fd = *to_net_fd;
return 0;
} else if (host->mode == DCC_MODE_SSH) {
if ((ret = dcc_ssh_connect(NULL, host->user, host->hostname,
host->ssh_command,
from_net_fd, to_net_fd,
ssh_pid)))
return ret;
return 0;
} else {
rs_log_crit("impossible host mode");
return EXIT_DISTCC_FAILED;
}
}
static int dcc_wait_for_cpp(pid_t cpp_pid,
int *status,
const char *input_fname)
{
int ret;
if (cpp_pid) {
dcc_note_state(DCC_PHASE_CPP, NULL, NULL);
if ((ret = dcc_collect_child("cpp", cpp_pid, status, timeout_null_fd)))
return ret;
if (dcc_critique_status(*status, "cpp", input_fname, dcc_hostdef_local, 0))
return 0;
}
return 0;
}
static const char *dcc_map_optx_language_for_cpp(const char *language, int use_new_names) {
static const char* keys[] = {
"c",
"c++",
"objective-c",
"objective-c++",
"cpp-output",
"c++-cpp-output",
"objc-cpp-output",
"objc++-cpp-output",
"objective-c-cpp-output",
"objective-c++-cpp-output",
NULL};
static const char* old_transforms[] = {
"cpp-output",
"c++-cpp-output",
"objc-cpp-output",
"objc++-cpp-output",
"cpp-output",
"c++-cpp-output",
"objc-cpp-output",
"objc++-cpp-output",
"objc-cpp-output",
"objc++-cpp-output",
NULL};
static const char* new_transforms[] = {
"cpp-output",
"c++-cpp-output",
"objective-c-cpp-output",
"objective-c++-cpp-output",
"cpp-output",
"c++-cpp-output",
"objective-c-cpp-output",
"objective-c++-cpp-output",
"objective-c-cpp-output",
"objective-c++-cpp-output",
NULL};
char** transform = (char**)(use_new_names ? new_transforms : old_transforms);
char* cur_key = (char*)keys[0];
unsigned int i = 0;
do
{
if(!strcmp(language, cur_key)) {
rs_trace("mapped cpp language (new style: %i) %s to %s", use_new_names, language, transform[i]);
return transform[i];
}
}
while((cur_key = (char*)keys[++i]) != NULL);
return NULL;
}
static int
dcc_send_header(int net_fd,
char **argv,
struct dcc_hostdef *host)
{
int ret, i;
char **new_argv = NULL;
const char *new_lang;
if ((ret = dcc_copy_argv(argv, &new_argv, 0)))
return ret;
if ((ret = dcc_xci_mask_developer_dir_in_argv(new_argv)))
return ret;
tcp_cork_sock(net_fd, 1);
if ((ret = dcc_x_req_header(net_fd, host->protover)))
return ret;
if (host->cpp_where == DCC_CPP_ON_SERVER) {
if ((ret = dcc_x_cwd(net_fd)))
goto out_error;
} else if (host->cpp_where == DCC_CPP_ON_CLIENT) {
int using_clang = 0;
for (i = 0; new_argv[i]; i++) {
if (strstr(new_argv[i], "/usr/bin/clang")) using_clang = 1;
if (!strcmp(new_argv[i], "-x") && new_argv[i+1]) {
new_lang = dcc_map_optx_language_for_cpp(new_argv[i+1], using_clang);
if (!new_lang) {
rs_log_error("got unsupported -x language: %s", new_argv[i+1]);
ret = EXIT_DISTCC_FAILED;
goto out_error;
}
new_argv[i+1] = strdup(new_lang);
if (!new_argv[i+1]) {
rs_log_error("failed to duplicate string");
ret = EXIT_OUT_OF_MEMORY;
goto out_error;
}
break;
}
}
}
if ((ret = dcc_x_argv(net_fd, new_argv))) {
goto out_error;
}
dcc_free_argv(new_argv);
return 0;
out_error:
if (new_argv)
dcc_free_argv(new_argv);
return ret;
}
int dcc_compile_remote(char **argv,
char *input_fname,
char *cpp_fname,
char **files,
char *output_fname,
char *deps_fname,
char *server_stderr_fname,
pid_t cpp_pid,
int local_cpu_lock_fd,
struct dcc_hostdef *host,
int *status)
{
int to_net_fd = -1, from_net_fd = -1;
int ret;
pid_t ssh_pid = 0;
int ssh_status;
off_t doti_size;
struct timeval before, after;
unsigned int n_files;
if (gettimeofday(&before, NULL))
rs_log_warning("gettimeofday failed");
dcc_note_execution(host, argv);
dcc_note_state(DCC_PHASE_CONNECT, input_fname, host->hostname);
*status = 0;
if ((ret = dcc_remote_connect(host, &to_net_fd, &from_net_fd, &ssh_pid)))
goto out;
dcc_note_state(DCC_PHASE_SEND, NULL, NULL);
if (host->cpp_where == DCC_CPP_ON_SERVER) {
if ((ret = dcc_send_header(to_net_fd, argv, host))) {
goto out;
}
n_files = dcc_argv_len(files);
if ((ret = dcc_x_many_files(to_net_fd, n_files, files))) {
goto out;
}
} else {
if ((ret = dcc_send_header(to_net_fd, argv, host)))
goto out;
if ((ret = dcc_wait_for_cpp(cpp_pid, status, input_fname)))
goto out;
if (local_cpu_lock_fd != -1) {
dcc_unlock(local_cpu_lock_fd);
local_cpu_lock_fd = -1;
}
if (*status != 0)
goto out;
if ((ret = dcc_x_file(to_net_fd, cpp_fname, "DOTI", host->compr,
&doti_size)))
goto out;
}
rs_trace("client finished sending request to server");
tcp_cork_sock(to_net_fd, 0);
dcc_note_state(DCC_PHASE_COMPILE, NULL, host->hostname);
if (ret == 0 && *status == 0) {
ret = dcc_retrieve_results(from_net_fd, status, output_fname,
deps_fname, server_stderr_fname, host);
}
if (gettimeofday(&after, NULL)) {
rs_log_warning("gettimeofday failed");
} else if (host->cpp_where == DCC_CPP_ON_CLIENT) {
double secs, rate;
dcc_calc_rate(doti_size, &before, &after, &secs, &rate);
rs_log(RS_LOG_INFO|RS_LOG_NONAME,
"%lu bytes from %s compiled on %s in %.4fs, rate %.0fkB/s",
(unsigned long) doti_size, input_fname, host->hostname,
secs, rate);
}
out:
if (local_cpu_lock_fd != -1) {
dcc_unlock(local_cpu_lock_fd);
local_cpu_lock_fd = -1;
}
if (to_net_fd != from_net_fd) {
if (to_net_fd != -1)
dcc_close(to_net_fd);
}
if (from_net_fd != -1)
dcc_close(from_net_fd);
if (ssh_pid) {
dcc_collect_child("ssh", ssh_pid, &ssh_status, timeout_null_fd);
}
return ret;
}
#ifdef XCODE_INTEGRATION
int dcc_show_host_info(char *host)
{
int to_net_fd = -1, from_net_fd = -1;
int ret;
pid_t ssh_pid = 0;
int ssh_status;
char *info;
struct dcc_hostdef *hostdef;
int n_hosts = 0;
const char *argv[] = { "--host-info", NULL };
if ((ret = dcc_parse_hosts(host, "command line",
&hostdef, &n_hosts, NULL))) {
rs_log_error("bad host argument: %s", host);
return ret;
}
if (n_hosts != 1) {
rs_log_error("too many hosts for --host-info %s", host);
return EXIT_BAD_ARGUMENTS;
}
if ((ret = dcc_remote_connect(hostdef, &to_net_fd,
&from_net_fd, &ssh_pid))) {
rs_log_error("couldn't connect to %s", host);
printf("ERROR=%d\n", errno);
return ret;
}
if ((ret = dcc_send_header(to_net_fd, (char**)argv, hostdef)) != 0) {
rs_log_error("failed to send request");
printf("ERROR=%d\n", errno);
goto out;
}
tcp_cork_sock(to_net_fd, 0);
if ((ret = dcc_r_result_header(from_net_fd, hostdef->protover)))
goto out;
if ((ret = dcc_r_token_string(from_net_fd, "HINF", &info))) {
rs_log_error("failed to read result");
printf("ERROR=%d\n", errno);
}
out:
if (to_net_fd != from_net_fd) {
if (to_net_fd != -1)
dcc_close(to_net_fd);
}
if (from_net_fd != -1)
dcc_close(from_net_fd);
if (ssh_pid) {
dcc_collect_child("ssh", ssh_pid, &ssh_status, timeout_null_fd);
}
if (!ret)
printf("%s\n", info);
return ret;
}
#endif