#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "hosts.h"
#include "exitcode.h"
#include "snprintf.h"
const int dcc_default_port = DISTCC_DEFAULT_PORT;
#ifndef HAVE_STRNDUP
static char *strndup(const char *src, size_t size)
{
char *dst;
dst = malloc(size + 1);
if (dst == NULL)
return NULL;
strncpy(dst, src, size);
dst[size] = '\0';
return dst;
}
#endif
int dcc_get_hostlist(struct dcc_hostdef **ret_list,
int *ret_nhosts)
{
char *env;
char *path, *top;
int ret;
if ((env = getenv("DISTCC_HOSTS")) != NULL) {
rs_trace("read hosts from environment");
return dcc_parse_hosts(env, "$DISTCC_HOSTS", ret_list, ret_nhosts);
}
if ((ret = dcc_get_top_dir(&top)) == 0) {
asprintf(&path, "%s/hosts", top);
if (access(path, R_OK) == 0) {
ret = dcc_parse_hosts_file(path, ret_list, ret_nhosts);
free(path);
return ret;
} else {
rs_trace("not reading %s: %s", path, strerror(errno));
free(path);
}
}
asprintf(&path, "%s/distcc/hosts", SYSCONFDIR);
if (access(path, R_OK) == 0) {
ret = dcc_parse_hosts_file(path, ret_list, ret_nhosts);
free(path);
return ret;
} else {
rs_trace("not reading %s: %s", path, strerror(errno));
free(path);
}
rs_log_warning("no hostlist is set; can't distribute work");
return EXIT_BAD_HOSTSPEC;
}
static int dcc_parse_multiplier(const char **psrc, struct dcc_hostdef *hostdef)
{
const char *token = *psrc;
if ((*psrc)[0] == '/') {
int val;
(*psrc)++;
val = atoi(*psrc);
if (val == 0) {
rs_log_error("bad multiplier \"%s\" in host specification", token);
return EXIT_BAD_HOSTSPEC;
}
while (isdigit(**psrc))
(*psrc)++;
hostdef->n_slots = val;
}
return 0;
}
static int dcc_parse_options(const char **psrc,
struct dcc_hostdef *host)
{
const char *started = *psrc, *p = *psrc;
while (p[0] == ',') {
p++;
if (str_startswith("lzo", p)) {
rs_trace("got LZO option");
host->protover = DCC_VER_2;
host->compr = DCC_COMPRESS_LZO1X;
p += 3;
} else {
rs_log_warning("unrecognized option in host specification %s",
started);
return EXIT_BAD_HOSTSPEC;
}
}
*psrc = p;
return 0;
}
static int dcc_parse_ssh_host(struct dcc_hostdef *hostdef,
const char *token_start)
{
int ret;
const char *token = token_start;
if ((ret = dcc_dup_part(&token, &hostdef->user, "@")) != 0)
return ret;
if (token[0] != '@') {
rs_log_error("expected '@' to start ssh token");
return EXIT_BAD_HOSTSPEC;
}
token++;
if ((ret = dcc_dup_part(&token, &hostdef->hostname, "/: \t\n\r\f,")) != 0)
return ret;
if (!hostdef->hostname) {
rs_log_error("hostname is required in SSH host specification \"%s\"",
token_start);
return EXIT_BAD_HOSTSPEC;
}
if ((ret = dcc_parse_multiplier(&token, hostdef)) != 0)
return ret;
if (token[0] == ':') {
token++;
if ((ret = dcc_dup_part(&token, &hostdef->ssh_command, " \t\n\r\f,")))
return ret;
}
if ((ret = dcc_parse_options(&token, hostdef)))
return ret;
hostdef->mode = DCC_MODE_SSH;
return 0;
}
static int dcc_parse_tcp_host(struct dcc_hostdef *hostdef,
const char * const token_start)
{
int ret;
const char *token = token_start;
if ((ret = dcc_dup_part(&token, &hostdef->hostname, "/: \t\n\r\f,")))
return ret;
if (!hostdef->hostname) {
rs_log_error("hostname is required in tcp host specification \"%s\"",
token_start);
return EXIT_BAD_HOSTSPEC;
}
if ((ret = dcc_parse_multiplier(&token, hostdef)) != 0)
return ret;
hostdef->port = dcc_default_port;
if (token[0] == ':') {
char *tail;
token++;
hostdef->port = strtol(token, &tail, 10);
if (*tail != '\0' && !isspace(*tail) && *tail != '/' && *tail != ',') {
rs_log_error("invalid tcp port specification in \"%s\"", token);
return EXIT_BAD_HOSTSPEC;
} else {
token = tail;
}
}
if ((ret = dcc_parse_multiplier(&token, hostdef)) != 0)
return ret;
if ((ret = dcc_parse_options(&token, hostdef)))
return ret;
hostdef->mode = DCC_MODE_TCP;
return 0;
}
static int dcc_parse_localhost(struct dcc_hostdef *hostdef,
const char * token_start)
{
const char *token = token_start + strlen("localhost");
hostdef->mode = DCC_MODE_LOCAL;
hostdef->hostname = strdup("localhost");
hostdef->n_slots = 2;
return dcc_parse_multiplier(&token, hostdef);
}
int dcc_parse_hosts(const char *where, const char *source_name,
struct dcc_hostdef **ret_list,
int *ret_nhosts)
{
int ret;
struct dcc_hostdef *prev, *curr;
prev = NULL;
*ret_list = NULL;
*ret_nhosts = 0;
while (1) {
int token_len;
const char *token_start;
int has_at;
if (where[0] == '\0')
break;
if (where[0] == '#') {
do
where++;
while (where[0] != '\n' && where[0] != '\r' && where[0] != '\0');
continue;
}
if (isspace(where[0])) {
where++;
continue;
}
token_start = where;
token_len = strcspn(where, " #\t\n\f\r");
curr = calloc(1, sizeof(struct dcc_hostdef));
if (!curr) {
rs_log_crit("failed to allocate host definition");
return EXIT_OUT_OF_MEMORY;
}
if (!(curr->hostdef_string = strndup(token_start, (size_t) token_len))) {
rs_log_crit("failed to allocate hostdef_string");
return EXIT_OUT_OF_MEMORY;
}
if (prev) {
prev->next = curr;
} else {
*ret_list = curr;
}
curr->n_slots = 4;
curr->protover = DCC_VER_1;
curr->compr = DCC_COMPRESS_NONE;
has_at = (memchr(token_start, '@', (size_t) token_len) != NULL);
if (!strncmp(token_start, "localhost", 9)
&& (token_len == 9 || token_start[9] == '/')) {
rs_trace("found localhost token \"%.*s\"", token_len, token_start);
if ((ret = dcc_parse_localhost(curr, token_start)) != 0)
return ret;
} else if (has_at) {
rs_trace("found ssh token \"%.*s\"", token_len, token_start);
if ((ret = dcc_parse_ssh_host(curr, token_start)) != 0)
return ret;
} else {
rs_trace("found tcp token \"%.*s\"", token_len, token_start);
if ((ret = dcc_parse_tcp_host(curr, token_start)) != 0)
return ret;
}
where = token_start + token_len;
prev = curr;
(*ret_nhosts)++;
}
if (*ret_nhosts) {
return 0;
} else {
rs_log_warning("%s contained no hosts; can't distribute work", source_name);
return EXIT_BAD_HOSTSPEC;
}
}
int dcc_free_hostdef(struct dcc_hostdef *host)
{
free(host->user);
free(host->hostname);
free(host->ssh_command);
free(host->hostdef_string);
free(host->system_info);
free(host->compiler_vers);
memset(host, 0xf1, sizeof *host);
free(host);
return 0;
}