Пример #1
0
/**
 * Change object file or suffix of -o to @p ofname
 * Frees the old value, if it exists.
 *
 * It's crucially important that in every case where an output file is
 * detected by dcc_scan_args(), it's also correctly identified here.
 * It might be better to make the code shared.
 **/
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);
            free(a[i+1]);
            a[i+1] = strdup(ofname);
            if (a[i+1] == NULL) {
                rs_log_crit("failed to allocate space for output parameter");
                return EXIT_OUT_OF_MEMORY;
            }
            dcc_trace_argv("command after", a);
            return 0;
        } else if (0 == strncmp(a[i], "-o", 2)) {
            char *newptr;
            rs_trace("changed output from \"%s\" to \"%s\"", a[i]+2, ofname);
            free(a[i]);
            if (asprintf(&newptr, "-o%s", ofname) == -1) {
                rs_log_crit("failed to allocate space for output parameter");
                return EXIT_OUT_OF_MEMORY;
            }
            a[i] = newptr;
            dcc_trace_argv("command after", a);
            return 0;
        }

    rs_log_error("failed to find \"-o\"");
    return EXIT_DISTCC_FAILED;
}
Пример #2
0
/**
 * Run @p argv in a child asynchronously.
 *
 * stdin, stdout and stderr are redirected as shown, unless those
 * filenames are NULL.  In that case they are left alone.
 *
 * @warning When called on the daemon, where stdin/stdout may refer to random
 * network sockets, all of the standard file descriptors must be redirected!
 **/
int dcc_spawn_child(char **argv, pid_t *pidptr,
                    const char *stdin_file,
                    const char *stdout_file,
                    const char *stderr_file)
{
    pid_t pid;

    dcc_trace_argv("forking to execute", argv);

    pid = fork();
    if (pid == -1) {
        rs_log_error("failed to fork: %s", strerror(errno));
        return EXIT_OUT_OF_MEMORY; /* probably */
    } else if (pid == 0) {
        /* If this is a remote compile,
         * put the child in a new group, so we can
         * kill it and all its descendents without killing distccd
         * FIXME: if you kill distccd while it's compiling, and
         * the compiler has an infinite loop bug, the new group
         * will run forever until you kill it.
         */
        if (stdout_file != NULL) {
            if (dcc_new_pgrp() != 0)
                rs_trace("Unable to start a new group\n");
        }
        dcc_inside_child(argv, stdin_file, stdout_file, stderr_file);
        /* !! NEVER RETURN FROM HERE !! */
    } else {
        *pidptr = pid;
        rs_trace("child started as pid%d", (int) pid);
        return 0;
    }
}
Пример #3
0
/**
 * Change input file to a copy of @p ifname; called on compiler.
 * Frees the old value.
 *
 * @todo Unify this with dcc_scan_args
 *
 * @todo Test this by making sure that when the modified arguments are
 * run through scan_args, the new ifname is identified as the input.
 **/
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);
            free(a[i]);
            a[i] = strdup(ifname);
            if (a[i] == NULL) {
                rs_log_crit("failed to allocate space for input parameter");
                return EXIT_OUT_OF_MEMORY;
            }
            dcc_trace_argv("command after", a);
            return 0;
        }

    rs_log_error("failed to find input file");
    return EXIT_DISTCC_FAILED;
}
Пример #4
0
/**
 * Remove "-o" options from argument list.
 *
 * This is used when running the preprocessor, when we just want it to write
 * to stdout, which is the default when no -o option is specified.
 *
 * Structurally similar to dcc_strip_local_args()
 **/
