/* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*- * * distcc -- A simple distributed compiler system * * Copyright (C) 2002, 2003, 2004 by Martin Pool * Copyright 2007 Google Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ /* dopt.c -- Parse and apply server options. */ #include #include #include #include #include #include #include #include #include #include #include #include "types.h" #include "distcc.h" #include "trace.h" #include "dopt.h" #include "exitcode.h" #include "daemon.h" #include "access.h" #include "exec.h" int opt_niceness = 5; /* default */ /** * Number of children running jobs on this machine. If zero (recommended), * then dynamically set from the number of CPUs. **/ int arg_max_jobs = 0; #ifdef HAVE_GSSAPI /* If true perform GSS-API based authentication. */ int opt_auth_enabled = 0; /* Control access through a specified list file. */ int opt_blacklist_enabled = 0; int opt_whitelist_enabled = 0; const char *arg_list_file = NULL; #endif int arg_port = DISTCC_DEFAULT_PORT; int arg_stats = DISTCC_DEFAULT_STATS_ENABLED; int arg_stats_port = DISTCC_DEFAULT_STATS_PORT; /** If true, serve all requests directly from listening process without forking. Better for debugging. **/ int opt_no_fork = 0; int opt_daemon_mode = 0; int opt_inetd_mode = 0; int opt_no_fifo = 0; /** If non-NULL, listen on only this address. **/ char *opt_listen_addr = NULL; struct dcc_allow_list *opt_allowed = NULL; int opt_allow_private = 0; /** * If true, don't detach from the parent. This is probably necessary * for use with daemontools or other monitoring programs, and is also * used by the test suite. **/ int opt_no_detach = 0; int opt_log_stderr = 0; int opt_log_level_num = RS_LOG_NOTICE; /** * If true, do not check if a link to distcc exists in /usr/lib/distcc * for every program executed remotely. **/ int opt_make_me_a_botnet = 0; /** * Daemon exits after this many seconds. Intended mainly for testing, to make * sure daemons don't persist for too long. */ int opt_lifetime = 0; const char *arg_pid_file = NULL; const char *arg_log_file = NULL; int opt_job_lifetime = 0; /* Enumeration values for options that don't have single-letter name. These * must be numerically above all the ascii letters. */ enum { opt_log_to_file = 300, opt_log_level }; #ifdef HAVE_AVAHI /* Flag for enabling/disabling Zeroconf using Avahi */ int opt_zeroconf = 0; #endif static const char *dcc_private_networks[] = {"192.168.0.0/16", "10.0.0.0/8", "172.16.0.0/12", "127.0.0.0/8", "fe80::/10", "fc00::/7", "::1/128"}; const struct poptOption options[] = { { "allow", 'a', POPT_ARG_STRING, 0, 'a', 0, 0 }, { "allow-private", 0,POPT_ARG_NONE, &opt_allow_private, 0, 0, 0 }, #ifdef HAVE_GSSAPI { "auth", 0, POPT_ARG_NONE, &opt_auth_enabled, 'A', 0, 0 }, { "blacklist", 0, POPT_ARG_STRING, &arg_list_file, 'b', 0, 0 }, #endif { "jobs", 'j', POPT_ARG_INT, &arg_max_jobs, 'j', 0, 0 }, { "daemon", 0, POPT_ARG_NONE, &opt_daemon_mode, 0, 0, 0 }, { "help", 0, POPT_ARG_NONE, 0, '?', 0, 0 }, { "inetd", 0, POPT_ARG_NONE, &opt_inetd_mode, 0, 0, 0 }, { "lifetime", 0, POPT_ARG_INT, &opt_lifetime, 0, 0, 0 }, { "listen", 0, POPT_ARG_STRING, &opt_listen_addr, 0, 0, 0 }, { "log-file", 0, POPT_ARG_STRING, &arg_log_file, 0, 0, 0 }, { "log-level", 0, POPT_ARG_STRING, 0, opt_log_level, 0, 0 }, { "log-stderr", 0, POPT_ARG_NONE, &opt_log_stderr, 0, 0, 0 }, { "job-lifetime", 0, POPT_ARG_INT, &opt_job_lifetime, 'l', 0, 0 }, { "nice", 'N', POPT_ARG_INT, &opt_niceness, 0, 0, 0 }, { "no-detach", 0, POPT_ARG_NONE, &opt_no_detach, 0, 0, 0 }, { "no-fifo", 0, POPT_ARG_NONE, &opt_no_fifo, 0, 0, 0 }, { "no-fork", 0, POPT_ARG_NONE, &opt_no_fork, 0, 0, 0 }, { "pid-file", 'P', POPT_ARG_STRING, &arg_pid_file, 0, 0, 0 }, { "port", 'p', POPT_ARG_INT, &arg_port, 0, 0, 0 }, #ifdef HAVE_GSSAPI { "show-principal", 0, POPT_ARG_NONE, 0, 'P', 0, 0 }, #endif { "user", 0, POPT_ARG_STRING, &opt_user, 'u', 0, 0 }, { "verbose", 0, POPT_ARG_NONE, 0, 'v', 0, 0 }, { "version", 0, POPT_ARG_NONE, 0, 'V', 0, 0 }, #ifdef HAVE_GSSAPI { "whitelist", 0, POPT_ARG_STRING, &arg_list_file, 'w', 0, 0 }, #endif { "wizard", 'W', POPT_ARG_NONE, 0, 'W', 0, 0 }, { "stats", 0, POPT_ARG_NONE, &arg_stats, 0, 0, 0 }, { "stats-port", 0, POPT_ARG_INT, &arg_stats_port, 0, 0, 0 }, #ifdef HAVE_AVAHI { "zeroconf", 0, POPT_ARG_NONE, &opt_zeroconf, 0, 0, 0 }, #endif { "make-me-a-botnet", 0, POPT_ARG_NONE, &opt_make_me_a_botnet, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 } }; static void distccd_show_usage(void) { dcc_show_version("distccd"); printf ( "Usage:\n" " distccd [OPTIONS]\n" "\n" "Options:\n" " --help explain usage and exit\n" " --version show version and exit\n" #ifdef HAVE_GSSAPI " --show-principal show current GSS-API principal and exit\n" #endif " -P, --pid-file FILE save daemon process id to file\n" " -N, --nice LEVEL lower priority, 20=most nice\n" " --user USER if run by root, change to this persona\n" " --jobs, -j LIMIT maximum tasks at any time\n" " --job-lifetime SECONDS maximum lifetime of a compile request\n" " Networking:\n" " -p, --port PORT TCP port to listen on\n" " --listen ADDRESS IP address to listen on\n" " -a, --allow IP[/BITS] client address access control\n" #ifdef HAVE_GSSAPI " --auth enable GSS-API based mutual authenticaton\n" " --blacklist=FILE control client access through a blacklist\n" " --whitelist=FILE control client access through a whitelist\n" #endif " --stats enable statistics reporting via HTTP server\n" " --stats-port PORT TCP port to listen on for statistics requests\n" #ifdef HAVE_AVAHI " --zeroconf register via mDNS/DNS-SD\n" #endif " Debug and trace:\n" " --log-level=LEVEL set detail level for log file\n" " levels: critical, error, warning, notice, info, debug\n" " --verbose set log level to \"debug\"\n" " --no-detach don't detach from parent (for daemontools, etc)\n" " --log-file=FILE send messages here instead of syslog\n" " --log-stderr send messages to stderr\n" " --wizard for running under gdb\n" " Mode of operation:\n" " --inetd serve client connected to stdin\n" " --daemon bind and listen on socket\n" "\n" "distccd runs either from inetd or as a standalone daemon to compile\n" "files submitted by the distcc client.\n" "\n" "distccd should only run on trusted networks.\n" ); } #ifdef HAVE_GSSAPI /* * Print out the name of the principal. */ static void dcc_gssapi_show_principal(void) { char *princ_env_val = NULL; if ((princ_env_val = getenv("DISTCCD_PRINCIPAL"))) { printf("Principal is\t: %s\n", princ_env_val); } else { printf("Principal\t: Not Set\n"); } } #endif int distccd_parse_options(int argc, const char **argv) { poptContext po; int po_err, exitcode; struct dcc_allow_list *new; po = poptGetContext("distccd", argc, argv, options, 0); while ((po_err = poptGetNextOpt(po)) != -1) { switch (po_err) { case '?': distccd_show_usage(); exitcode = 0; goto out_exit; case 'a': { /* TODO: Allow this to be a hostname, which is resolved to an address. */ /* TODO: Split this into a small function. */ new = malloc(sizeof *new); if (!new) { rs_log_crit("malloc failed"); exitcode = EXIT_OUT_OF_MEMORY; goto out_exit; } new->next = opt_allowed; opt_allowed = new; if ((exitcode = dcc_parse_mask(poptGetOptArg(po), &new->addr, &new->mask))) goto out_exit; } break; #ifdef HAVE_GSSAPI /* Set the flag to indicate that authentication is requested. */ case 'A': { if (opt_auth_enabled < 0) { opt_auth_enabled = 0; } dcc_auth_enabled = opt_auth_enabled; break; } case 'b': { if (opt_whitelist_enabled) { rs_log_error("Can't specify both --whitelist and --blacklist."); exitcode = EXIT_BAD_ARGUMENTS; goto out_exit; } else { opt_blacklist_enabled = 1; } break; } #endif case 'j': if (arg_max_jobs < 1 || arg_max_jobs > 200) { rs_log_error("--jobs argument must be between 1 and 200"); exitcode = EXIT_BAD_ARGUMENTS; goto out_exit; } break; case 'l': if (opt_job_lifetime < 0) { opt_job_lifetime = 0; } dcc_job_lifetime = opt_job_lifetime; break; #ifdef HAVE_GSSAPI case 'P': { dcc_gssapi_show_principal(); exitcode = 0; goto out_exit; } #endif case 'u': if (getuid() != 0 && geteuid() != 0) { rs_log_warning("--user is ignored when distccd is not run by root"); /* continue */ } break; case 'V': dcc_show_version("distccd"); exitcode = EXIT_SUCCESS; goto out_exit; case opt_log_level: { int level; const char *level_name; level_name = poptGetOptArg(po); level = rs_loglevel_from_name(level_name); if (level == -1) { rs_log_warning("invalid --log-level argument \"%s\"", level_name); } else { rs_trace_set_level(level); opt_log_level_num = level; } } break; case 'v': rs_trace_set_level(RS_LOG_DEBUG); opt_log_level_num = RS_LOG_DEBUG; break; #ifdef HAVE_GSSAPI case 'w': { if (opt_blacklist_enabled) { rs_log_error("Can't specify both --blacklist and --whitelist."); exitcode = EXIT_BAD_ARGUMENTS; goto out_exit; } else { opt_whitelist_enabled = 1; } break; } #endif case 'W': /* catchall for running under gdb */ opt_log_stderr = 1; opt_daemon_mode = 1; opt_no_detach = 1; opt_no_fork = 1; opt_no_fifo = 1; rs_trace_set_level(RS_LOG_DEBUG); opt_log_level_num = RS_LOG_DEBUG; break; default: /* bad? */ rs_log(RS_LOG_NONAME|RS_LOG_ERR|RS_LOG_NO_PID, "%s: %s", poptBadOption(po, POPT_BADOPTION_NOALIAS), poptStrerror(po_err)); exitcode = EXIT_BAD_ARGUMENTS; goto out_exit; } } if (opt_allow_private) { int i; for (i = 0;i<6;i++) { new = malloc(sizeof *new); if (!new) { rs_log_crit("malloc failed"); exitcode = EXIT_OUT_OF_MEMORY; goto out_exit; } new->next = opt_allowed; opt_allowed = new; if ((exitcode = dcc_parse_mask(dcc_private_networks[i], &new->addr, &new->mask))) goto out_exit; } } poptFreeContext(po); return 0; out_exit: poptFreeContext(po); exit(exitcode); }