/** * Set log to the final destination after options have been read. **/ static void dcc_setup_real_log(void) { int fd; /* Even in inetd mode, we might want to log to stderr, because that will * work OK for ssh connections. */ if (opt_log_stderr) { rs_remove_all_loggers(); rs_add_logger(rs_logger_file, opt_log_level_num, 0, STDERR_FILENO); return; } if (arg_log_file) { /* Don't remove loggers yet, in case this fails and needs to go to the * default. */ if ((fd = open(arg_log_file, O_CREAT|O_APPEND|O_WRONLY, 0666)) == -1) { rs_log_error("failed to open %s: %s", arg_log_file, strerror(errno)); /* continue and use syslog */ } else { rs_remove_all_loggers(); rs_add_logger(rs_logger_file, opt_log_level_num, NULL, fd); return; } } rs_remove_all_loggers(); openlog("distccd", LOG_PID, LOG_DAEMON); rs_add_logger(rs_logger_syslog, opt_log_level_num, NULL, 0); }
/** * Errors during startup (e.g. bad options) need to be reported somewhere, * although we have not yet parsed the options to work out where the user * wants them. * * In inetd mode, we can't write to stderr because that will corrupt the * stream, so if it looks like stderr is a socket we go to syslog instead. **/ static int dcc_setup_startup_log(void) { rs_trace_set_level(RS_LOG_INFO); if (!is_a_socket(STDERR_FILENO)) { rs_add_logger(rs_logger_file, RS_LOG_DEBUG, 0, STDERR_FILENO); } else { openlog("distccd", LOG_PID, LOG_DAEMON); rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); } return 0; }
/** * If you don't initialize a logger before first logging, then we * write to stderr by default. **/ static void rs_lazy_default(void) { static int called; if (called) return; called = 1; if (logger_list == NULL) rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, STDERR_FILENO); }
int main(int argc, char **argv) { rs_program_name = argv[0]; rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, STDERR_FILENO); rs_trace_set_level(RS_LOG_DEBUG); if (argc != 4) { rs_log_error("Usage: %s <filename> <client-path> <server-path>", rs_program_name); exit(1); } return dcc_fix_debug_info(argv[1], argv[2], argv[3]); }
int main(int argc, char *argv[]) { rs_trace_set_level(RS_LOG_DEBUG); rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, STDERR_FILENO); if (argc < 2) { rs_log_error(USAGE); return 1; } if (strcmp(argv[1], "dcc_fresh_dependency_exists") == 0) { if (argc != 5) { rs_log_error("dcc_fresh_dependency_exists expects DOTD_FNAME " "EXCL_PAT REF_TIME"); return 1; } errno = 0; char *ptr; time_t ref_time = (time_t)strtol(argv[4], &ptr, 0); if (errno || (*ptr != '\0')) { rs_log_error("strtol failed"); return 1; } else { char *result; int ret; ret = dcc_fresh_dependency_exists((const char *)argv[2], (const char *)argv[3], ref_time, &result); if (ret) printf("h_compile.c: UNEXPECTED RETURN VALUE\n"); else printf("result %s\n", result ? result : "(NULL)"); if (result) free(result); } } else if (strcmp(argv[1], "dcc_discrepancy_filename") == 0) { if (argc != 2) { rs_log_error("dcc_discrepancy_filename expects no arguments"); return 1; } char *result; int ret = dcc_discrepancy_filename(&result); if (ret) printf("h_compile.c: UNEXPECTED RETURN VALUE\n"); else printf("%s", result ? result : "(NULL)"); } else { rs_log_error(USAGE); return 1; } return 0; }
/** * Setup client error/trace output. * * Trace goes to the file specified by DISTCC_LOG, if any. Otherwise, it goes * to stderr, except that UNCACHED_ERR_FD can redirect it elsewhere, for use * under ccache. * * The exact setting of log level is a little strange, but for a good * reason: if you ask for verbose, you get everything. Otherwise, if * you set a file, you get INFO and above. Otherwise, you only get * WARNING messages. In practice this seems to be a nice balance. **/ void dcc_set_trace_from_env(void) { const char *logfile, *logfd_name; int fd; if ((logfile = getenv("DISTCC_LOG")) && logfile[0]) { rs_trace_set_level(RS_LOG_INFO); fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0666); if (fd == -1) { /* use stderr instead */ int save_errno = errno; rs_trace_set_level(RS_LOG_WARNING); rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, STDERR_FILENO); rs_log_error("failed to open logfile %s: %s", logfile, strerror(save_errno)); } else { rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, fd); rs_trace_set_level(RS_LOG_INFO); } } else { if ((logfd_name = getenv("UNCACHED_ERR_FD")) == NULL || (fd = atoi(logfd_name)) == 0) { fd = STDERR_FILENO; } rs_trace_set_level(RS_LOG_WARNING); rs_add_logger(rs_logger_file, RS_LOG_DEBUG, NULL, fd); } if (dcc_getenv_bool("DISTCC_VERBOSE", 0)) { rs_trace_set_level(RS_LOG_DEBUG); } }
/** * Copy all server messages to the error file, so that they can be * echoed back to the client if necessary. **/ static int dcc_add_log_to_file(const char *err_fname) { if (dcc_compile_log_fd != -1) { rs_log_crit("compile log already open?"); return 0; /* continue? */ } dcc_compile_log_fd = open(err_fname, O_WRONLY|O_CREAT|O_TRUNC, 0600); if (dcc_compile_log_fd == -1) { rs_log_error("failed to open %s: %s", err_fname, strerror(errno)); return EXIT_IO_ERROR; } /* Only send fairly serious errors back */ rs_add_logger(rs_logger_file, RS_LOG_WARNING, NULL, dcc_compile_log_fd); return 0; }
/** * Setup client error/trace output. * * Trace goes to the file specified by MRCC_LOG, if any. Otherwise, it goes * to stderr, except that UNCACHED_ERR_FD can redirect it elsewhere, for use * under ccache. * * The exact setting of log level is a little strange, but for a good * reason: if you ask for verbose, you get everything. Otherwise, if * you set a file, you get INFO and above. Otherwise, you only get * WARNING messages. In practice this seems to be a nice balance. **/ void set_trace_from_env(void) { const char *logfile, *logfd_name; int fd; int failed_to_open_logfile = 0; int save_errno = 0; int level = RS_LOG_WARNING; /* by default, warnings only */ /* let the decision on what to log rest on the loggers */ /* the email-an-error functionality in emaillog.c depends on this */ rs_trace_set_level(RS_LOG_DEBUG); if ((logfile = getenv("MRCC_LOG")) && logfile[0]) { fd = open(logfile, O_WRONLY|O_APPEND|O_CREAT, 0666); if (fd != -1) { /* asked for a file, and we can open that file: include info messages */ level = RS_LOG_INFO; } else { /* asked for a file, can't use it; use stderr instead */ fd = STDERR_FILENO; save_errno = errno; failed_to_open_logfile = 1; } } else { /* not asked for file */ if ((logfd_name = getenv("UNCACHED_ERR_FD")) == NULL || (fd = atoi(logfd_name)) == 0) { fd = STDERR_FILENO; } } if (getenv_bool("MRCC_VERBOSE", 0)) { level = RS_LOG_DEBUG; } rs_add_logger(rs_logger_file, level, NULL, fd); if (failed_to_open_logfile) { rs_log_error("failed to open logfile %s: %s", logfile, strerror(save_errno)); } }
/* Get the host list from zeroconf */ int dcc_zeroconf_add_hosts(struct dcc_hostdef **ret_list, int *ret_nhosts, int n_slots, struct dcc_hostdef **ret_prev) { char host_file[PATH_MAX], lock_file[PATH_MAX], *s = NULL; int lock_fd = -1, host_fd = -1; int fork_daemon = 0; int r = -1; char *dir; struct stat st; if (get_zeroconf_dir(&dir) != 0) { rs_log_crit("failed to get zeroconf dir.\n"); goto finish; } snprintf(lock_file, sizeof(lock_file), "%s/lock", dir); snprintf(host_file, sizeof(host_file), "%s/hosts", dir); /* Open lock file */ if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno)); goto finish; } /* Try to lock the lock file */ if (generic_lock(lock_fd, 1, 1, 0) >= 0) { /* The lock succeeded => there's no daemon running yet! */ fork_daemon = 1; generic_lock(lock_fd, 1, 0, 0); } close(lock_fd); /* Shall we fork a new daemon? */ if (fork_daemon) { pid_t pid; rs_log_info("Spawning zeroconf daemon.\n"); if ((pid = fork()) == -1) { rs_log_crit("fork() failed: %s\n", strerror(errno)); goto finish; } else if (pid == 0) { int fd; /* Child */ /* Close file descriptors and replace them by /dev/null */ close(0); close(1); close(2); fd = open("/dev/null", O_RDWR); assert(fd == 0); fd = dup(0); assert(fd == 1); fd = dup(0); assert(fd == 2); #ifdef HAVE_SETSID setsid(); #endif chdir("/"); rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); _exit(daemon_proc(host_file, lock_file, n_slots)); } /* Parent */ /* Wait some time for initial host gathering */ usleep(1000000); /* 1000 ms */ } /* Open host list read-only */ if ((host_fd = open(host_file, O_RDONLY)) < 0) { rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno)); goto finish; } /* A read lock */ if (generic_lock(host_fd, 0, 1, 1) < 0) { rs_log_crit("lock failed: %s\n", strerror(errno)); goto finish; } /* Get file size */ if (fstat(host_fd, &st) < 0) { rs_log_crit("stat() failed: %s\n", strerror(errno)); goto finish; } if (st.st_size >= MAX_FILE_SIZE) { rs_log_crit("file too large.\n"); goto finish; } /* read file data */ s = malloc((size_t) st.st_size+1); assert(s); if (dcc_readx(host_fd, s, (size_t) st.st_size) != 0) { rs_log_crit("failed to read from file.\n"); goto finish; } s[st.st_size] = 0; /* Parse host data */ if (dcc_parse_hosts(s, host_file, ret_list, ret_nhosts, ret_prev) != 0) { rs_log_crit("failed to parse host file.\n"); goto finish; } r = 0; finish: if (host_fd >= 0) { generic_lock(host_fd, 0, 0, 1); close(host_fd); } free(s); return r; }
/* The main function of the background daemon */ static int daemon_proc(const char *host_file, const char *lock_file, int n_slots) { int ret = 1; int lock_fd = -1; struct daemon_data d; time_t clip_time; int error; char machine[64], version[64], stype[128]; rs_add_logger(rs_logger_syslog, RS_LOG_DEBUG, NULL, 0); /* Prepare daemon data structure */ d.fd = -1; d.hosts = NULL; d.n_slots = n_slots; d.simple_poll = NULL; d.browser = NULL; d.client = NULL; clip_time = time(NULL); rs_log_info("Zeroconf daemon running.\n"); /* Open daemon lock file and lock it */ if ((lock_fd = open(lock_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", lock_file, strerror(errno)); goto finish; } if (generic_lock(lock_fd, 1, 1, 0) < 0) { /* lock failed, there's probably already another daemon running */ goto finish; } /* Open host file */ if ((d.fd = open(host_file, O_RDWR|O_CREAT, 0666)) < 0) { rs_log_crit("open('%s') failed: %s\n", host_file, strerror(errno)); goto finish; } /* Clear host file */ write_hosts(&d); if (!(d.simple_poll = avahi_simple_poll_new())) { rs_log_crit("Failed to create simple poll object.\n"); goto finish; } if (!(d.client = avahi_client_new( avahi_simple_poll_get(d.simple_poll), 0, client_callback, &d, &error))) { rs_log_crit("Failed to create Avahi client object: %s\n", avahi_strerror(error)); goto finish; } if (dcc_get_gcc_version(version, sizeof(version)) && dcc_get_gcc_machine(machine, sizeof(machine))) { dcc_make_dnssd_subtype(stype, sizeof(stype), version, machine); } else { rs_log_warning("Warning, failed to get CC version and machine type.\n"); strncpy(stype, DCC_DNS_SERVICE_TYPE, sizeof(stype)); stype[sizeof(stype)-1] = 0; } rs_log_info("Browsing for '%s'.\n", stype); if (!(d.browser = avahi_service_browser_new( d.client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, stype, NULL, 0, browse_reply, &d))) { rs_log_crit("Failed to create service browser object: %s\n", avahi_strerror(avahi_client_errno(d.client))); goto finish; } /* Check whether the host file has been used recently */ while (fd_last_used(d.fd, clip_time) <= MAX_IDLE_TIME) { /* Iterate the main loop for 5s */ if (avahi_simple_poll_iterate(d.simple_poll, 5000) != 0) { rs_log_crit("Event loop exited abnormaly.\n"); goto finish; } } /* Wer are idle */ rs_log_info("Zeroconf daemon unused.\n"); ret = 0; finish: /* Cleanup */ if (lock_fd >= 0) { generic_lock(lock_fd, 1, 0, 0); close(lock_fd); } if (d.fd >= 0) close(d.fd); while (d.hosts) { struct host *h = d.hosts; d.hosts = d.hosts->next; free_host(h); } if (d.client) avahi_client_free(d.client); if (d.simple_poll) avahi_simple_poll_free(d.simple_poll); rs_log_info("zeroconf daemon ended.\n"); return ret; }