int dcc_strip_dasho(char **from, char ***out_argv)
{
    char **to;
    int from_i, to_i;
    int from_len;

    from_len = dcc_argv_len(from);
    *out_argv = to = malloc((from_len + 1) * sizeof (char *));

    if (!to) {
        rs_log_error("failed to allocate space for arguments");
        return EXIT_OUT_OF_MEMORY;
    }

    /* skip through argv, copying all arguments but skipping ones that
     * ought to be omitted */
    for (from_i = to_i = 0; from[from_i]; ) {
        if (!strcmp(from[from_i], "-o")) {
            /* skip "-o  FILE" */
            from_i += 2;
        }
        else if (str_startswith("-o", from[from_i])) {
            /* skip "-oFILE" */
            from_i++;
        }
        else {
            to[to_i++] = from[from_i++];
        }
    }

    /* NULL-terminate */
    to[to_i] = NULL;

    dcc_trace_argv("result", to);

    return 0;
}
Пример #5
0
/**
 * Parse arguments, extract ones we care about, and also work out
 * whether it will be possible to distribute this invocation remotely.
 *
 * This is a little hard because the cc argument rules are pretty complex, but
 * the function still ought to be simpler than it already is.
 *
 * This code is called on both the client and the server, though they use the
 * results differently.
 *
 * This function makes a copy of the arguments, modified to ensure that
 * the arguments include '-o <filename>'.  This is returned in *ret_newargv.
 * The copy is dynamically allocated and the caller is responsible for
 * deallocating it.
 *
 * If @p forced_cpp_ext is non NULL, it is filled it with the extension that
 * is forced by a -x language directive.  The caller should not free this
 * value.
 *
 * @returns 0 if it's ok to distribute this compilation, or an error code.
 **/
