#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 "io.h"
#include "util.h"
#include "dopt.h"
#include "exec.h"
#include "tempfile.h"
#include "srvnet.h"
#include "indirect_server.h"
#include "indirect_util.h"
#include "zeroconf_reg.h"
#include "zeroconf_util.h"
#ifndef WAIT_ANY
# define WAIT_ANY (-1)
#endif
static int dcc_start_child(int listen_fd, int);
static void dcc_parent_loop(int listen_fd) NORETURN;
static int dcc_serve_connection(int, int acc_fd);
void dcc_detach(void);
static void dcc_save_pid(pid_t);
void dcc_reap_kids(void);
static int nkids = 0;
int dcc_standalone_server(void)
{
int listen_fd;
int n_cpus;
#if defined(DARWIN)
char *zcTxtRecord;
#endif // DARWIN
if ((listen_fd = open_socket_in(arg_port)) == -1)
return EXIT_BIND_FAILED;
set_cloexec_flag(listen_fd, 1);
rs_log(RS_LOG_INFO|RS_LOG_NONAME,
"distccd (version %s, built %s %s) listening on port %d",
PACKAGE_VERSION, __DATE__, __TIME__, arg_port);
if (dcc_ncpus(&n_cpus) == 0)
rs_log_info("%d CPU%s online", n_cpus, n_cpus == 1 ? "" : "s");
dcc_catch_signals();
#if defined(DARWIN)
zcTxtRecord = dcc_generate_txt_record();
#endif
if (!opt_no_detach) {
dcc_detach();
} else {
dcc_save_pid(getpid());
}
#if defined(DARWIN)
dcc_register_for_zeroconfig(zcTxtRecord);
#endif
dcc_parent_loop(listen_fd);
}
void dcc_reap_kids(void)
{
while (1) {
int status;
pid_t kid;
kid = waitpid(WAIT_ANY, &status, WNOHANG);
if (kid == 0) {
break;
} else if (kid != -1) {
--nkids;
rs_log_info("down to %d children", nkids);
if (WIFSIGNALED(status)) {
rs_log_error("child %d exited on signal %d",
(int) kid, WTERMSIG(status));
} else if (WIFEXITED(status)) {
rs_log_notice("child %d exited: exit status %d",
(int) kid, WEXITSTATUS(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);
}
}
}
static void dcc_parent_loop(int listen_fd)
{
while (1) {
int acc_fd;
rs_log_info("waiting to accept connection");
acc_fd = accept(listen_fd, NULL, 0);
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_serve_connection(listen_fd, acc_fd);
}
if (!opt_no_fork) {
dcc_reap_kids();
}
}
}
static int dcc_serve_connection(int listen_fd, int acc_fd)
{
int ret = 0;
if (opt_no_fork) {
ret = dcc_accept_job(acc_fd);
dcc_cleanup_tempfiles();
} else {
dcc_start_child(listen_fd, acc_fd);
++nkids;
rs_log_info("up to %d children", nkids);
}
if (close(acc_fd)) {
rs_log_error("failed to close accepted fd%d: %s", acc_fd,
strerror(errno));
}
return ret;
}
static int dcc_start_child(int listen_fd, int accepted_fd)
{
pid_t kid;
int ret, close_ret;
kid = fork();
if (kid == 0) {
close(listen_fd);
#if defined(DARWIN)
dcc_support_indirection(&accepted_fd);
#endif // DARWIN
ret = dcc_accept_job(accepted_fd);
close_ret = dcc_close(accepted_fd);
dcc_exit(ret ? ret : close_ret);
} else if (kid == -1) {
rs_log_error("fork failed: %s", strerror(errno));
return -1;
}
return 0;
}
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));
}
}
void dcc_detach(void)
{
int i;
pid_t pid;
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
setsid();
#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);
}
#if ! defined(DARWIN)
dcc_set_lifetime();
#endif // ! DARWIN
}