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