int dcc_scan_args(char *argv[], char **input_file, char **output_file,
                  char ***ret_newargv, const char **forced_cpp_ext)
{
    int seen_opt_c = 0, seen_opt_s = 0;
    int i;
    char *a, *optx_lang;
    const char *optx_ext = NULL;
    int ret;

     /* allow for -o foo.o */
    if ((ret = dcc_copy_argv(argv, ret_newargv, 2)) != 0)
        return ret;
    argv = *ret_newargv;

    /* FIXME: new copy of argv is leaked */

    dcc_trace_argv("scanning arguments", argv);

#ifdef XCODE_INTEGRATION
    /* Xcode invokes the distcc client as "distcc --host-info HOST" to gather
     * info about HOST.  When the request is transmitted to the distccd server,
     * it will see only "--host-info" and no other arguments in argv. */
    if (argv[0] && !strcmp(argv[0], "--host-info")) {
        return 0;
    }
#endif /* XCODE_INTEGRATION */

    /* Things like "distcc -c hello.c" with an implied compiler are
     * handled earlier on by inserting a compiler name.  At this
     * point, argv[0] should always be a compiler name. */
    if (argv[0] && 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 EXIT_DISTCC_FAILED;
            } else if (!strcmp(a, "-MD") || !strcmp(a, "-MMD")) {
                /* These two generate dependencies as a side effect.  They
                 * should work with the way we call cpp. */
            } else if (!strcmp(a, "-MG") || !strcmp(a, "-MP")) {
                /* These just modify the behaviour of other -M* options and do
                 * nothing by themselves. */
            } else if (!strcmp(a, "-MF") || !strcmp(a, "-MT") ||
                       !strcmp(a, "-MQ")) {
                /* As above but with extra argument. */
                i++;
            } else if (!strncmp(a, "-MF", 3) || !strncmp(a, "-MT", 3) ||
                       !strncmp(a, "-MQ", 3)) {
                /* As above, without extra argument. */
            } else if (a[1] == 'M') {
                /* -M(anything else) causes the preprocessor to
                    produce a list of make-style dependencies on
                    header files, either to stdout or to a local file.
                    It implies -E, so only the preprocessor is run,
                    not the compiler.  There would be no point trying
                    to distribute it even if we could. */
                rs_trace("%s implies -E (maybe) and must be local", a);
                return EXIT_DISTCC_FAILED;
            } else if (!strcmp(a, "-march=native")) {
                rs_trace("-march=native generates code for local machine; "
                         "must be local");
                return EXIT_DISTCC_FAILED;
            } else if (!strcmp(a, "-mtune=native")) {
                rs_trace("-mtune=native optimizes for local machine; "
                         "must be local");
                return EXIT_DISTCC_FAILED;
            } else if (str_startswith("-Wa,", a)) {
                /* Look for assembler options that would produce output
                 * files and must be local.
                 *
                 * Writing listings to stdout could be supported but it might
                 * be hard to parse reliably. */
                if (strstr(a, ",-a") || strstr(a, "--MD")) {
                    rs_trace("%s must be local", a);
                    return EXIT_DISTCC_FAILED;
                }
            } else if (str_startswith("-specs=", a)) {
                rs_trace("%s must be local", a);
                return EXIT_DISTCC_FAILED;
            } 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 EXIT_DISTCC_FAILED;
            } else if (!strcmp(a, "-frepo")) {
                rs_log_info("compiler will emit .rpo files; must be local");
                return EXIT_DISTCC_FAILED;
            } else if (!strcmp("-x", a)) {
              optx_lang = argv[++i];
              if (!optx_lang || !strlen(optx_lang)) {
                rs_log_info("-x requires an argument; running locally");
                return EXIT_DISTCC_FAILED;
              }
              if (*input_file) {
                rs_log_info("-x must precede source file; running locally");
                return EXIT_DISTCC_FAILED;
              }
              if (optx_ext) {
                rs_log_info("at most one -x supported; running locally");
                return EXIT_DISTCC_FAILED;
              }
              optx_ext = dcc_optx_ext_lookup(optx_lang);
              if (!optx_ext) {
                rs_log_info("unsupported -x language; running locally");
                return EXIT_DISTCC_FAILED;
              }
            } else if (str_startswith("-x", a)) {
                /* Handling -xlanguage is possible, but it makes some of the
                 * command rewriting (over in remote.c) much harder, so it
                 * isn't supported at this time. */
                rs_log_info("-xlanguage unsupported, use -x language instead; "
                            "running locally");
                return EXIT_DISTCC_FAILED;
            } else if (str_startswith("-dr", a)) {
                rs_log_info("gcc's debug option %s may write extra files; "
                            "running locally", a);
                return EXIT_DISTCC_FAILED;
            } else if (!strcmp(a, "-c")) {
                seen_opt_c = 1;
            } else if (!strcmp(a, "-o")) {
                /* Whatever follows must be the output */
                a = argv[++i];
                goto GOT_OUTPUT;
            } else if (str_startswith("-o", a)) {
                a += 2;         /* skip "-o" */
                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 EXIT_DISTCC_FAILED;
                }
                *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 EXIT_DISTCC_FAILED;
                }
                *output_file = a;
            }
        }
    }

    /* TODO: ccache has the heuristic of ignoring arguments that are not
     * extant files when looking for the input file; that's possibly
     * worthwile.  Of course we can't do that on the server. */

    if (!seen_opt_c && !seen_opt_s) {
        rs_log_info("compiler apparently called not for compile");
        return EXIT_DISTCC_FAILED;
    }

    if (!*input_file) {
        rs_log_info("no visible input file");
        return EXIT_DISTCC_FAILED;
    }

    if (dcc_source_needs_local(*input_file))
        return EXIT_DISTCC_FAILED;

    if (!*output_file) {
        /* This is a commandline like "gcc -c hello.c".  They want
         * hello.o, but they don't say so.  For example, the Ethereal
         * makefile does this.
         *
         * Note: this doesn't handle a.out, the other implied
         * filename, but that doesn't matter because it would already
         * be excluded by not having -c or -S.
         */
        char *ofile;

        /* -S takes precedence over -c, because it means "stop after
         * preprocessing" rather than "stop after compilation." */
        if (seen_opt_s) {
            if (dcc_output_from_source(*input_file, ".s", &ofile))
                return EXIT_DISTCC_FAILED;
        } else if (seen_opt_c) {
            if (dcc_output_from_source(*input_file, ".o", &ofile))
                return EXIT_DISTCC_FAILED;
        } else {
            rs_log_crit("this can't be happening(%d)!", __LINE__);
            return EXIT_DISTCC_FAILED;
        }
        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;
    }

    dcc_note_compiled(*input_file, *output_file);

    if (strcmp(*output_file, "-") == 0) {
        /* Different compilers may treat "-o -" as either "write to
         * stdout", or "write to a file called '-'".  We can't know,
         * so we just always run it locally.  Hopefully this is a
         * pretty rare case. */
        rs_log_info("output to stdout?  running locally");
        return EXIT_DISTCC_FAILED;
    }

    if (forced_cpp_ext)
        *forced_cpp_ext = optx_ext;

    return 0;
}
Пример #6
0
/**
 * Strip arguments like -D and -I from a command line, because they do
 * not need to be passed across the wire.  This covers options for
 * both the preprocess and link phases, since they should never happen
 * remotely.
 *
 * In the case where we inadvertently do cause preprocessing to happen
 * remotely, it is possible that omitting these options will make
 * failure more obvious and avoid false success.
 *
 * Giving -L on a compile-only command line is a bit wierd, but it is
 * observed to happen in Makefiles that are not strict about CFLAGS vs
 * LDFLAGS, etc.
 *
 * NOTE: gcc-3.2's manual in the "preprocessor options" section
 * describes some options, such as -d, that only take effect when
 * passed directly to cpp.  When given to gcc they have different
 * meanings.
 *
 * The value stored in '*out_argv' is malloc'd, but the arguments that
 * are pointed to by that array are aliased with the values pointed
 * to by 'from'.  The caller is responsible for calling free() on
 * '*out_argv'.
 **/
