#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include "exitcode.h"
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "dopt.h"
#include "exec.h"
#include "srvnet.h"
#include "types.h"
#include "daemon.h"
#include "netutil.h"
#include "zeroconf.h"
#ifdef XCODE_INTEGRATION
#include "xci.h"
#endif
static void dcc_nofork_parent(int listen_fd) NORETURN;
static void dcc_detach(void);
static void dcc_save_pid(pid_t);
int dcc_nkids = 0;
int dcc_max_kids = 0;
int dcc_standalone_server(void)
{
int listen_fd;
int n_cpus;
int ret;
#ifdef HAVE_AVAHI
#if 0 // http://code.google.com/p/toolwhip/issues/detail?id=3
void *avahi = NULL;
#endif // http://code.google.com/p/toolwhip/issues/detail?id=3
#endif
if ((ret = dcc_socket_listen(arg_port, &listen_fd, opt_listen_addr)) != 0)
return ret;
dcc_defer_accept(listen_fd);
set_cloexec_flag(listen_fd, 1);
if (dcc_ncpus(&n_cpus) == 0)
rs_log_info("%d CPU%s online on this server", n_cpus, n_cpus == 1 ? "" : "s");
if (arg_max_jobs)
dcc_max_kids = arg_max_jobs;
else
dcc_max_kids = 2 + n_cpus;
rs_log_info("allowing up to %d active jobs", dcc_max_kids);
if (!opt_no_detach) {
dcc_detach();
} else {
rs_trace("not detaching");
if ((ret = dcc_new_pgrp()) != 0)
return ret;
dcc_save_pid(getpid());
}
dcc_daemon_catch_signals();
#if defined(HAVE_AVAHI) || (defined(XCODE_INTEGRATION) && defined(HAVE_DNSSD))
if (opt_zeroconf) {
#ifdef HAVE_AVAHI
#if 0 // http://code.google.com/p/toolwhip/issues/detail?id=3
if (!(avahi = dcc_zeroconf_register((uint16_t) arg_port, n_cpus)))
return EXIT_CONNECT_FAILED;
#endif // http://code.google.com/p/toolwhip/issues/detail?id=3
#endif
#ifdef XCODE_INTEGRATION
dcc_xci_zeroconf_register();
#endif
}
#endif
dcc_master_pid = getpid();
if (opt_no_fork) {
dcc_log_daemon_started("non-forking daemon");
dcc_nofork_parent(listen_fd);
ret = 0;
} else {
dcc_log_daemon_started("preforking daemon");
ret = dcc_preforking_parent(listen_fd);
}
#ifdef HAVE_AVAHI
#if 0 // http://code.google.com/p/toolwhip/issues/detail?id=3
if (opt_zeroconf) {
if (dcc_zeroconf_unregister(avahi) != 0)
return EXIT_CONNECT_FAILED;
}
#endif // http://code.google.com/p/toolwhip/issues/detail?id=3
#endif
return ret;
}
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));
}
}
void dcc_reap_kids(int must_reap)
{
while (1) {
int status;
pid_t kid;
kid = waitpid(WAIT_ANY, &status, must_reap ? 0 : WNOHANG);
if (kid == 0) {
break;
} else if (kid != -1) {
--dcc_nkids;
rs_trace("down to %d children", dcc_nkids);
dcc_log_child_exited(kid, status);
} else if (errno == ECHILD) {
break;
} else if (errno == EINTR) {
continue;
} else {
rs_log_error("wait failed: %s", strerror(errno));
dcc_exit(EXIT_DISTCC_FAILED);
}
must_reap = FALSE;
}
}
static void dcc_nofork_parent(int listen_fd)
{
while (1) {
int acc_fd;
struct dcc_sockaddr_storage cli_addr;
socklen_t cli_len;
rs_log_info("waiting to accept connection");
cli_len = sizeof cli_addr;
acc_fd = accept(listen_fd,
(struct sockaddr *) &cli_addr, &cli_len);
if (acc_fd == -1 && errno == EINTR) {
;
} else if (acc_fd == -1) {
rs_log_error("accept failed: %s", strerror(errno));
dcc_exit(EXIT_CONNECT_FAILED);
} else {
dcc_service_job(acc_fd, acc_fd, (struct sockaddr *) &cli_addr, cli_len);
dcc_close(acc_fd);
}
}
}
static void dcc_save_pid(pid_t pid)
{
FILE *fp;
if (!arg_pid_file)
return;
if (!(fp = fopen(arg_pid_file, "wt"))) {
rs_log_error("failed to open pid file: %s: %s", arg_pid_file,
strerror(errno));
return;
}
fprintf(fp, "%ld\n", (long) pid);
if (fclose(fp) == -1) {
rs_log_error("failed to close pid file: %s: %s", arg_pid_file,
strerror(errno));
return;
}
atexit(dcc_remove_pid);
}
void dcc_remove_pid(void)
{
if (!arg_pid_file)
return;
if (unlink(arg_pid_file)) {
rs_log_warning("failed to remove pid file %s: %s",
arg_pid_file, strerror(errno));
}
}
static void dcc_detach(void)
{
int i;
pid_t pid;
pid_t sid;
dcc_ignore_sighup();
if ((pid = fork()) == -1) {
rs_log_error("fork failed: %s", strerror(errno));
exit(EXIT_DISTCC_FAILED);
} else if (pid != 0) {
dcc_save_pid(pid);
_exit(0);
}
#ifdef HAVE_SETSID
if ((sid = setsid()) == -1) {
rs_log_error("setsid failed: %s", strerror(errno));
} else {
rs_trace("setsid to session %d", (int) sid);
}
#else
#ifdef TIOCNOTTY
i = open("/dev/tty", O_RDWR);
if (i >= 0) {
ioctl(i, (int) TIOCNOTTY, (char *)0);
close(i);
}
#endif
#endif
for (i=0;i<3;i++) {
close(i);
open("/dev/null", O_RDWR);
}
dcc_set_lifetime();
}