/** * Invoke a compiler locally. This is, obviously, the alternative to * dcc_compile_remote(). * * The server does basically the same thing, but it doesn't call this * routine because it wants to overlap execution of the compiler with * copying the input from the network. * * This routine used to exec() the compiler in place of distcc. That * is slightly more efficient, because it avoids the need to create, * schedule, etc another process. The problem is that in that case we * can't clean up our temporary files, and (not so important) we can't * log our resource usage. * **/ int build_local(CompileJob &job, MsgChannel *local_daemon, struct rusage *used) { list<string> arguments; string compiler_name = find_compiler(job); trace() << "invoking: " << compiler_name << endl; if (compiler_name.empty()) { log_error() << "could not find " << job.compilerName() << " in PATH." << endl; return EXIT_NO_SUCH_FILE; } arguments.push_back(compiler_name); appendList(arguments, job.allFlags()); if (!job.inputFile().empty()) { arguments.push_back(job.inputFile()); } if (!job.outputFile().empty()) { arguments.push_back("-o"); arguments.push_back(job.outputFile()); } char **argv = new char*[arguments.size() + 1]; int argc = 0; for (list<string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { argv[argc++] = strdup(it->c_str()); } argv[argc] = 0; #if CLIENT_DEBUG trace() << "execing "; for (int i = 0; argv[i]; i++) { trace() << argv[i] << " "; } trace() << endl; #endif if (!local_daemon) { int fd; if (!dcc_lock_host(fd)) { log_error() << "can't lock for local job" << endl; return EXIT_DISTCC_FAILED; } lock_fd = fd; } bool color_output = job.language() != CompileJob::Lang_Custom && colorify_wanted(job); int pf[2]; if (color_output && pipe(pf)) { color_output = false; } if (used || color_output) { flush_debug(); child_pid = fork(); } if (!child_pid) { dcc_increment_safeguard(); if (color_output) { close(pf[0]); close(2); dup2(pf[1], 2); } int ret = execv(argv[0], argv); if (lock_fd) { dcc_unlock(lock_fd); } if (ret) { char buf[256]; snprintf(buf, sizeof(buf), "ICECC[%d]: %s:", getpid(), argv[0]); log_perror(buf); } _exit(ret); } if (color_output) { close(pf[1]); } // setup interrupt signals, so that the JobLocalBeginMsg will // have a matching JobLocalDoneMsg void (*old_sigint)(int) = signal(SIGINT, handle_user_break); void (*old_sigterm)(int) = signal(SIGTERM, handle_user_break); void (*old_sigquit)(int) = signal(SIGQUIT, handle_user_break); void (*old_sighup)(int) = signal(SIGHUP, handle_user_break); if (color_output) { string s_ccout; char buf[250]; int r; for (;;) { while ((r = read(pf[0], buf, sizeof(buf) - 1)) > 0) { buf[r] = '\0'; s_ccout.append(buf); } if (r == 0) { break; } if (r < 0 && errno != EINTR) { break; } } colorify_output(s_ccout); } int status = 1; while (wait4(child_pid, &status, 0, used) < 0 && errno == EINTR) {} status = shell_exit_status(status); signal(SIGINT, old_sigint); signal(SIGTERM, old_sigterm); signal(SIGQUIT, old_sigquit); signal(SIGHUP, old_sighup); if (user_break_signal) { raise(user_break_signal); } if (lock_fd) { dcc_unlock(lock_fd); } return status; }
/** * Invoke a compiler locally. This is, obviously, the alternative to * dcc_compile_remote(). * * The server does basically the same thing, but it doesn't call this * routine because it wants to overlap execution of the compiler with * copying the input from the network. * * This routine used to exec() the compiler in place of distcc. That * is slightly more efficient, because it avoids the need to create, * schedule, etc another process. The problem is that in that case we * can't clean up our temporary files, and (not so important) we can't * log our resource usage. * **/ int build_local(CompileJob &job, MsgChannel *local_daemon, struct rusage *used) { list<string> arguments; string compiler_name = find_compiler(job); if (compiler_name.empty()) { log_error() << "could not find " << job.compilerName() << " in PATH." << endl; return EXIT_NO_SUCH_FILE; } arguments.push_back(compiler_name); appendList(arguments, job.allFlags()); if (job.dwarfFissionEnabled()) { arguments.push_back("-gsplit-dwarf"); } if (!job.inputFile().empty()) { arguments.push_back(job.inputFile()); } if (!job.outputFile().empty()) { arguments.push_back("-o"); arguments.push_back(job.outputFile()); } vector<char*> argv; string argstxt; for (list<string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { argv.push_back(strdup(it->c_str())); argstxt += ' '; argstxt += *it; } argv.push_back(0); trace() << "invoking:" << argstxt << endl; if (!local_daemon) { int fd; if (!dcc_lock_host(fd)) { log_error() << "can't lock for local job" << endl; return EXIT_DISTCC_FAILED; } lock_fd = fd; } bool color_output = job.language() != CompileJob::Lang_Custom && colorify_wanted(job); int pf[2]; if (color_output && pipe(pf)) { color_output = false; } if (used || color_output) { flush_debug(); child_pid = fork(); } if (child_pid == -1){ log_perror("fork failed"); } if (!child_pid) { dcc_increment_safeguard(job.language() == CompileJob::Lang_Custom ? SafeguardStepCustom : SafeguardStepCompiler); if (color_output) { if ((-1 == close(pf[0])) && (errno != EBADF)){ log_perror("close failed"); } if ((-1 == close(2)) && (errno != EBADF)){ log_perror("close failed"); } if (-1 == dup2(pf[1], 2)){ log_perror("dup2 failed"); } } execv(argv[0], &argv[0]); int exitcode = ( errno == ENOENT ? 127 : 126 ); log_perror("execv failed"); if (lock_fd) { dcc_unlock(lock_fd); } { char buf[256]; snprintf(buf, sizeof(buf), "ICECC[%d]: %s:", getpid(), argv[0]); log_perror(buf); } _exit(exitcode); } for(vector<char*>::const_iterator i = argv.begin(); i != argv.end(); ++i){ free(*i); } argv.clear(); if (color_output) { if ((-1 == close(pf[1])) && (errno != EBADF)){ log_perror("close failed"); } } // setup interrupt signals, so that the JobLocalBeginMsg will // have a matching JobLocalDoneMsg void (*old_sigint)(int) = signal(SIGINT, handle_user_break); void (*old_sigterm)(int) = signal(SIGTERM, handle_user_break); void (*old_sigquit)(int) = signal(SIGQUIT, handle_user_break); void (*old_sighup)(int) = signal(SIGHUP, handle_user_break); if (color_output) { string s_ccout; char buf[250]; for (;;) { int r; while ((r = read(pf[0], buf, sizeof(buf) - 1)) > 0) { buf[r] = '\0'; s_ccout.append(buf); } if (r == 0) { break; } if (r < 0 && errno != EINTR) { break; } } colorify_output(s_ccout); } int status = 1; while (wait4(child_pid, &status, 0, used) < 0 && errno == EINTR) {} status = shell_exit_status(status); signal(SIGINT, old_sigint); signal(SIGTERM, old_sigterm); signal(SIGQUIT, old_sigquit); signal(SIGHUP, old_sighup); if (user_break_signal) { raise(user_break_signal); } if (lock_fd) { dcc_unlock(lock_fd); } return status; }