int dcc_strip_local_args(char **from, char ***out_argv)
{
    char **to;
    int from_i, to_i;
    int from_len;

    from_len = dcc_argv_len(from);
    *out_argv = to = malloc((from_len + 1) * sizeof (char *));

    if (!to) {
        rs_log_error("failed to allocate space for arguments");
        return EXIT_OUT_OF_MEMORY;
    }

    /* skip through argv, copying all arguments but skipping ones that
     * ought to be omitted */
    for (from_i = to_i = 0; from[from_i]; from_i++) {
        if (str_equal("-D", from[from_i])
            || str_equal("-I", from[from_i])
            || str_equal("-U", from[from_i])
            || str_equal("-L", from[from_i])
            || str_equal("-l", from[from_i])
            || str_equal("-MF", from[from_i])
            || str_equal("-MT", from[from_i])
            || str_equal("-MQ", from[from_i])
            || str_equal("-include", from[from_i])
            || str_equal("-imacros", from[from_i])
            || str_equal("-iprefix", from[from_i])
            || str_equal("-iwithprefix", from[from_i])
            || str_equal("-isystem", from[from_i])
            || str_equal("-iwithprefixbefore", from[from_i])
            || str_equal("-idirafter", from[from_i])) {
            /* skip next word, being option argument */
            if (from[from_i+1])
                from_i++;
        }
        else if (str_startswith("-Wp,", from[from_i])
                 || str_startswith("-Wl,", from[from_i])
                 || str_startswith("-D", from[from_i])
                 || str_startswith("-U", from[from_i])
                 || str_startswith("-I", from[from_i])
                 || str_startswith("-l", from[from_i])
                 || str_startswith("-L", from[from_i])
                 || str_startswith("-MF", from[from_i])
                 || str_startswith("-MT", from[from_i])
                 || str_startswith("-MQ", from[from_i])) {
            /* Something like "-DNDEBUG" or
             * "-Wp,-MD,.deps/nsinstall.pp".  Just skip this word */
            ;
        }
        else if (str_equal("-undef", from[from_i])
                 || str_equal("-nostdinc", from[from_i])
                 || str_equal("-nostdinc++", from[from_i])
                 || str_equal("-MD", from[from_i])
                 || str_equal("-MMD", from[from_i])
                 || str_equal("-MG", from[from_i])
                 || str_equal("-MP", from[from_i])) {
            /* Options that only affect cpp; skip */
            ;
        }
        else {
            to[to_i++] = from[from_i];
        }
    }

    /* NULL-terminate */
    to[to_i] = NULL;

    dcc_trace_argv("result", to);

    return 0;
}