static rs_result rs_patch_s_copy(rs_job_t *job) { rs_long_t where, len; rs_stats_t *stats; where = job->param1; len = job->param2; rs_trace("COPY(where=" PRINTF_FORMAT_U64 ", len=" PRINTF_FORMAT_U64 ")", PRINTF_CAST_U64(where), PRINTF_CAST_U64(len)); if (len < 0) { rs_log(RS_LOG_ERR, "invalid length=" PRINTF_FORMAT_U64 " on COPY command", PRINTF_CAST_U64(len)); return RS_CORRUPT; } if (where < 0) { rs_log(RS_LOG_ERR, "invalid where=" PRINTF_FORMAT_U64 " on COPY command", PRINTF_CAST_U64(where)); return RS_CORRUPT; } job->basis_pos = where; job->basis_len = len; stats = &job->stats; stats->copy_cmds++; stats->copy_bytes += len; stats->copy_cmdbytes += 1 + job->cmd->len_1 + job->cmd->len_2; job->statefn = rs_patch_s_copying; return RS_RUNNING; }
/** * Analyze and report to the user on a command's exit code. * * @param command short human-readable description of the command (perhaps * argv[0]) * * @returns 0 if the command succeeded; 128+SIGNAL if it stopped on a * signal; otherwise the command's exit code. **/ int dcc_critique_status(int status, const char *command, const char *input_fname, struct dcc_hostdef *host, int verbose) { int logmode; /* verbose mode is only used for executions that the user is likely to * particularly need to know about */ if (verbose) logmode = RS_LOG_ERR | RS_LOG_NONAME; else logmode = RS_LOG_INFO | RS_LOG_NONAME; if (input_fname == NULL) input_fname = "(null)"; if (WIFSIGNALED(status)) { #ifdef HAVE_STRSIGNAL rs_log(logmode, "%s %s on %s: %s%s", command, input_fname, host->hostdef_string, strsignal(WTERMSIG(status)), WCOREDUMP(status) ? " (core dumped)" : ""); #else rs_log(logmode, "%s %s on %s terminated by signal %d%s", command, input_fname, host->hostdef_string, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); #endif /* Unix convention is to return 128+signal when a subprocess crashes. */ return 128 + WTERMSIG(status); } else if (WEXITSTATUS(status) == 1) { /* Normal failure gives exit code 1, so handle that specially */ rs_log(logmode, "%s %s on %s failed", command, input_fname, host->hostdef_string); return WEXITSTATUS(status); } else if (WEXITSTATUS(status)) { /* This is a tough call; we don't really want to clutter the client's * error stream, but if we don't say where the compilation failed then * people may find it hard to work things out. */ rs_log(logmode, "%s %s on %s failed with exit code %d", command, input_fname, host->hostdef_string, WEXITSTATUS(status)); return WEXITSTATUS(status); } else { rs_log(RS_LOG_INFO|RS_LOG_NONAME, "%s %s on %s completed ok", command, input_fname, host->hostdef_string); return 0; } }
void rs_sumset_dump(rs_signature_t const *sums) { int i; rs_block_sig_t *b; char strong_hex[RS_MAX_STRONG_SUM_LENGTH * 3]; rs_log(RS_LOG_INFO|RS_LOG_NONAME, "sumset info: magic=%#x, block_len=%d, block_num=%d", sums->magic, sums->block_len, sums->count); for (i = 0; i < sums->count; i++) { b = rs_block_sig_ptr(sums, i); rs_hexify(strong_hex, b->strong_sum, sums->strong_sum_len); rs_log(RS_LOG_INFO|RS_LOG_NONAME, "sum %6d: weak="FMT_WEAKSUM", strong=%s", i, b->weak_sum, strong_hex); } }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ static int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; dcc_sockaddr_to_string(sa, salen, &s); rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && errno == EINTR); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }
int rs_log_stats(rs_stats_t const *stats) { char buf[1000]; rs_format_stats(stats, buf, sizeof buf - 1); rs_log(RS_LOG_INFO|RS_LOG_NONAME, "%s", buf); return 0; }
void dcc_note_execution(struct dcc_hostdef *host, char **argv) { char *astr; astr = dcc_argv_tostr(argv); rs_log(RS_LOG_INFO|RS_LOG_NONAME, "exec on %s: %s", host->hostdef_string, astr); free(astr); }
static void dcc_note_compiled(const char *input_file, const char *output_file) { const char *input_base, *output_base; input_base = dcc_find_basename(input_file); output_base = dcc_find_basename(output_file); rs_log(RS_LOG_INFO|RS_LOG_NONAME, "compile from %s to %s", input_base, output_base); }
/** * Dump signatures to the log. */ void rs_sumset_dump(rs_signature_t const *sums) { int i; char strong_hex[RS_STRONG_SUM_LEN * 3]; rs_log(RS_LOG_INFO, "sumset info: block_len=%d, file length=%lu, " "number of chunks=%d, remainder=%d", sums->block_len, (unsigned long) sums->flength, sums->count, sums->remainder); for (i = 0; i < sums->count; i++) { rs_hexify(strong_hex, sums->block_sigs[i].strong_sum, sums->strong_sum_len); rs_log(RS_LOG_INFO, "sum %6d: weak=%08x, strong=%s", i, sums->block_sigs[i].weak_sum, strong_hex); } }
static void dcc_log_child_exited(pid_t kid, int status) { if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); int severity = sig == SIGTERM ? RS_LOG_INFO : RS_LOG_ERR; rs_log(severity, "child %d: signal %d (%s)", (int) kid, sig, WCOREDUMP(status) ? "core dumped" : "no core"); } else if (WIFEXITED(status)) { rs_log_info("child %d exited: exit status %d", (int) kid, WEXITSTATUS(status)); } }
int main(const int argc, const char *argv[]) { poptContext opcon; rs_result result; opcon = poptGetContext(PROGRAM, argc, argv, opts, 0); rdiff_options(opcon); result = rdiff_action(opcon); if (result != RS_DONE) rs_log(RS_LOG_ERR|RS_LOG_NONAME, "%s", rs_strerror(result)); return result; }
/** * Prepare to compute a streaming delta. */ rs_job_t *rs_delta_begin(rs_signature_t *sig) { rs_job_t *job; job = rs_job_new("delta", rs_delta_s_header); job->signature = sig; RollsumInit(&job->weak_sum); if ((job->block_len = sig->block_len) < 0) { rs_log(RS_LOG_ERR, "unreasonable block_len %d in signature", job->block_len); return NULL; } job->strong_sum_len = sig->strong_sum_len; if (job->strong_sum_len < 0 || job->strong_sum_len > RS_STRONG_SUM_LEN) { rs_log(RS_LOG_ERR, "unreasonable strong_sum_len %d in signature", job->strong_sum_len); return NULL; } return job; }
void rs_signature_log_stats(rs_signature_t const *sig) { #ifndef HASHTABLE_NSTATS hashtable_t *t = sig->hashtable; rs_log(RS_LOG_INFO|RS_LOG_NONAME, "match statistics: signature[%ld searches, %ld (%.3f%%) matches, " "%ld (%.3fx) weak sum compares, %ld (%.3f%%) strong sum compares, " "%ld (%.3f%%) strong sum calcs]", t->find_count, t->match_count, 100.0 * (double)t->match_count / t->find_count, t->hashcmp_count, (double)t->hashcmp_count / t->find_count, t->entrycmp_count, 100.0 * (double)t->entrycmp_count / t->find_count, sig->calc_strong_count, 100.0 * (double)sig->calc_strong_count / t->find_count); #endif }
/** * Called when trying to copy through literal data. */ static rs_result rs_patch_s_literal(rs_job_t *job) { rs_long_t len = job->param1; rs_trace("LITERAL(len=" PRINTF_FORMAT_U64 ")", PRINTF_CAST_U64(len)); if (len < 0) { rs_log(RS_LOG_ERR, "invalid length=" PRINTF_FORMAT_U64 " on LITERAL command", PRINTF_CAST_U64(len)); return RS_CORRUPT; } job->stats.lit_cmds++; job->stats.lit_bytes += len; job->stats.lit_cmdbytes += 1 + job->cmd->len_1; rs_tube_copy(job, len); job->statefn = rs_patch_s_cmdbyte; return RS_RUNNING; }
/** * Called while we're trying to read the header of the patch. */ static rs_result rs_patch_s_header(rs_job_t *job) { int v; rs_result result; if ((result = rs_suck_n4(job, &v)) != RS_DONE) return result; if (v != RS_DELTA_MAGIC) { rs_log(RS_LOG_ERR, "got magic number %#x rather than expected value %#x", v, RS_DELTA_MAGIC); return RS_BAD_MAGIC; } else rs_trace("got patch magic %#x", v); job->statefn = rs_patch_s_cmdbyte; return RS_RUNNING; }
/** * Default copy implementation that retrieves a part of a stdio file. */ rs_result rs_file_copy_cb(void *arg, rs_long_t pos, size_t *len, void **buf) { int got; FILE *f = (FILE *) arg; if (fseek(f, pos, SEEK_SET)) { rs_log(RS_LOG_ERR, "seek failed: %s", strerror(errno)); return RS_IO_ERROR; } got = fread(*buf, 1, *len, f); if (got == -1) { rs_error("read error: %s", strerror(errno)); return RS_IO_ERROR; } else if (got == 0) { rs_error("unexpected eof on fd%d", fileno(f)); return RS_INPUT_ENDED; } else { *len = got; return RS_DONE; } }
rs_result rs_job_iter(rs_job_t *job, rs_buffers_t *buffers) { rs_result result; rs_long_t orig_in, orig_out; orig_in = buffers->avail_in; orig_out = buffers->avail_out; result = rs_job_work(job, buffers); if (result == RS_BLOCKED || result == RS_DONE) if ((orig_in == buffers->avail_in) && (orig_out == buffers->avail_out) && orig_in && orig_out) { rs_log(RS_LOG_ERR, "internal error: job made no progress " "[orig_in=" PRINTF_FORMAT_U64 ", orig_out=" PRINTF_FORMAT_U64 ", final_in=" PRINTF_FORMAT_U64 ", final_out=" PRINTF_FORMAT_U64 "]", PRINTF_CAST_U64(orig_in), PRINTF_CAST_U64(orig_out), PRINTF_CAST_U64(buffers->avail_in), PRINTF_CAST_U64(buffers->avail_out)); return RS_INTERNAL_ERROR; } return result; }
void dcc_exit(int exitcode) { struct rusage self_ru, children_ru; if (getrusage(RUSAGE_SELF, &self_ru)) { rs_log_warning("getrusage(RUSAGE_SELF) failed: %s", strerror(errno)); memset(&self_ru, 0, sizeof self_ru); } if (getrusage(RUSAGE_CHILDREN, &children_ru)) { rs_log_warning("getrusage(RUSAGE_CHILDREN) failed: %s", strerror(errno)); memset(&children_ru, 0, sizeof children_ru); } /* NB fields must match up for microseconds */ rs_log(RS_LOG_INFO, "exit: code %d; self: %d.%06d user %d.%06d sys; children: %d.%06d user %d.%06d sys", exitcode, (int) self_ru.ru_utime.tv_sec, (int) self_ru.ru_utime.tv_usec, (int) self_ru.ru_stime.tv_sec, (int) self_ru.ru_stime.tv_usec, (int) children_ru.ru_utime.tv_sec, (int) children_ru.ru_utime.tv_usec, (int) children_ru.ru_stime.tv_sec, (int) children_ru.ru_stime.tv_usec); exit(exitcode); }
/** * Read a request, run the compiler, and send a response. **/ static int dcc_run_job(int in_fd, int out_fd) { char **argv; int status; char *temp_i, *temp_o, *err_fname, *out_fname; int ret, compile_ret; char *orig_input, *orig_output; pid_t cc_pid; enum dcc_protover protover; enum dcc_compress compr; dcc_indirection indirect; if ((ret = dcc_make_tmpnam("distcc", ".stderr", &err_fname))) goto out_cleanup; if ((ret = dcc_make_tmpnam("distcc", ".stdout", &out_fname))) goto out_cleanup; dcc_remove_if_exists(err_fname); dcc_remove_if_exists(out_fname); /* Capture any messages relating to this compilation to the same file as * compiler errors so that they can all be sent back to the client. */ dcc_add_log_to_file(err_fname); /* Ignore SIGPIPE; we consistently check error codes and will see the * EPIPE. Note that it is set back to the default behaviour when spawning * a child, to handle cases like the assembler dying while its being fed * from the compiler */ dcc_ignore_sigpipe(1); /* Allow output to accumulate into big packets. */ tcp_cork_sock(out_fd, 1); if ((ret = dcc_r_request_header(in_fd, &protover)) || (ret = dcc_r_argv(in_fd, &argv)) || (ret = dcc_scan_args(argv, &orig_input, &orig_output, &argv))) goto out_cleanup; if (strcmp(argv[0],"--host-info") == 0) { if ((ret = dcc_x_result_header(out_fd, protover)) || (ret = dcc_send_host_info(out_fd))) dcc_x_token_int(out_fd, "DOTO", 0); } else { rs_trace("output file %s", orig_output); if ((ret = dcc_input_tmpnam(orig_input, &temp_i))) goto out_cleanup; if ((ret = dcc_make_tmpnam("distccd", ".o", &temp_o))) goto out_cleanup; compr = (protover == 2) ? DCC_COMPRESS_LZO1X : DCC_COMPRESS_NONE; if ((ret = dcc_r_token_file(in_fd, "DOTI", temp_i, compr)) || (ret = dcc_set_input(argv, temp_i)) || (ret = dcc_set_output(argv, temp_o))) goto out_cleanup; if ((ret = dcc_check_compiler_masq(argv[0]))) goto out_cleanup; indirect.in_fd = in_fd; indirect.out_fd = out_fd; if ((compile_ret = dcc_spawn_child(argv, &cc_pid, "/dev/null", out_fname, err_fname, &indirect)) || (compile_ret = dcc_collect_child("cc", cc_pid, &status))) { /* We didn't get around to finding a wait status from the actual compiler */ status = W_EXITCODE(compile_ret, 0); } if ((ret = dcc_x_result_header(out_fd, protover)) || (ret = dcc_send_system_info(out_fd)) || (ret = dcc_send_compiler_version(out_fd, argv[0])) || (ret = dcc_x_cc_status(out_fd, status)) || (ret = dcc_x_file(out_fd, err_fname, "SERR", compr, NULL)) || (ret = dcc_x_file(out_fd, out_fname, "SOUT", compr, NULL)) || WIFSIGNALED(status) || WEXITSTATUS(status)) { /* Something went wrong, so send DOTO 0 */ dcc_x_token_int(out_fd, "DOTO", 0); } else { ret = dcc_x_file(out_fd, temp_o, "DOTO", compr, NULL); } dcc_critique_status(status, argv[0], orig_input, dcc_hostdef_local, 0); } tcp_cork_sock(out_fd, 0); rs_log(RS_LOG_INFO|RS_LOG_NONAME, "job complete"); out_cleanup: dcc_remove_log_to_file(); dcc_cleanup_tempfiles(); return ret; }
/* * Connect to a host given its binary address, with a timeout. * * host and port are only here to aid printing debug messages. */ int dcc_connect_by_addr(struct sockaddr *sa, size_t salen, int *p_fd) { int fd; int ret; char *s; int failed; int connecterr; int tries = 3; dcc_sockaddr_to_string(sa, salen, &s); if (s == NULL) return EXIT_OUT_OF_MEMORY; rs_trace("started connecting to %s", s); if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) { rs_log_error("failed to create socket: %s", strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } dcc_set_nonblocking(fd); /* start the nonblocking connect... */ do failed = connect(fd, sa, salen); while (failed == -1 && (errno == EINTR || (errno == EAGAIN && tries-- && poll(NULL, 0, 500) == 0))); if (failed == -1 && errno != EINPROGRESS) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "failed to connect to %s: %s", s, strerror(errno)); ret = EXIT_CONNECT_FAILED; goto out_failed; } do { socklen_t len; if ((ret = dcc_select_for_write(fd, dcc_connect_timeout))) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "timeout while connecting to %s", s); goto out_failed; } connecterr = -1; len = sizeof(connecterr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&connecterr, &len) < 0) { rs_log_error("getsockopt SO_ERROR failed?!"); ret = EXIT_CONNECT_FAILED; goto out_failed; } /* looping is unlikely, but I believe I needed this in dkftpbench */ /* fixme: should reduce timeout on each time around this loop */ } while (connecterr == EINPROGRESS); if (connecterr) { rs_log(RS_LOG_ERR|RS_LOG_NONAME, "nonblocking connect to %s failed: %s", s, strerror(connecterr)); ret = EXIT_CONNECT_FAILED; goto out_failed; } *p_fd = fd; free(s); return 0; out_failed: free(s); return ret; }