/** * Run @p argv in a child asynchronously. * * stdin, stdout and stderr are redirected as shown, unless those * filenames are NULL. In that case they are left alone. * * @warning When called on the daemon, where stdin/stdout may refer to random * network sockets, all of the standard file descriptors must be redirected! **/ int dcc_spawn_child(char **argv, pid_t *pidptr, const char *stdin_file, const char *stdout_file, const char *stderr_file) { pid_t pid; dcc_trace_argv("forking to execute", argv); pid = fork(); if (pid == -1) { rs_log_error("failed to fork: %s", strerror(errno)); return EXIT_OUT_OF_MEMORY; /* probably */ } else if (pid == 0) { /* If this is a remote compile, * put the child in a new group, so we can * kill it and all its descendents without killing distccd * FIXME: if you kill distccd while it's compiling, and * the compiler has an infinite loop bug, the new group * will run forever until you kill it. */ if (stdout_file != NULL) { if (dcc_new_pgrp() != 0) rs_trace("Unable to start a new group\n"); } dcc_inside_child(argv, stdin_file, stdout_file, stderr_file); /* !! NEVER RETURN FROM HERE !! */ } else { *pidptr = pid; rs_trace("child started as pid%d", (int) pid); return 0; } }
/** * Be a standalone server, with responsibility for sockets and forking * children. Puts the daemon in the background and detaches from the * controlling tty. **/ int dcc_standalone_server(void) { int listen_fd; int n_cpus; int ret; #ifdef HAVE_AVAHI void *avahi = NULL; #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"); /* By default, allow one job per CPU, plus two for the pot. The extra * ones are started to allow for a bit of extra concurrency so that the * machine is not idle waiting for disk or network IO. */ 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) { /* Don't go into the background until we're listening and * ready. This is useful for testing -- when the daemon * detaches, we know we can go ahead and try to connect. */ dcc_detach(); } else { /* Still create a new process group, even if not detached */ rs_trace("not detaching"); if ((ret = dcc_new_pgrp()) != 0) return ret; dcc_save_pid(getpid()); } /* Don't catch signals until we've detached or created a process group. */ dcc_daemon_catch_signals(); #ifdef HAVE_AVAHI /* Zeroconf registration */ if (opt_zeroconf) { if (!(avahi = dcc_zeroconf_register((uint16_t) arg_port, n_cpus, dcc_max_kids))) return EXIT_CONNECT_FAILED; } #endif /* This is called in the master daemon, whether that is detached or * not. */ 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 /* Remove zeroconf registration */ if (opt_zeroconf) { if (dcc_zeroconf_unregister(avahi) != 0) return EXIT_CONNECT_FAILED; } #endif return ret; }