#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 <sys/types.h>
#include <sys/stat.h>
#include "distcc.h"
#include "trace.h"
#include "io.h"
#include "util.h"
#include "exitcode.h"
#include "cpp_dialect.h"
static int dcc_argv_append(char *argv[], char *toadd)
{
int l = dcc_argv_len(argv);
argv[l] = toadd;
argv[l+1] = NULL;
return 0;
}
int dcc_trace_argv(const char *message, char *argv[])
{
if (rs_trace_enabled()) {
char *astr;
astr = dcc_argv_tostr(argv);
rs_trace("%s: %s", message, astr);
free(astr);
}
return 0;
}
int dcc_scan_args(char *argv[], char **input_file, char **output_file,
char ***ret_newargv)
{
int seen_opt_c = 0, seen_opt_s = 0;
int i;
char *a;
int ret;
if ((ret = dcc_shallowcopy_argv(argv, ret_newargv, 2)) != 0)
return ret;
argv = *ret_newargv;
dcc_trace_argv("scanning arguments", argv);
if (argv[0][0] == '-') {
rs_log_error("unrecognized distcc option: %s", argv[0]);
exit(EXIT_BAD_ARGUMENTS);
}
*input_file = *output_file = NULL;
for (i = 0; (a = argv[i]); i++) {
if (a[0] == '-') {
if (!strcmp(a, "-E")) {
rs_trace("-E call for cpp must be local");
return -1;
} else if (!strcmp(a, "-MD") || !strcmp(a, "-MMD") ||
!strcmp(a, "-MG") || !strcmp(a, "-MP")) {
;
} else if (!strcmp(a, "-MF") || !strcmp(a, "-MT") ||
!strcmp(a, "-MQ")) {
i++;
} else if (a[1] == 'M') {
rs_trace("%s implies -E (maybe) and must be local", a);
return -1;
} else if (!strcmp(a, "-S")) {
seen_opt_s = 1;
} else if (!strcmp(a, "-fprofile-arcs")
|| !strcmp(a, "-ftest-coverage")) {
rs_log_info("compiler will emit profile info; must be local");
return -1;
} else if (!strcmp(a, "-x")) {
a = argv[++i];
char *ext;
if (ext = ext_lookup(a)) {
opt_x_ext = ext;
seen_opt_x = 1;
} else {
rs_log_info("gcc's -x handling is complex; running locally");
return -1;
}
} else if (!strcmp(a, "-c")) {
seen_opt_c = 1;
} else if (!strcmp(a, "-o")) {
a = argv[++i];
goto GOT_OUTPUT;
} else if (str_startswith("-o", a)) {
a += 2;
goto GOT_OUTPUT;
}
} else {
if (dcc_is_source(a)) {
rs_trace("found input file \"%s\"", a);
if (*input_file) {
rs_log_info("do we have two inputs? i give up");
return -1;
}
*input_file = a;
} else if (str_endswith(".o", a)) {
GOT_OUTPUT:
rs_trace("found object/output file \"%s\"", a);
if (*output_file) {
rs_log_info("called for link? i give up");
return -1;
}
*output_file = a;
}
}
}
if (!seen_opt_c && !seen_opt_s) {
rs_log_info("compiler apparently called not for compile");
return -1;
}
if (!*input_file) {
rs_log_info("no visible input file");
return -1;
}
if (!*output_file) {
char *ofile;
if (seen_opt_s) {
if (dcc_output_from_source(*input_file, ".s", &ofile))
return -1;
} else if (seen_opt_c) {
if (dcc_output_from_source(*input_file, ".o", &ofile))
return -1;
} else {
rs_log_crit("this can't be happening(%d)!", __LINE__);
return -1;
}
rs_log_info("no visible output file, going to add \"-o %s\" at end",
ofile);
dcc_argv_append(argv, strdup("-o"));
dcc_argv_append(argv, ofile);
*output_file = ofile;
}
rs_log(RS_LOG_INFO|RS_LOG_NONAME, "compile from %s to %s", *input_file, *output_file);
if (strcmp(*output_file, "-") == 0) {
rs_log_info("output to stdout? running locally");
return -1;
}
return 0;
}
int
dcc_argv_len (char **a)
{
int i;
for (i = 0; a[i]; i++)
;
return i;
}
int dcc_shallowcopy_argv(char **from, char ***out, int delta)
{
char **b;
int l, i;
assert(out != NULL);
assert(from != NULL);
l = dcc_argv_len(from);
b = malloc((l+1+delta) * (sizeof from[0]));
if (b == NULL) {
rs_log_error("failed to allocate copy of argv");
exit(EXIT_FAILURE);
}
for (i = 0; i < l; i++) {
b[i] = from[i];
}
b[l] = NULL;
*out = b;
return 0;
}
int dcc_deepcopy_argv(char **from, char ***out)
{
char **b;
int i, l;
l = dcc_argv_len(from);
assert(out != NULL);
assert(from != NULL);
l = dcc_argv_len(from);
b = malloc((l+1) * (sizeof from[0]));
if (b == NULL) {
rs_log_error("failed to allocate copy of argv");
exit(EXIT_FAILURE);
}
for (i = 0; i < l; i++)
b[i] = strdup(from[i]);
b[l] = NULL;
*out = b;
return 0;
}
int dcc_set_action_opt(char **a, const char *new_c)
{
int gotone = 0;
for (; *a; a++)
if (!strcmp(*a, "-c") || !strcmp(*a, "-S")) {
*a = strdup(new_c);
if (*a == NULL) {
rs_log_error("strdup failed");
exit(EXIT_FAILURE);
}
gotone = 1;
}
if (!gotone) {
rs_log_error("failed to find -c or -S");
return -1;
} else {
return 0;
}
}
int dcc_set_output(char **a, char *ofname)
{
int i;
for (i = 0; a[i]; i++)
if (0==strcmp(a[i], "-o") && a[i+1] != NULL) {
rs_trace("changed output from \"%s\" to \"%s\"", a[i+1], ofname);
a[i+1] = ofname;
dcc_trace_argv("command after", a);
return 0;
}
rs_log_error("failed to find \"-o\"");
return -1;
}
int dcc_set_input(char **a, char *ifname)
{
int i;
for (i =0; a[i]; i++)
if (dcc_is_source(a[i])) {
rs_trace("changed input from \"%s\" to \"%s\"", a[i], ifname);
a[i] = ifname;
dcc_trace_argv("command after", a);
return 0;
}
rs_log_error("failed to find input file");
return -1;
}
char *dcc_argv_tostr(char **a)
{
int l, i;
char *s, *ss;
for (l = 0, i = 0; a[i]; i++) {
l += strlen(a[i]) + 3;
}
ss = s = malloc((size_t) l + 1);
if (!s) {
rs_log_crit("failed to allocate %d bytes", l+1);
exit(EXIT_OUT_OF_MEMORY);
}
for (i = 0; a[i]; i++) {
int needs_quotes = (strpbrk(a[i], " \t\n\"\';") != NULL);
if (i)
*ss++ = ' ';
if (needs_quotes)
*ss++ = '"';
strcpy(ss, a[i]);
ss += strlen(a[i]);
if (needs_quotes)
*ss++ = '"';
}
*ss = '\0';
return s;
}
static char **_dcc_allowed_compilers(void) {
static char **allowedCompilers = NULL;
static char *allowedCompilersStrings = NULL;
if (allowedCompilersStrings == NULL) {
#define COMPILERS_FILE_PATH "/etc/compilers"
rs_trace("parsing %s", COMPILERS_FILE_PATH);
int allowedCompilersFile = open(COMPILERS_FILE_PATH, O_RDONLY, 0);
if ( allowedCompilersFile < 0 ) {
rs_log_crit("failed to open() '%s'", COMPILERS_FILE_PATH);
exit(EXIT_DISTCC_FAILED);
}
struct stat allowedCompilersFileStatB;
if ( fstat( allowedCompilersFile, &allowedCompilersFileStatB) == -1 ) {
close( allowedCompilersFile );
rs_log_crit("failed to fstat() '%s'", COMPILERS_FILE_PATH);
exit(EXIT_DISTCC_FAILED);
}
allowedCompilersStrings = malloc( allowedCompilersFileStatB.st_size + 1 );
if ( allowedCompilersStrings == NULL ) {
rs_log_crit("failed to allocate buffer for '%s' content.", COMPILERS_FILE_PATH);
exit(EXIT_OUT_OF_MEMORY);
}
allowedCompilersStrings[allowedCompilersFileStatB.st_size] = '\0';
int maximumNumberOfCompilers = 10;
int currentCompilerSlot = 0;
allowedCompilers = calloc( maximumNumberOfCompilers + 1, sizeof(char *) );
if ( allowedCompilers == NULL ) {
close( allowedCompilersFile );
free( allowedCompilersStrings );
rs_log_crit("failed to allocate buffer for '%s' content [slots].", COMPILERS_FILE_PATH);
exit(EXIT_OUT_OF_MEMORY);
}
int dataRead = read( allowedCompilersFile, allowedCompilersStrings, allowedCompilersFileStatB.st_size );
if ( dataRead != allowedCompilersFileStatB.st_size ) {
close( allowedCompilersFile );
free( allowedCompilersStrings );
free( allowedCompilers );
rs_log_crit("failed to read '%s' content (read %d of %d bytes).", COMPILERS_FILE_PATH, (int)dataRead, (int)allowedCompilersFileStatB.st_size);
exit(EXIT_DISTCC_FAILED);
}
char *currentCharacterPtr = allowedCompilersStrings;
char *endOfBuffer = allowedCompilersStrings + allowedCompilersFileStatB.st_size;
while ( currentCharacterPtr < endOfBuffer ) {
if ( ( *currentCharacterPtr != '#' ) && ( *currentCharacterPtr != '\n' ) ) {
if ( currentCompilerSlot == maximumNumberOfCompilers ) {
#define SLOTS_TO_EXPAND_BY 10
maximumNumberOfCompilers = maximumNumberOfCompilers + SLOTS_TO_EXPAND_BY;
allowedCompilers = realloc( allowedCompilers, sizeof( char * ) * (maximumNumberOfCompilers + 1) );
bzero(allowedCompilers + currentCompilerSlot, (SLOTS_TO_EXPAND_BY + 1) * sizeof( char * ));
if ( allowedCompilers == NULL ) {
close( allowedCompilersFile );
free( allowedCompilers );
free( allowedCompilersStrings );
rs_log_crit("failed to reallocate buffer for '%s' content.", COMPILERS_FILE_PATH);
exit(EXIT_OUT_OF_MEMORY);
}
}
allowedCompilers[currentCompilerSlot] = currentCharacterPtr;
currentCompilerSlot++;
}
while ( ( currentCharacterPtr < endOfBuffer ) && ( *currentCharacterPtr != '\n' ) )
currentCharacterPtr++;
if ( currentCharacterPtr == endOfBuffer )
break;
*currentCharacterPtr = '\0';
currentCharacterPtr++;
}
close( allowedCompilersFile );
if (rs_trace_enabled()) {
char **compilers = allowedCompilers;
int i = 0;
while ( *compilers != NULL ) {
rs_trace("allowed compiler: %s", *compilers);
i++;
compilers++;
}
rs_trace("allowed compilers count: %d", i);
}
}
return allowedCompilers;
}
int dcc_validate_compiler(char *aPath)
{
rs_trace("validating compiler %s", aPath);
char **allowedCompilers = _dcc_allowed_compilers();
while ( *allowedCompilers != NULL ) {
rs_trace("testing %s against %s", *allowedCompilers, aPath);
if ( !strcmp(*allowedCompilers, aPath) ) {
rs_trace("compiler '%s' is allowed", aPath);
return 0;
}
allowedCompilers++;
}
rs_log_crit("compiler '%s' not allowed by '%s'.", aPath, COMPILERS_FILE_PATH);
return -1;
}