Annotation of fam/fam/main.c++, Revision 1.1.1.1
1.1 trev 1: // Copyright (C) 1999 Silicon Graphics, Inc. All Rights Reserved.
2: // This program is free software; you can redistribute it and/or modify it
3: // under the terms of version 2 of the GNU General Public License as
4: // published by the Free Software Foundation.
5: //
6: // This program is distributed in the hope that it would be useful, but
7: // WITHOUT ANY WARRANTY; without even the implied warranty of
8: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, any
9: // license provided herein, whether implied or otherwise, is limited to
10: // this program in accordance with the express provisions of the GNU
11: // General Public License. Patent licenses, if any, provided herein do not
12: // apply to combinations of this program with other product or programs, or
13: // any other product whatsoever. This program is distributed without any
14: // warranty that the program is delivered free of the rightful claim of any
15: // third person by way of infringement or the like. See the GNU General
16: // Public License for more details.
17: //
18: // You should have received a copy of the GNU General Public License along
19: // with this program; if not, write the Free Software Foundation, Inc., 59
20: // Temple Place - Suite 330, Boston MA 02111-1307, USA.
21:
22: #include "config.h"
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26: #include <signal.h>
27: #include <sys/stat.h>
28: #if HAVE_SYSSGI
29: #include <sys/syssgi.h>
30: #endif
31: #include <unistd.h>
32: #include <ctype.h>
33: #include <limits.h>
34:
35: #include "Activity.h"
36: #include "Listener.h"
37: #include "Log.h"
38: #include "Pollster.h"
39: #include "Scheduler.h"
40: #include "Cred.h"
41: #include "Interest.h"
42:
43: const char *program_name;
44:
45: // These are options which are read from the configuration file & the
46: // command line.
47: struct config_opts
48: {
49: config_opts();
50: const char *config_file; // do not free this
51: char *untrusted_user;
52: int pollster_interval; // in seconds
53: int activity_timeout; // in seconds
54: bool disable_pollster;
55: bool local_only;
56: bool xtab_verification;
57: bool disable_audit;
58: bool disable_mac;
59: bool insecure_compat;
60: bool debugging;
61: };
62: #define CFG_INSECURE_COMPAT "insecure_compatibility"
63: #define CFG_LOCAL_ONLY "local_only"
64: #define CFG_XTAB_VERIFICATION "xtab_verification"
65: #define CFG_UNTRUSTED_USER "untrusted_user"
66: #define CFG_IDLE_TIMEOUT "idle_timeout"
67: #define CFG_NFS_POLLING_INTERVAL "nfs_polling_interval"
68: static void parse_config(config_opts &opts);
69: static void parse_config_line(config_opts &opts, int line,
70: const char *k, const char *v);
71: static bool is_true(const char *str);
72:
73: static const char *basename2(const char *p)
74: {
75: const char *tail = p;
76: while (*p)
77: if (*p++ == '/' && *p && *p != '/')
78: tail = p;
79: return tail;
80: }
81:
82: void usage()
83: { fprintf(stderr,
84: "fam, version %s\n"
85: "Use: %s [ -f | -d | -v ] [ -l | -t seconds ] [ -T seconds ] \\\n"
86: "\t\t\t\t[ -p prog.vers ] [ -L ] [ -c config_file ] [-C]\n",
87: VERSION, program_name);
88: fprintf(stderr, "\t-f\t\tstay in foreground\n");
89: fprintf(stderr, "\t-d\t\tdebug\n");
90: fprintf(stderr, "\t-v\t\tverbose\n");
91: fprintf(stderr, "\t-l\t\tno polling\n");
92: fprintf(stderr, "\t-t seconds\tset polling interval (default 6 s)\n");
93: fprintf(stderr, "\t-T seconds\tset inactive timeout (default 5 s)\n");
94: fprintf(stderr, "\t-p prog.vers\tset RPC program number and version\n");
95: fprintf(stderr, "\t-L\t\tlocal only (ignore remote requests)\n");
96: fprintf(stderr, "\t-c config_file\tpath to alternate configuration file\n");
97: fprintf(stderr, "\t\t\t (default is %s)\n", CONFIG_ETC_CONFIG_PATH);
98: fprintf(stderr, "\t-C\t\tinsecure compatibility\n");
99: fprintf(stderr, "\n");
100: exit(1);
101: }
102:
103: int main(int argc, char *argv[])
104: {
105: #if HAVE_SGI_NOHANG
106: // This keeps down nfs mounts from hanging fam. System calls on
107: // down nfs mounts fail with errno == ETIMEDOUT. We don't do this
108: // if we're running diskless because we don't want fam to crash
109: // because a page fault timed out.
110: char buf[20];
111: if (sgikopt("diskless", buf, sizeof buf) == 0
112: && strcmp(buf, "0") == 0) {
113: syssgi(SGI_NOHANG, 1);
114: }
115: #endif
116:
117: config_opts opts;
118:
119: // If fd 0 is a socket, we figure we were started by inetd.
120: struct stat st;
121: fstat(0, &st);
122: bool started_by_inetd = S_ISSOCK(st.st_mode);
123:
124: unsigned long program = Listener::FAMPROG, version = Listener::FAMVERS;
125:
126: program_name = basename2(argv[0]);
127: Log::name(program_name);
128:
129: if (!started_by_inetd)
130: Log::foreground();
131:
132: // Run through the command-line args once, looking for debugging flags
133: // and config-file flags. (We want to check those before we parse the
134: // config file, and we want to parse the config file before reading other
135: // command-line args which might override its contents.)
136: int i;
137: for (i = 1; i < argc; i++)
138: {
139: if (argv[i][0] != '-' || !argv[i][1] || argv[i][2]) continue;
140: switch (argv[i][1])
141: {
142: case 'c':
143: if(++i >= argc) usage();
144: opts.config_file = argv[i];
145: break;
146: case 'f':
147: opts.debugging = true;
148: break;
149: case 'd':
150: Log::debug();
151: opts.debugging = true;
152: break;
153: case 'v':
154: Log::info();
155: opts.debugging = true;
156: break;
157: }
158: }
159:
160: if (getuid() != 0)
161: { Log::error("must be superuser");
162: exit(1);
163: }
164:
165: parse_config(opts);
166:
167: // Now run through the command-line arguments again.
168: for (i = 1; i < argc; i++)
169: { if (argv[i][0] != '-' || !argv[i][1] || argv[i][2])
170: usage();
171: switch (argv[i][1])
172: {
173: char *p, *q;
174: unsigned secs;
175:
176: case 'f':
177: case 'd':
178: case 'v':
179: // handled above.
180: break;
181:
182: case 'l':
183: opts.disable_pollster = true;
184: break;
185:
186: case 'p':
187: if (++i >= argc)
188: usage();
189: p = strchr(argv[i], '.');
190: if (p)
191: { *p++ = '\0';
192: version = strtoul(p, &q, 10);
193: if (p == q) usage();
194: }
195: program = strtoul(argv[i], &q, 10);
196: if (argv[i] == q) usage();
197: break;
198:
199: case 't':
200: if (i + 1 >= argc)
201: usage();
202: secs = strtoul(argv[++i], &p, 10);
203: if (*p)
204: usage();
205: if (secs == 0)
206: Log::error("illegal poll interval 0");
207: else
208: opts.pollster_interval = secs;
209: break;
210:
211: case 'T':
212: if (i + 1 >= argc)
213: usage();
214: secs = strtoul(argv[++i], &p, 10);
215: if (*p)
216: usage();
217: opts.activity_timeout = secs;
218: break;
219:
220: case 'L':
221: opts.local_only = true;
222: break;
223:
224: case 'C':
225: opts.insecure_compat = true;
226: opts.xtab_verification = false;
227: Log::info("Running with -C (" CFG_INSECURE_COMPAT
228: ") command-line option");
229: break;
230:
231: case 'c':
232: // handled above.
233: ++i;
234: break;
235:
236: default:
237: usage();
238: }
239: }
240:
241: // Apply the various options.
242: if (opts.local_only && started_by_inetd) {
243: Log::error("Warning! Started by inetd, so -L (" CFG_LOCAL_ONLY
244: ") option is being ignored!");
245: opts.local_only = false;
246: }
247: if (opts.disable_audit) Log::disable_audit();
248: if (opts.disable_mac) Cred::disable_mac();
249: if (opts.insecure_compat) Cred::enable_insecure_compat();
250: if (opts.untrusted_user) Cred::set_untrusted_user(opts.untrusted_user);
251: else {
252: Log::error("Fatal misconfiguration! No " CFG_UNTRUSTED_USER
253: " found in %s!", opts.config_file);
254: exit(1);
255: }
256: Pollster::interval(opts.pollster_interval);
257: Activity::timeout(opts.activity_timeout);
258: if (opts.disable_pollster) Pollster::disable();
259: if (!opts.local_only) {
260: Interest::enable_xtab_verification(opts.xtab_verification);
261: }
262: Log::audit(true, "fam starting SAT with process name \"%s\"",
263: program_name);
264:
265: if (!started_by_inetd)
266: {
267: #if HAVE__DAEMONIZE
268: if (!opts.debugging)
269: { _daemonize(0, -1, -1, -1);
270: Log::background();
271: }
272: #else
273: # if HAVE_DAEMON
274: if (!opts.debugging)
275: { daemon(0, 0);
276: Log::background();
277: }
278: # endif
279: #endif
280: }
281: (void) signal(SIGPIPE, SIG_IGN);
282:
283: #if HAVE_SGI_NOHANG
284: // Ignore SIGCHLD because we run nfsunhang to unhang down nfs
285: // mounts, and we don't care about the exit status of nfsunhang
286: // (since we poll anyway) and we don't want to create zombies.
287: (void) signal(SIGCHLD, SIG_IGN);
288: #endif
289: new Listener(started_by_inetd, opts.local_only, program, version);
290: Scheduler::loop();
291: return 0;
292: }
293:
294:
295: static void
296: parse_config(config_opts &opts)
297: {
298: FILE *cfg = fopen(opts.config_file, "r");
299: if(cfg == NULL)
300: {
301: Log::error("Couldn't open config file \"%s\"!", opts.config_file);
302: usage();
303: return;
304: }
305: char buf[PATH_MAX + 64];
306: char *bp;
307: int lineno = 0;
308: while(fgets(buf, sizeof(buf), cfg))
309: {
310: ++lineno;
311: bp = buf;
312: while(isspace(*bp)) ++bp;
313: if ((*bp == '\0') || (*bp == '#') || (*bp == '!')) continue;
314: if (bp[strlen(bp) - 1] != '\n')
315: {
316: Log::error("config file %s line %d is too long, and is "
317: "being ignored!", opts.config_file, lineno);
318: --lineno;
319: continue;
320: }
321: bp[strlen(bp) - 1] = '\0'; // whap newline
322:
323: // split line at "=", strip whitespace from ends of both pieces
324: char *vp = strchr(bp, '=');
325: if(vp == NULL)
326: {
327: Log::error("config file %s line %d seems dain bramaged (no \"=\"), "
328: "and is being ignored.", opts.config_file, lineno);
329: continue;
330: }
331: *vp++ = '\0';
332: while (isspace(*vp)) ++vp;
333: char *end = bp + strlen(bp) - 1;
334: while (isspace(*end) && (end >= bp)) *end-- = '\0';
335: end = vp + strlen(vp) - 1;
336: while (isspace(*end) && (end >= vp)) *end-- = '\0';
337:
338: Log::debug("read %s line %d: \"%s\" = \"%s\"",
339: opts.config_file, lineno, bp, vp);
340: parse_config_line(opts, lineno, bp, vp);
341: }
342: fclose(cfg);
343: }
344:
345:
346:
347: static void
348: parse_config_line(config_opts &opts, int lineno, const char *key, const char *val)
349: {
350: char *p;
351: unsigned secs;
352:
353: if(!strcmp(key, CFG_UNTRUSTED_USER))
354: {
355: if (!opts.untrusted_user) opts.untrusted_user = strdup(val);
356: else Log::error("config file %s line %d: ignoring duplicate %s",
357: opts.config_file, lineno, key);
358: }
359: else if(!strcmp(key, CFG_LOCAL_ONLY))
360: {
361: opts.local_only = is_true(val);
362: }
363: else if(!strcmp(key, CFG_IDLE_TIMEOUT))
364: {
365: secs = strtoul(val, &p, 10);
366: if (*p)
367: {
368: Log::error("config file %s line %d: ignoring invalid value for %s",
369: opts.config_file, lineno, key);
370: }
371: else
372: {
373: opts.activity_timeout = secs;
374: }
375: }
376: else if(!strcmp(key, CFG_NFS_POLLING_INTERVAL))
377: {
378: secs = strtoul(val, &p, 10);
379: if (*p || secs == 0)
380: {
381: Log::error("config file %s line %d: ignoring invalid value for %s",
382: opts.config_file, lineno, key);
383: }
384: else
385: {
386: opts.pollster_interval = secs;
387: }
388: }
389: else if(!strcmp(key, CFG_XTAB_VERIFICATION))
390: {
391: opts.xtab_verification = is_true(val);
392: if(opts.xtab_verification && opts.insecure_compat)
393: {
394: opts.xtab_verification = false;
395: Log::error("config file %s line %d: ignoring %s because "
396: CFG_INSECURE_COMPAT " is set",
397: opts.config_file, lineno, key);
398: }
399: }
400: else if(!strcmp(key, CFG_INSECURE_COMPAT))
401: {
402: opts.insecure_compat = is_true(val);
403: //ehh... it would be nice to handle this a little better.
404: // if(opts.insecure_compat && opts.xtab_verification)
405: // {
406: // opts.xtab_verification = false;
407: // Log::error("config file %s line %d: %s overrides "
408: // CFG_XTAB_VERIFICATION,
409: // opts.config_file, lineno, key);
410: // }
411: }
412: else if(!strcmp(key, "disable_audit"))
413: {
414: opts.disable_audit = is_true(val);
415: }
416: else if(!strcmp(key, "disable_mac"))
417: {
418: opts.disable_mac = is_true(val);
419: }
420: else
421: {
422: Log::error("config file %s line %d: unrecognized key \"%s\"",
423: opts.config_file, lineno, key);
424: }
425: }
426:
427:
428: config_opts::config_opts()
429: {
430: memset(this, 0, sizeof(config_opts));
431:
432: config_file = CONFIG_ETC_CONFIG_PATH;
433: untrusted_user = NULL;
434: pollster_interval = 6;
435: activity_timeout = 5;
436: disable_pollster = false;
437: local_only = false;
438: xtab_verification = true;
439: #ifdef HAVE_AUDIT
440: disable_audit = (sysconf(_SC_AUDIT) != 1);
441: #else
442: disable_audit = true;
443: #endif
444: #ifdef HAVE_MAC
445: disable_mac = ((sysconf(_SC_MAC) != 1) || (sysconf(_SC_IP_SECOPTS) != 1));
446: #else
447: disable_mac = true;
448: #endif
449: insecure_compat = false;
450: debugging = false;
451: }
452:
453: static bool
454: is_true(const char *val)
455: {
456: if (!strcasecmp(val, "false") ||
457: !strcasecmp(val, "no") ||
458: !strcmp(val, "0"))
459: {
460: return false;
461: }
462: return true;
463: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>