#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.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 "zeroconf_client.h"
const int dcc_default_port = DISTCC_DEFAULT_PORT;
int dcc_parse_hosts_env(struct dcc_hostdef **ret_list,
int *ret_nhosts)
{
char *where;
where = getenv("DISTCC_HOSTS");
#if defined(DARWIN)
if (!where) {
dcc_zc_get_resolved_services_list(&where);
}
#endif // DARWIN
if (!where) {
#if defined(DARWIN)
rs_log_error("Unable to use zeroconfig; can't distribute work");
#else
rs_log_warning("$DISTCC_HOSTS is not defined; can't distribute work");
#endif // DARWIN
return EXIT_BAD_HOSTSPEC;
}
return dcc_parse_hosts(where, ret_list, ret_nhosts);
}
static int dcc_dup_part(const char **psrc, char **pdst, const char *sep)
{
int len;
len = strcspn(*psrc, sep);
if (len == 0) {
*pdst = NULL;
} else {
if (!(*pdst = malloc(len + 1))) {
rs_log_error("failed to allocate string duplicate: %d", (int) len);
return EXIT_OUT_OF_MEMORY;
}
strncpy(*pdst, *psrc, len);
(*pdst)[len] = '\0';
(*psrc) += len;
}
return 0;
}
static int dcc_parse_multiplier(const char **psrc, struct dcc_hostdef *hostdef)
{
char *mul;
const char *token = *psrc;
int ret;
if ((*psrc)[0] == '/') {
(*psrc)++;
if ((ret = dcc_dup_part(psrc, &mul, "/: \t\n\f")) != 0)
return ret;
if (!mul || atoi(mul) == 0) {
rs_log_error("bad multiplier \"%s\" in host specification", token);
return EXIT_BAD_HOSTSPEC;
}
hostdef->n_slots = atoi(mul);
}
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;
assert(token[0] == '@');
token++;
if ((ret = dcc_dup_part(&token, &hostdef->hostname, "/: \t\n\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\f")) != 0)
return ret;
}
hostdef->mode = DCC_MODE_SSH;
return 0;
}
static int dcc_parse_tcp_host(struct dcc_hostdef *hostdef,
const char * const token_start)
{
char *port_str;
int ret;
const char *token = token_start;
if ((ret = dcc_dup_part(&token, &hostdef->hostname, "/: \t\n\f")) != 0)
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] == ':') {
token++;
if ((ret = dcc_dup_part(&token, &port_str, " \t\n\f")) != 0)
return ret;
if (port_str) {
char *tail;
hostdef->port = strtol(port_str, &tail, 10);
if (*tail != '\0' && !isspace(*tail)) {
rs_log_error("invalid tcp port specification in \"%s\"", port_str);
return EXIT_BAD_HOSTSPEC;
}
free(port_str);
}
}
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 = 1;
return dcc_parse_multiplier(&token, hostdef);
}
int dcc_parse_hosts(const char *where,
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;
while (isspace(where[0]))
where++;
if (where[0] == '\0')
break;
token_start = where;
token_len = strcspn(where, " \t\n\f");
curr = calloc(1, sizeof(struct dcc_hostdef));
if (!curr) {
rs_log_crit("failed to allocate host definition");
return EXIT_OUT_OF_MEMORY;
}
if (prev) {
prev->next = curr;
} else {
*ret_list = curr;
}
curr->n_slots = 4;
has_at = (memchr(token_start, '@', token_len) != NULL);
if (!strncmp(token_start, "localhost", token_len)
|| !strncmp(token_start, "localhost/", strlen("localhost/"))) {
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("$DISTCC_HOSTS is empty; can't distribute work");
return EXIT_BAD_HOSTSPEC;
}
}