1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2  *
3  * distcc -- A simple distributed compiler system
4  *
5  * Copyright (C) 2002, 2003, 2004 by Martin Pool <mbp@samba.org>
6  * Copyright 2007 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21  * USA.
22  */
23 
24 
25 /* dopt.c -- Parse and apply server options. */
26 
27 #include <config.h>
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <popt.h>
36 
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 
41 #include "types.h"
42 #include "distcc.h"
43 #include "trace.h"
44 #include "dopt.h"
45 #include "exitcode.h"
46 #include "daemon.h"
47 #include "access.h"
48 #include "exec.h"
49 
50 int opt_niceness = 5;           /* default */
51 
52 /**
53  * Number of children running jobs on this machine.  If zero (recommended),
54  * then dynamically set from the number of CPUs.
55  **/
56 int arg_max_jobs = 0;
57 
58 #ifdef HAVE_GSSAPI
59 /* If true perform GSS-API based authentication. */
60 int opt_auth_enabled = 0;
61 /* Control access through a specified list file. */
62 int opt_blacklist_enabled = 0;
63 int opt_whitelist_enabled = 0;
64 const char *arg_list_file = NULL;
65 #endif
66 
67 int arg_port = DISTCC_DEFAULT_PORT;
68 int arg_stats = DISTCC_DEFAULT_STATS_ENABLED;
69 int arg_stats_port = DISTCC_DEFAULT_STATS_PORT;
70 
71 /** If true, serve all requests directly from listening process
72     without forking.  Better for debugging. **/
73 int opt_no_fork = 0;
74 
75 int opt_daemon_mode = 0;
76 int opt_inetd_mode = 0;
77 int opt_no_fifo = 0;
78 
79 /** If non-NULL, listen on only this address. **/
80 char *opt_listen_addr = NULL;
81 
82 struct dcc_allow_list *opt_allowed = NULL;
83 
84 int opt_allow_private = 0;
85 
86 /**
87  * If true, don't detach from the parent.  This is probably necessary
88  * for use with daemontools or other monitoring programs, and is also
89  * used by the test suite.
90  **/
91 int opt_no_detach = 0;
92 
93 int opt_log_stderr = 0;
94 
95 int opt_log_level_num = RS_LOG_NOTICE;
96 
97 /**
98  * If true, do not check if a link to distcc exists in /usr/lib/distcc
99  * for every program executed remotely.
100  **/
101 int opt_enable_tcp_insecure = 0;
102 
103 /**
104  * Daemon exits after this many seconds.  Intended mainly for testing, to make
105  * sure daemons don't persist for too long.
106  */
107 int opt_lifetime = 0;
108 
109 const char *arg_pid_file = NULL;
110 const char *arg_log_file = NULL;
111 
112 int opt_job_lifetime = 0;
113 
114 /* Enumeration values for options that don't have single-letter name.  These
115  * must be numerically above all the ascii letters. */
116 enum {
117     opt_log_to_file = 300,
118     opt_log_level
119 };
120 
121 #ifdef HAVE_AVAHI
122 /* Flag for enabling/disabling Zeroconf using Avahi */
123 int opt_zeroconf = 0;
124 #endif
125 
126 
127 static const char *dcc_private_networks[] = {"192.168.0.0/16",
128                                              "10.0.0.0/8",
129                                              "172.16.0.0/12",
130                                              "127.0.0.0/8",
131 
132                                              "fe80::/10",
133                                               "fc00::/7",
134                                               "::1/128"};
135 
136 const struct poptOption options[] = {
137     { "allow", 'a',      POPT_ARG_STRING, 0, 'a', 0, 0 },
138     { "allow-private", 0,POPT_ARG_NONE, &opt_allow_private, 0, 0, 0 },
139 #ifdef HAVE_GSSAPI
140     { "auth", 0,	 POPT_ARG_NONE, &opt_auth_enabled, 'A', 0, 0 },
141     { "blacklist", 0,    POPT_ARG_STRING, &arg_list_file, 'b', 0, 0 },
142 #endif
143     { "jobs", 'j',       POPT_ARG_INT, &arg_max_jobs, 'j', 0, 0 },
144     { "daemon", 0,       POPT_ARG_NONE, &opt_daemon_mode, 0, 0, 0 },
145     { "help", 0,         POPT_ARG_NONE, 0, '?', 0, 0 },
146     { "inetd", 0,        POPT_ARG_NONE, &opt_inetd_mode, 0, 0, 0 },
147     { "lifetime", 0,     POPT_ARG_INT, &opt_lifetime, 0, 0, 0 },
148     { "listen", 0,       POPT_ARG_STRING, &opt_listen_addr, 0, 0, 0 },
149     { "log-file", 0,     POPT_ARG_STRING, &arg_log_file, 0, 0, 0 },
150     { "log-level", 0,    POPT_ARG_STRING, 0, opt_log_level, 0, 0 },
151     { "log-stderr", 0,   POPT_ARG_NONE, &opt_log_stderr, 0, 0, 0 },
152     { "job-lifetime", 0, POPT_ARG_INT, &opt_job_lifetime, 'l', 0, 0 },
153     { "nice", 'N',       POPT_ARG_INT,  &opt_niceness,  0, 0, 0 },
154     { "no-detach", 0,    POPT_ARG_NONE, &opt_no_detach, 0, 0, 0 },
155     { "no-fifo", 0,      POPT_ARG_NONE, &opt_no_fifo, 0, 0, 0 },
156     { "no-fork", 0,      POPT_ARG_NONE, &opt_no_fork, 0, 0, 0 },
157     { "pid-file", 'P',   POPT_ARG_STRING, &arg_pid_file, 0, 0, 0 },
158     { "port", 'p',       POPT_ARG_INT, &arg_port, 0, 0, 0 },
159 #ifdef HAVE_GSSAPI
160     { "show-principal", 0,	 POPT_ARG_NONE, 0, 'P', 0, 0 },
161 #endif
162     { "user", 0,         POPT_ARG_STRING, &opt_user, 'u', 0, 0 },
163     { "verbose", 0,      POPT_ARG_NONE, 0, 'v', 0, 0 },
164     { "version", 0,      POPT_ARG_NONE, 0, 'V', 0, 0 },
165 #ifdef HAVE_GSSAPI
166     { "whitelist", 0,    POPT_ARG_STRING, &arg_list_file, 'w', 0, 0 },
167 #endif
168     { "wizard", 'W',     POPT_ARG_NONE, 0, 'W', 0, 0 },
169     { "stats", 0,        POPT_ARG_NONE, &arg_stats, 0, 0, 0 },
170     { "stats-port", 0,   POPT_ARG_INT, &arg_stats_port, 0, 0, 0 },
171 #ifdef HAVE_AVAHI
172     { "zeroconf", 0,     POPT_ARG_NONE, &opt_zeroconf, 0, 0, 0 },
173 #endif
174     { "make-me-a-botnet", 0, POPT_ARG_NONE, &opt_enable_tcp_insecure, 0, 0, 0 },
175     { "enable-tcp-insecure", 0, POPT_ARG_NONE, &opt_enable_tcp_insecure, 0, 0, 0 },
176     { 0, 0, 0, 0, 0, 0, 0 }
177 };
178 
distccd_show_usage(void)179 static void distccd_show_usage(void)
180 {
181     dcc_show_version("distccd");
182     printf (
183 "Usage:\n"
184 "   distccd [OPTIONS]\n"
185 "\n"
186 "Options:\n"
187 "    --help                     explain usage and exit\n"
188 "    --version                  show version and exit\n"
189 #ifdef HAVE_GSSAPI
190 "    --show-principal           show current GSS-API principal and exit\n"
191 #endif
192 "    -P, --pid-file FILE        save daemon process id to file\n"
193 "    -N, --nice LEVEL           lower priority, 20=most nice\n"
194 "    --user USER                if run by root, change to this persona\n"
195 "    --jobs, -j LIMIT           maximum tasks at any time\n"
196 "    --job-lifetime SECONDS     maximum lifetime of a compile request\n"
197 "  Networking:\n"
198 "    -p, --port PORT            TCP port to listen on\n"
199 "    --listen ADDRESS           IP address to listen on\n"
200 "    -a, --allow IP[/BITS]      client address access control\n"
201 #ifdef HAVE_GSSAPI
202 "    --auth                     enable GSS-API based mutual authenticaton\n"
203 "    --blacklist=FILE           control client access through a blacklist\n"
204 "    --whitelist=FILE           control client access through a whitelist\n"
205 #endif
206 "    --stats                    enable statistics reporting via HTTP server\n"
207 "    --stats-port PORT          TCP port to listen on for statistics requests\n"
208 #ifdef HAVE_AVAHI
209 "    --zeroconf                 register via mDNS/DNS-SD\n"
210 #endif
211 "  Debug and trace:\n"
212 "    --log-level=LEVEL          set detail level for log file\n"
213 "      levels: critical, error, warning, notice, info, debug\n"
214 "    --verbose                  set log level to \"debug\"\n"
215 "    --no-detach                don't detach from parent (for daemontools, etc)\n"
216 "    --log-file=FILE            send messages here instead of syslog\n"
217 "    --log-stderr               send messages to stderr\n"
218 "    --wizard                   for running under gdb\n"
219 "  Mode of operation:\n"
220 "    --inetd                    serve client connected to stdin\n"
221 "    --daemon                   bind and listen on socket\n"
222 "\n"
223 "distccd runs either from inetd or as a standalone daemon to compile\n"
224 "files submitted by the distcc client.\n"
225 "\n"
226 "distccd should only run on trusted networks.\n"
227 );
228 }
229 
230 #ifdef HAVE_GSSAPI
231 /*
232  * Print out the name of the principal.
233  */
dcc_gssapi_show_principal(void)234 static void dcc_gssapi_show_principal(void) {
235     char *princ_env_val = NULL;
236 
237     if ((princ_env_val = getenv("DISTCCD_PRINCIPAL"))) {
238 	    printf("Principal is\t: %s\n", princ_env_val);
239     } else {
240         printf("Principal\t: Not Set\n");
241     }
242 }
243 #endif
244 
distccd_parse_options(int argc,const char ** argv)245 int distccd_parse_options(int argc, const char **argv)
246 {
247     poptContext po;
248     int po_err, exitcode;
249     struct dcc_allow_list *new;
250 
251     po = poptGetContext("distccd", argc, argv, options, 0);
252 
253     while ((po_err = poptGetNextOpt(po)) != -1) {
254         switch (po_err) {
255         case '?':
256             distccd_show_usage();
257             exitcode = 0;
258             goto out_exit;
259 
260         case 'a': {
261             /* TODO: Allow this to be a hostname, which is resolved to an address. */
262             /* TODO: Split this into a small function. */
263             new = malloc(sizeof *new);
264             if (!new) {
265                 rs_log_crit("malloc failed");
266                 exitcode = EXIT_OUT_OF_MEMORY;
267                 goto out_exit;
268             }
269             new->next = opt_allowed;
270             opt_allowed = new;
271             if ((exitcode = dcc_parse_mask(poptGetOptArg(po), &new->addr, &new->mask)))
272                 goto out_exit;
273         }
274             break;
275 
276 #ifdef HAVE_GSSAPI
277 	    /* Set the flag to indicate that authentication is requested. */
278         case 'A': {
279 	        if (opt_auth_enabled < 0) {
280 		        opt_auth_enabled = 0;
281             }
282 
283 	        dcc_auth_enabled = opt_auth_enabled;
284 	        break;
285         }
286 
287         case 'b': {
288             if (opt_whitelist_enabled) {
289 	            rs_log_error("Can't specify both --whitelist and --blacklist.");
290                 exitcode = EXIT_BAD_ARGUMENTS;
291                 goto out_exit;
292 	        } else {
293 		        opt_blacklist_enabled = 1;
294 	        }
295 
296 	        break;
297 	    }
298 #endif
299 
300         case 'j':
301             if (arg_max_jobs < 1 || arg_max_jobs > 200) {
302                 rs_log_error("--jobs argument must be between 1 and 200");
303                 exitcode = EXIT_BAD_ARGUMENTS;
304                 goto out_exit;
305             }
306             break;
307 
308         case 'l':
309             if (opt_job_lifetime < 0) {
310                 opt_job_lifetime = 0;
311             }
312             dcc_job_lifetime = opt_job_lifetime;
313             break;
314 
315 #ifdef HAVE_GSSAPI
316         case 'P': {
317 	        dcc_gssapi_show_principal();
318 	        exitcode = 0;
319             goto out_exit;
320         }
321 #endif
322 
323         case 'u':
324             if (getuid() != 0 && geteuid() != 0) {
325                 rs_log_warning("--user is ignored when distccd is not run by root");
326                 /* continue */
327             }
328             break;
329 
330         case 'V':
331             dcc_show_version("distccd");
332             exitcode = EXIT_SUCCESS;
333             goto out_exit;
334 
335         case opt_log_level:
336             {
337                 int level;
338                 const char *level_name;
339 
340                 level_name = poptGetOptArg(po);
341                 level = rs_loglevel_from_name(level_name);
342                 if (level == -1) {
343                     rs_log_warning("invalid --log-level argument \"%s\"",
344                                    level_name);
345                 } else {
346                     rs_trace_set_level(level);
347                     opt_log_level_num = level;
348                 }
349             }
350             break;
351 
352         case 'v':
353             rs_trace_set_level(RS_LOG_DEBUG);
354             opt_log_level_num = RS_LOG_DEBUG;
355             break;
356 
357 #ifdef HAVE_GSSAPI
358 	    case 'w': {
359             if (opt_blacklist_enabled) {
360 	            rs_log_error("Can't specify both --blacklist and --whitelist.");
361                 exitcode = EXIT_BAD_ARGUMENTS;
362                 goto out_exit;
363 	        } else {
364 		        opt_whitelist_enabled = 1;
365 	        }
366 
367 	        break;
368 	    }
369 #endif
370 
371         case 'W':
372             /* catchall for running under gdb */
373             opt_log_stderr = 1;
374             opt_daemon_mode = 1;
375             opt_no_detach = 1;
376             opt_no_fork = 1;
377             opt_no_fifo = 1;
378             rs_trace_set_level(RS_LOG_DEBUG);
379             opt_log_level_num = RS_LOG_DEBUG;
380             break;
381 
382         default:                /* bad? */
383             rs_log(RS_LOG_NONAME|RS_LOG_ERR|RS_LOG_NO_PID, "%s: %s",
384                    poptBadOption(po, POPT_BADOPTION_NOALIAS),
385                    poptStrerror(po_err));
386             exitcode = EXIT_BAD_ARGUMENTS;
387             goto out_exit;
388         }
389     }
390 
391     if (opt_allow_private) {
392         int i;
393         for (i = 0;i<6;i++) {
394             new = malloc(sizeof *new);
395             if (!new) {
396                 rs_log_crit("malloc failed");
397                 exitcode = EXIT_OUT_OF_MEMORY;
398                 goto out_exit;
399             }
400             new->next = opt_allowed;
401             opt_allowed = new;
402             if ((exitcode = dcc_parse_mask(dcc_private_networks[i], &new->addr, &new->mask)))
403                 goto out_exit;
404         }
405     }
406 
407     poptFreeContext(po);
408     return 0;
409 
410     out_exit:
411     poptFreeContext(po);
412     exit(exitcode);
413 }
414