bozo: Fix problems found by static analysis
[openafs.git] / src / bozo / bosserver.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <afs/procmgmt.h>
15 #include <roken.h>
16 #include <ctype.h>
17
18 #ifdef IGNORE_SOME_GCC_WARNINGS
19 # ifdef __clang__
20 #  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
21 # else
22 #  pragma GCC diagnostic warning "-Wdeprecated-declarations"
23 # endif
24 #endif
25
26 #ifdef HAVE_SYS_RESOURCE_H
27 #include <sys/resource.h>
28 #endif
29
30 #ifdef AFS_NT40_ENV
31 #define PATH_DELIM '\\'
32 #include <direct.h>
33 #include <WINNT/afsevent.h>
34 #endif /* AFS_NT40_ENV */
35
36 #define PATH_DELIM '/'
37 #include <rx/rx.h>
38 #include <rx/xdr.h>
39 #include <rx/rx_globals.h>
40 #include <rx/rxkad.h>
41 #include <rx/rxstat.h>
42 #include <afs/keys.h>
43 #include <afs/ktime.h>
44 #include <afs/afsutil.h>
45 #include <afs/fileutil.h>
46 #include <afs/audit.h>
47 #include <afs/authcon.h>
48 #include <afs/cellconfig.h>
49 #include <afs/cmd.h>
50
51 #if defined(AFS_SGI_ENV)
52 #include <afs/afs_args.h>
53 #endif
54
55 #include "bosint.h"
56 #include "bnode.h"
57 #include "bnode_internal.h"
58 #include "bosprototypes.h"
59
60 #define BOZO_LWP_STACKSIZE      16000
61 extern struct bnode_ops fsbnode_ops, dafsbnode_ops, ezbnode_ops, cronbnode_ops;
62
63 struct afsconf_dir *bozo_confdir = 0;   /* bozo configuration dir */
64 static PROCESS bozo_pid;
65 const char *bozo_fileName;
66 FILE *bozo_logFile;
67 #ifndef AFS_NT40_ENV
68 static int bozo_argc = 0;
69 static char** bozo_argv = NULL;
70 #endif
71
72 char *DoCore;
73 int DoLogging = 0;
74 int DoSyslog = 0;
75 char *DoPidFiles = NULL;
76 #ifndef AFS_NT40_ENV
77 int DoSyslogFacility = LOG_DAEMON;
78 #endif
79 int DoTransarcLogs = 0;
80 static afs_int32 nextRestart;
81 static afs_int32 nextDay;
82
83 struct ktime bozo_nextRestartKT, bozo_nextDayKT;
84 int bozo_newKTs;
85 int rxBind = 0;
86 int rxkadDisableDotCheck = 0;
87
88 int bozo_isrestricted = 0;
89 int bozo_restdisable = 0;
90
91 void
92 bozo_insecureme(int sig)
93 {
94     signal(SIGFPE, bozo_insecureme);
95     bozo_isrestricted = 0;
96     bozo_restdisable = 1;
97 }
98
99 struct bztemp {
100     FILE *file;
101 };
102
103 /* check whether caller is authorized to manage RX statistics */
104 int
105 bozo_rxstat_userok(struct rx_call *call)
106 {
107     return afsconf_SuperUser(bozo_confdir, call, NULL);
108 }
109
110 /**
111  * Return true if this name is a member of the local realm.
112  */
113 int
114 bozo_IsLocalRealmMatch(void *rock, char *name, char *inst, char *cell)
115 {
116     struct afsconf_dir *dir = (struct afsconf_dir *)rock;
117     afs_int32 islocal = 0;      /* default to no */
118     int code;
119
120     code = afsconf_IsLocalRealmMatch(dir, &islocal, name, inst, cell);
121     if (code) {
122         bozo_Log("Failed local realm check; code=%d, name=%s, inst=%s, cell=%s\n",
123                  code, name, inst, cell);
124     }
125     return islocal;
126 }
127
128 /* restart bozo process */
129 int
130 bozo_ReBozo(void)
131 {
132 #ifdef AFS_NT40_ENV
133     /* exit with restart code; SCM integrator process will restart bosserver with
134        the same arguments */
135     exit(BOSEXIT_RESTART);
136 #else
137     /* exec new bosserver process */
138     int i = 0;
139
140     /* close random fd's */
141     for (i = 3; i < 64; i++) {
142         close(i);
143     }
144
145     unlink(AFSDIR_SERVER_BOZRXBIND_FILEPATH);
146
147     execv(bozo_argv[0], bozo_argv);     /* should not return */
148     _exit(1);
149 #endif /* AFS_NT40_ENV */
150 }
151
152 /*!
153  * Make directory with parents.
154  *
155  * \param[in] adir      directory path to create
156  * \param[in] areqPerm  permissions to set on the last component of adir
157  * \return              0 on success
158  */
159 static int
160 MakeDirParents(const char *adir, int areqPerm)
161 {
162     struct stat stats;
163     int error = 0;
164     char *tdir;
165     char *p;
166     int parent_perm = 0777;     /* use umask for parent perms */
167     size_t len;
168
169     tdir = strdup(adir);
170     if (!tdir) {
171         error = ENOMEM;
172         goto done;
173     }
174
175     /* strip trailing slashes */
176     len = strlen(tdir);
177     if (!len) {
178         goto done;
179     }
180     p = tdir + len - 1;
181     while (p != tdir && *p == PATH_DELIM) {
182         *p-- = '\0';
183     }
184
185     p = tdir;
186 #ifdef AFS_NT40_ENV
187     /* skip drive letter */
188     if (isalpha(p[0]) && p[1] == ':') {
189         p += 2;
190     }
191 #endif
192     /* skip leading slashes */
193     while (*p == PATH_DELIM) {
194         p++;
195     }
196
197     /* create parent directories with default perms */
198     p = strchr(p, PATH_DELIM);
199     while (p) {
200         *p = '\0';
201         if (stat(tdir, &stats) != 0 || !S_ISDIR(stats.st_mode)) {
202             if (mkdir(tdir, parent_perm) != 0) {
203                 error = errno;
204                 goto done;
205             }
206         }
207         *p++ = PATH_DELIM;
208
209         /* skip back to back slashes */
210         while (*p == PATH_DELIM) {
211             p++;
212         }
213         p = strchr(p, PATH_DELIM);
214     }
215
216     /* set required perms on the last path component */
217     if (stat(tdir, &stats) != 0 || !S_ISDIR(stats.st_mode)) {
218         if (mkdir(tdir, areqPerm) != 0) {
219             error = errno;
220         }
221     }
222
223   done:
224     free(tdir);
225     return error;
226 }
227
228 /* make sure a dir exists */
229 static int
230 MakeDir(const char *adir)
231 {
232     struct stat tstat;
233     afs_int32 code;
234     if (stat(adir, &tstat) < 0 || (tstat.st_mode & S_IFMT) != S_IFDIR) {
235         int reqPerm;
236         unlink(adir);
237         reqPerm = GetRequiredDirPerm(adir);
238         if (reqPerm == -1)
239             reqPerm = 0777;
240         code = MakeDirParents(adir, reqPerm);
241         return code;
242     }
243     return 0;
244 }
245
246 /* create all the bozo dirs */
247 static int
248 CreateDirs(const char *coredir)
249 {
250     if ((!strncmp
251          (AFSDIR_USR_DIRPATH, AFSDIR_CLIENT_ETC_DIRPATH,
252           strlen(AFSDIR_USR_DIRPATH)))
253         ||
254         (!strncmp
255          (AFSDIR_USR_DIRPATH, AFSDIR_SERVER_BIN_DIRPATH,
256           strlen(AFSDIR_USR_DIRPATH)))) {
257         if (MakeDir(AFSDIR_USR_DIRPATH))
258             return errno;
259     }
260     if (!strncmp
261         (AFSDIR_SERVER_AFS_DIRPATH, AFSDIR_SERVER_BIN_DIRPATH,
262          strlen(AFSDIR_SERVER_AFS_DIRPATH))) {
263         if (MakeDir(AFSDIR_SERVER_AFS_DIRPATH))
264             return errno;
265     }
266     if (MakeDir(AFSDIR_SERVER_BIN_DIRPATH))
267         return errno;
268     if (MakeDir(AFSDIR_SERVER_ETC_DIRPATH))
269         return errno;
270     if (MakeDir(AFSDIR_SERVER_LOCAL_DIRPATH))
271         return errno;
272     if (MakeDir(AFSDIR_SERVER_DB_DIRPATH))
273         return errno;
274     if (MakeDir(AFSDIR_SERVER_LOGS_DIRPATH))
275         return errno;
276 #ifndef AFS_NT40_ENV
277     if (!strncmp
278         (AFSDIR_CLIENT_VICE_DIRPATH, AFSDIR_CLIENT_ETC_DIRPATH,
279          strlen(AFSDIR_CLIENT_VICE_DIRPATH))) {
280         if (MakeDir(AFSDIR_CLIENT_VICE_DIRPATH))
281             return errno;
282     }
283     if (MakeDir(AFSDIR_CLIENT_ETC_DIRPATH))
284         return errno;
285
286     if (symlink(AFSDIR_SERVER_THISCELL_FILEPATH,
287             AFSDIR_CLIENT_THISCELL_FILEPATH)) {
288         if (errno != EEXIST) {
289             return errno;
290         }
291     }
292     if (symlink(AFSDIR_SERVER_CELLSERVDB_FILEPATH,
293             AFSDIR_CLIENT_CELLSERVDB_FILEPATH)) {
294         if (errno != EEXIST) {
295             return errno;
296         }
297     }
298 #endif /* AFS_NT40_ENV */
299     if (coredir) {
300         if (MakeDir(coredir))
301             return errno;
302     }
303     return 0;
304 }
305
306 /* strip the \\n from the end of the line, if it is present */
307 static int
308 StripLine(char *abuffer)
309 {
310     char *tp;
311
312     tp = abuffer + strlen(abuffer);     /* starts off pointing at the null  */
313     if (tp == abuffer)
314         return 0;               /* null string, no last character to check */
315     tp--;                       /* aim at last character */
316     if (*tp == '\n')
317         *tp = 0;
318     return 0;
319 }
320
321 /* write one bnode's worth of entry into the file */
322 static int
323 bzwrite(struct bnode *abnode, void *arock)
324 {
325     struct bztemp *at = (struct bztemp *)arock;
326     int i;
327     char tbuffer[BOZO_BSSIZE];
328     afs_int32 code;
329
330     if (abnode->notifier)
331         fprintf(at->file, "bnode %s %s %d %s\n", abnode->type->name,
332                 abnode->name, abnode->fileGoal, abnode->notifier);
333     else
334         fprintf(at->file, "bnode %s %s %d\n", abnode->type->name,
335                 abnode->name, abnode->fileGoal);
336     for (i = 0;; i++) {
337         code = bnode_GetParm(abnode, i, tbuffer, BOZO_BSSIZE);
338         if (code) {
339             if (code != BZDOM)
340                 return code;
341             break;
342         }
343         fprintf(at->file, "parm %s\n", tbuffer);
344     }
345     fprintf(at->file, "end\n");
346     return 0;
347 }
348
349 #define MAXPARMS    20
350 int
351 ReadBozoFile(char *aname)
352 {
353     FILE *tfile;
354     char tbuffer[BOZO_BSSIZE];
355     char *tp;
356     char *instp = NULL, *typep = NULL, *notifier = NULL, *notp = NULL;
357     afs_int32 code;
358     afs_int32 ktmask, ktday, kthour, ktmin, ktsec;
359     afs_int32 i, goal;
360     struct bnode *tb;
361     char *parms[MAXPARMS];
362     char *thisparms[MAXPARMS];
363     int rmode;
364
365     /* rename BozoInit to BosServer for the user */
366     if (!aname) {
367         /* if BozoInit exists and BosConfig doesn't, try a rename */
368         if (access(AFSDIR_SERVER_BOZINIT_FILEPATH, 0) == 0
369             && access(AFSDIR_SERVER_BOZCONF_FILEPATH, 0) != 0) {
370             code = rk_rename(AFSDIR_SERVER_BOZINIT_FILEPATH,
371                              AFSDIR_SERVER_BOZCONF_FILEPATH);
372             if (code < 0)
373                 perror("bosconfig rename");
374         }
375         if (access(AFSDIR_SERVER_BOZCONFNEW_FILEPATH, 0) == 0) {
376             code = rk_rename(AFSDIR_SERVER_BOZCONFNEW_FILEPATH,
377                              AFSDIR_SERVER_BOZCONF_FILEPATH);
378             if (code < 0)
379                 perror("bosconfig rename");
380         }
381     }
382
383     /* don't do server restarts by default */
384     bozo_nextRestartKT.mask = KTIME_NEVER;
385     bozo_nextRestartKT.hour = 0;
386     bozo_nextRestartKT.min = 0;
387     bozo_nextRestartKT.day = 0;
388
389     /* restart processes at 5am if their binaries have changed */
390     bozo_nextDayKT.mask = KTIME_HOUR | KTIME_MIN;
391     bozo_nextDayKT.hour = 5;
392     bozo_nextDayKT.min = 0;
393
394     for (code = 0; code < MAXPARMS; code++)
395         parms[code] = NULL;
396     if (!aname)
397         aname = (char *)bozo_fileName;
398     tfile = fopen(aname, "r");
399     if (!tfile)
400         return 0;               /* -1 */
401     instp = malloc(BOZO_BSSIZE);
402     if (!instp) {
403         code = ENOMEM;
404         goto fail;
405     }
406     typep = malloc(BOZO_BSSIZE);
407     if (!typep) {
408         code = ENOMEM;
409         goto fail;
410     }
411     notp = malloc(BOZO_BSSIZE);
412     if (!notp) {
413         code = ENOMEM;
414         goto fail;
415     }
416     while (1) {
417         /* ok, read lines giving parms and such from the file */
418         tp = fgets(tbuffer, sizeof(tbuffer), tfile);
419         if (tp == (char *)0)
420             break;              /* all done */
421
422         if (strncmp(tbuffer, "restarttime", 11) == 0) {
423             code =
424                 sscanf(tbuffer, "restarttime %d %d %d %d %d", &ktmask, &ktday,
425                        &kthour, &ktmin, &ktsec);
426             if (code != 5) {
427                 code = -1;
428                 goto fail;
429             }
430             /* otherwise we've read in the proper ktime structure; now assign
431              * it and continue processing */
432             bozo_nextRestartKT.mask = ktmask;
433             bozo_nextRestartKT.day = ktday;
434             bozo_nextRestartKT.hour = kthour;
435             bozo_nextRestartKT.min = ktmin;
436             bozo_nextRestartKT.sec = ktsec;
437             continue;
438         }
439
440         if (strncmp(tbuffer, "checkbintime", 12) == 0) {
441             code =
442                 sscanf(tbuffer, "checkbintime %d %d %d %d %d", &ktmask,
443                        &ktday, &kthour, &ktmin, &ktsec);
444             if (code != 5) {
445                 code = -1;
446                 goto fail;
447             }
448             /* otherwise we've read in the proper ktime structure; now assign
449              * it and continue processing */
450             bozo_nextDayKT.mask = ktmask;       /* time to restart the system */
451             bozo_nextDayKT.day = ktday;
452             bozo_nextDayKT.hour = kthour;
453             bozo_nextDayKT.min = ktmin;
454             bozo_nextDayKT.sec = ktsec;
455             continue;
456         }
457
458         if (strncmp(tbuffer, "restrictmode", 12) == 0) {
459             code = sscanf(tbuffer, "restrictmode %d", &rmode);
460             if (code != 1) {
461                 code = -1;
462                 goto fail;
463             }
464             if (rmode != 0 && rmode != 1) {
465                 code = -1;
466                 goto fail;
467             }
468             bozo_isrestricted = rmode;
469             continue;
470         }
471
472         if (strncmp("bnode", tbuffer, 5) != 0) {
473             code = -1;
474             goto fail;
475         }
476         notifier = notp;
477         code =
478             sscanf(tbuffer, "bnode %s %s %d %s", typep, instp, &goal,
479                    notifier);
480         if (code < 3) {
481             code = -1;
482             goto fail;
483         } else if (code == 3)
484             notifier = NULL;
485
486         memset(thisparms, 0, sizeof(thisparms));
487
488         for (i = 0; i < MAXPARMS; i++) {
489             /* now read the parms, until we see an "end" line */
490             tp = fgets(tbuffer, sizeof(tbuffer), tfile);
491             if (!tp) {
492                 code = -1;
493                 goto fail;
494             }
495             StripLine(tbuffer);
496             if (!strncmp(tbuffer, "end", 3))
497                 break;
498             if (strncmp(tbuffer, "parm ", 5)) {
499                 code = -1;
500                 goto fail;      /* no "parm " either */
501             }
502             if (!parms[i]) {    /* make sure there's space */
503                 parms[i] = malloc(BOZO_BSSIZE);
504                 if (parms[i] == NULL) {
505                     code = ENOMEM;
506                     goto fail;
507                 }
508             }
509             strcpy(parms[i], tbuffer + 5);      /* remember the parameter for later */
510             thisparms[i] = parms[i];
511         }
512
513         /* ok, we have the type and parms, now create the object */
514         code =
515             bnode_Create(typep, instp, &tb, thisparms[0], thisparms[1],
516                          thisparms[2], thisparms[3], thisparms[4], notifier,
517                          goal ? BSTAT_NORMAL : BSTAT_SHUTDOWN, 0);
518         if (code)
519             goto fail;
520
521         /* bnode created in 'temporarily shutdown' state;
522          * check to see if we are supposed to run this guy,
523          * and if so, start the process up */
524         if (goal) {
525             bnode_SetStat(tb, BSTAT_NORMAL);    /* set goal, taking effect immediately */
526         } else {
527             bnode_SetStat(tb, BSTAT_SHUTDOWN);
528         }
529     }
530     /* all done */
531     code = 0;
532
533   fail:
534     if (instp)
535         free(instp);
536     if (typep)
537         free(typep);
538     if (notp)
539         free(notp);
540     for (i = 0; i < MAXPARMS; i++)
541         if (parms[i])
542             free(parms[i]);
543     if (tfile)
544         fclose(tfile);
545     return code;
546 }
547
548 /* write a new bozo file */
549 int
550 WriteBozoFile(char *aname)
551 {
552     FILE *tfile;
553     char *tbuffer = NULL;
554     afs_int32 code;
555     struct bztemp btemp;
556     int ret = 0;
557
558     if (!aname)
559         aname = (char *)bozo_fileName;
560     if (asprintf(&tbuffer, "%s.NBZ", aname) < 0)
561         return -1;
562
563     tfile = fopen(tbuffer, "w");
564     if (!tfile) {
565         ret = -1;
566         goto out;
567     }
568     btemp.file = tfile;
569
570     fprintf(tfile, "restrictmode %d\n", bozo_isrestricted);
571     fprintf(tfile, "restarttime %d %d %d %d %d\n", bozo_nextRestartKT.mask,
572             bozo_nextRestartKT.day, bozo_nextRestartKT.hour,
573             bozo_nextRestartKT.min, bozo_nextRestartKT.sec);
574     fprintf(tfile, "checkbintime %d %d %d %d %d\n", bozo_nextDayKT.mask,
575             bozo_nextDayKT.day, bozo_nextDayKT.hour, bozo_nextDayKT.min,
576             bozo_nextDayKT.sec);
577     code = bnode_ApplyInstance(bzwrite, &btemp);
578     if (code || (code = ferror(tfile))) {       /* something went wrong */
579         fclose(tfile);
580         unlink(tbuffer);
581         ret = code;
582         goto out;
583     }
584     /* close the file, check for errors and snap new file into place */
585     if (fclose(tfile) == EOF) {
586         unlink(tbuffer);
587         ret = -1;
588         goto out;
589     }
590     code = rk_rename(tbuffer, aname);
591     if (code) {
592         unlink(tbuffer);
593         ret = -1;
594         goto out;
595     }
596     ret = 0;
597 out:
598     free(tbuffer);
599     return ret;
600 }
601
602 static int
603 bdrestart(struct bnode *abnode, void *arock)
604 {
605     afs_int32 code;
606
607     if (abnode->fileGoal != BSTAT_NORMAL || abnode->goal != BSTAT_NORMAL)
608         return 0;               /* don't restart stopped bnodes */
609     bnode_Hold(abnode);
610     code = bnode_RestartP(abnode);
611     if (code) {
612         /* restart the dude */
613         bnode_SetStat(abnode, BSTAT_SHUTDOWN);
614         bnode_WaitStatus(abnode, BSTAT_SHUTDOWN);
615         bnode_SetStat(abnode, BSTAT_NORMAL);
616     }
617     bnode_Release(abnode);
618     return 0;                   /* keep trying all bnodes */
619 }
620
621 #define BOZO_MINSKIP 3600       /* minimum to advance clock */
622 /* lwp to handle system restarts */
623 static void *
624 BozoDaemon(void *unused)
625 {
626     afs_int32 now;
627
628     /* now initialize the values */
629     bozo_newKTs = 1;
630     while (1) {
631         IOMGR_Sleep(60);
632         now = FT_ApproxTime();
633
634         if (bozo_restdisable) {
635             bozo_Log("Restricted mode disabled by signal\n");
636             bozo_restdisable = 0;
637         }
638
639         if (bozo_newKTs) {      /* need to recompute restart times */
640             bozo_newKTs = 0;    /* done for a while */
641             nextRestart = ktime_next(&bozo_nextRestartKT, BOZO_MINSKIP);
642             nextDay = ktime_next(&bozo_nextDayKT, BOZO_MINSKIP);
643         }
644
645         /* see if we should do a restart */
646         if (now > nextRestart) {
647             SBOZO_ReBozo(0);    /* doesn't come back */
648         }
649
650         /* see if we should restart a server */
651         if (now > nextDay) {
652             nextDay = ktime_next(&bozo_nextDayKT, BOZO_MINSKIP);
653
654             /* call the bnode restartp function, and restart all that require it */
655             bnode_ApplyInstance(bdrestart, 0);
656         }
657     }
658     AFS_UNREACHED(return(NULL));
659 }
660
661 #ifdef AFS_AIX32_ENV
662 static int
663 tweak_config(void)
664 {
665     FILE *f;
666     char c[80];
667     int s, sb_max, ipfragttl;
668
669     sb_max = 131072;
670     ipfragttl = 20;
671     f = popen("/usr/sbin/no -o sb_max", "r");
672     s = fscanf(f, "sb_max = %d", &sb_max);
673     fclose(f);
674     if (s < 1)
675         return;
676     f = popen("/usr/sbin/no -o ipfragttl", "r");
677     s = fscanf(f, "ipfragttl = %d", &ipfragttl);
678     fclose(f);
679     if (s < 1)
680         ipfragttl = 20;
681
682     if (sb_max < 131072)
683         sb_max = 131072;
684     if (ipfragttl > 20)
685         ipfragttl = 20;
686
687     sprintf(c, "/usr/sbin/no -o sb_max=%d -o ipfragttl=%d", sb_max,
688             ipfragttl);
689     f = popen(c, "r");
690     fclose(f);
691 }
692 #endif
693
694 static char *
695 make_pid_filename(char *ainst, char *aname)
696 {
697     char *buffer = NULL;
698     int r;
699
700     if (aname && *aname) {
701         r = asprintf(&buffer, "%s/%s.%s.pid", DoPidFiles, ainst, aname);
702         if (r < 0 || buffer == NULL)
703             bozo_Log("Failed to alloc pid filename buffer for %s.%s.\n",
704                      ainst, aname);
705     } else {
706         r = asprintf(&buffer, "%s/%s.pid", DoPidFiles, ainst);
707         if (r < 0 || buffer == NULL)
708             bozo_Log("Failed to alloc pid filename buffer for %s.\n", ainst);
709     }
710
711     return buffer;
712 }
713
714 /**
715  * Write a file containing the pid of the named process.
716  *
717  * @param ainst instance name
718  * @param aname sub-process name of the instance, may be null
719  * @param apid  process id of the newly started process
720  *
721  * @returns status
722  */
723 int
724 bozo_CreatePidFile(char *ainst, char *aname, pid_t apid)
725 {
726     int code = 0;
727     char *pidfile = NULL;
728     FILE *fp;
729
730     pidfile = make_pid_filename(ainst, aname);
731     if (!pidfile) {
732         return ENOMEM;
733     }
734     if ((fp = fopen(pidfile, "w")) == NULL) {
735         bozo_Log("Failed to open pidfile %s; errno=%d\n", pidfile, errno);
736         free(pidfile);
737         return errno;
738     }
739     if (fprintf(fp, "%ld\n", afs_printable_int32_ld(apid)) < 0) {
740         code = errno;
741     }
742     if (fclose(fp) != 0) {
743         code = errno;
744     }
745     free(pidfile);
746     return code;
747 }
748
749 /**
750  * Clean a pid file for a process which just exited.
751  *
752  * @param ainst instance name
753  * @param aname sub-process name of the instance, may be null
754  *
755  * @returns status
756  */
757 int
758 bozo_DeletePidFile(char *ainst, char *aname)
759 {
760     char *pidfile = NULL;
761     pidfile = make_pid_filename(ainst, aname);
762     if (pidfile) {
763         unlink(pidfile);
764         free(pidfile);
765     }
766     return 0;
767 }
768
769 /**
770  * Create the rxbind file of this bosserver.
771  *
772  * @param host  bind address of this server
773  *
774  * @returns status
775  */
776 void
777 bozo_CreateRxBindFile(afs_uint32 host)
778 {
779     char buffer[16];
780     FILE *fp;
781
782     afs_inet_ntoa_r(host, buffer);
783     bozo_Log("Listening on %s:%d\n", buffer, AFSCONF_NANNYPORT);
784     if ((fp = fopen(AFSDIR_SERVER_BOZRXBIND_FILEPATH, "w")) == NULL) {
785         bozo_Log("Unable to open rxbind address file: %s, code=%d\n",
786                  AFSDIR_SERVER_BOZRXBIND_FILEPATH, errno);
787     } else {
788         /* If listening on any interface, write the loopback interface
789            to the rxbind file to give local scripts a usable addresss. */
790         if (host == htonl(INADDR_ANY)) {
791             afs_inet_ntoa_r(htonl(0x7f000001), buffer);
792         }
793         fprintf(fp, "%s\n", buffer);
794         fclose(fp);
795     }
796 }
797
798 /**
799  * Get an interface address in network byte order, modulo the
800  * NetInfo/NetRestrict configuration files. Return the INADDR_ANY if no
801  * interface address is found.
802  */
803 static afs_uint32
804 GetRxBindAddress(void)
805 {
806     afs_uint32 addr;
807     afs_int32 ccode; /* number of addresses found */
808
809     if (AFSDIR_SERVER_NETRESTRICT_FILEPATH || AFSDIR_SERVER_NETINFO_FILEPATH) {
810         char reason[1024];
811         ccode = afsconf_ParseNetFiles(&addr, NULL, NULL, 1, reason,
812                                       AFSDIR_SERVER_NETINFO_FILEPATH,
813                                       AFSDIR_SERVER_NETRESTRICT_FILEPATH);
814     } else {
815         /* Get the first non-loopback address from the kernel. */
816         ccode = rx_getAllAddr(&addr, 1);
817     }
818
819     if (ccode != 1) {
820         addr = htonl(INADDR_ANY);
821     }
822     return addr;
823 }
824
825 /**
826  * Try to create local cell config file.
827  */
828 static struct afsconf_dir *
829 CreateLocalCellConfig(void)
830 {
831     int code;
832     struct afsconf_dir *tdir = NULL;
833     struct afsconf_cell tcell;
834
835     memset(&tcell, 0, sizeof(tcell));
836     strcpy(tcell.name, "localcell");  /* assume name is big enough for the default value */
837     tcell.numServers = 1;
838     code = gethostname(tcell.hostName[0], MAXHOSTCHARS);
839     if (code) {
840         bozo_Log("failed to get hostname, code %d\n", errno);
841         exit(1);
842     }
843     if (tcell.hostName[0][0] == 0) {
844         bozo_Log("host name not set, can't start\n");
845         bozo_Log("try the 'hostname' command\n");
846         exit(1);
847     }
848     code = afsconf_SetCellInfo(NULL, AFSDIR_SERVER_ETC_DIRPATH, &tcell);
849     if (code) {
850         bozo_Log
851             ("could not create cell database in '%s' (code %d), quitting\n",
852              AFSDIR_SERVER_ETC_DIRPATH, code);
853         exit(1);
854     }
855     tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
856     if (!tdir) {
857         bozo_Log("failed to open newly-created cell database, quitting\n");
858         exit(1);
859     }
860     return tdir;
861 }
862
863 /* start a process and monitor it */
864
865 #include "AFS_component_version_number.c"
866
867 enum optionsList {
868     OPT_noauth,
869     OPT_log,
870     OPT_restricted,
871     OPT_pidfiles,
872     OPT_auditinterface,
873     OPT_auditlog,
874     OPT_transarc_logs,
875     OPT_peer_stats,
876     OPT_process_stats,
877     OPT_rxbind,
878     OPT_rxmaxmtu,
879     OPT_dotted,
880 #ifndef AFS_NT40_ENV
881     OPT_nofork,
882     OPT_cores,
883     OPT_syslog
884 #endif /* AFS_NT40_ENV */
885 };
886
887 int
888 main(int argc, char **argv, char **envp)
889 {
890     struct cmd_syndesc *opts;
891
892     struct rx_service *tservice;
893     afs_int32 code;
894     struct afsconf_dir *tdir;
895     int noAuth = 0;
896     int i;
897     char *oldlog;
898     int rxMaxMTU = -1;
899     afs_uint32 host = htonl(INADDR_ANY);
900     char *auditIface = NULL;
901     struct cmd_item *auditLogList = NULL;
902     struct rx_securityClass **securityClasses;
903     afs_int32 numClasses;
904     int DoPeerRPCStats = 0;
905     int DoProcessRPCStats = 0;
906     struct stat sb;
907     struct afsconf_bsso_info bsso;
908 #ifndef AFS_NT40_ENV
909     int nofork = 0;
910 #endif
911 #ifdef  AFS_AIX32_ENV
912     struct sigaction nsa;
913
914     /* for some reason, this permits user-mode RX to run a lot faster.
915      * we do it here in the bosserver, so we don't have to do it
916      * individually in each server.
917      */
918     tweak_config();
919
920     /*
921      * The following signal action for AIX is necessary so that in case of a
922      * crash (i.e. core is generated) we can include the user's data section
923      * in the core dump. Unfortunately, by default, only a partial core is
924      * generated which, in many cases, isn't too useful.
925      */
926     sigemptyset(&nsa.sa_mask);
927     nsa.sa_handler = SIG_DFL;
928     nsa.sa_flags = SA_FULLDUMP;
929     sigaction(SIGSEGV, &nsa, NULL);
930     sigaction(SIGABRT, &nsa, NULL);
931 #endif
932     osi_audit_init();
933     signal(SIGFPE, bozo_insecureme);
934
935     memset(&bsso, 0, sizeof(bsso));
936
937 #ifdef AFS_NT40_ENV
938     /* Initialize winsock */
939     if (afs_winsockInit() < 0) {
940         ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0, argv[0], 0);
941         fprintf(stderr, "%s: Couldn't initialize winsock.\n", argv[0]);
942         exit(2);
943     }
944 #endif
945
946     /* Initialize dirpaths */
947     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
948 #ifdef AFS_NT40_ENV
949         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
950 #endif
951         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
952                 argv[0]);
953         exit(2);
954     }
955
956     /* some path inits */
957     bozo_fileName = AFSDIR_SERVER_BOZCONF_FILEPATH;
958     DoCore = strdup(AFSDIR_SERVER_LOGS_DIRPATH);
959     if (!DoCore) {
960         fprintf(stderr, "bosserver: Failed to allocate memory.\n");
961         exit(1);
962     }
963
964     /* initialize the list of dirpaths that the bosserver has
965      * an interest in monitoring */
966     initBosEntryStats();
967
968 #if defined(AFS_SGI_ENV)
969     /* offer some protection if AFS isn't loaded */
970     if (syscall(AFS_SYSCALL, AFSOP_ENDLOG) < 0 && errno == ENOPKG) {
971         printf("bosserver: AFS doesn't appear to be configured in O.S..\n");
972         exit(1);
973     }
974 #endif
975
976 #ifndef AFS_NT40_ENV
977     /* save args for restart */
978     bozo_argc = argc;
979     bozo_argv = malloc((argc+1) * sizeof(char*));
980     if (!bozo_argv) {
981         fprintf(stderr, "%s: Failed to allocate argument list.\n", argv[0]);
982         exit(1);
983     }
984     bozo_argv[0] = (char*)AFSDIR_SERVER_BOSVR_FILEPATH; /* expected path */
985     bozo_argv[bozo_argc] = NULL; /* null terminate list */
986     for (i = 1; i < argc; i++) {
987         bozo_argv[i] = argv[i];
988     }
989 #endif  /* AFS_NT40_ENV */
990
991     /* parse cmd line */
992     opts = cmd_CreateSyntax(NULL, NULL, NULL, 0, NULL);
993
994     /* bosserver specific options */
995     cmd_AddParmAtOffset(opts, OPT_noauth, "-noauth", CMD_FLAG,
996                         CMD_OPTIONAL, "disable authentication");
997     cmd_AddParmAtOffset(opts, OPT_log, "-log", CMD_FLAG,
998                         CMD_OPTIONAL, "enable logging of privileged commands");
999     cmd_AddParmAtOffset(opts, OPT_restricted, "-restricted", CMD_FLAG,
1000                         CMD_OPTIONAL, "enable restricted mode");
1001     cmd_AddParmAtOffset(opts, OPT_pidfiles, "-pidfiles", CMD_SINGLE_OR_FLAG,
1002                         CMD_OPTIONAL, "enable creating pid files");
1003 #ifndef AFS_NT40_ENV
1004     cmd_AddParmAtOffset(opts, OPT_nofork, "-nofork", CMD_FLAG,
1005                         CMD_OPTIONAL, "run in the foreground");
1006     cmd_AddParmAtOffset(opts, OPT_cores, "-cores", CMD_SINGLE,
1007                         CMD_OPTIONAL, "none | path for core files");
1008 #endif /* AFS_NT40_ENV */
1009
1010     /* general server options */
1011     cmd_AddParmAtOffset(opts, OPT_auditinterface, "-audit-interface", CMD_SINGLE,
1012                         CMD_OPTIONAL, "default interface");
1013     cmd_AddParmAtOffset(opts, OPT_auditlog, "-auditlog", CMD_SINGLE,
1014                         CMD_OPTIONAL, "[interface:]path[:options]");
1015     cmd_AddParmAtOffset(opts, OPT_transarc_logs, "-transarc-logs", CMD_FLAG,
1016                         CMD_OPTIONAL, "enable Transarc style logging");
1017
1018 #ifndef AFS_NT40_ENV
1019     cmd_AddParmAtOffset(opts, OPT_syslog, "-syslog", CMD_SINGLE_OR_FLAG,
1020                         CMD_OPTIONAL, "log to syslog");
1021 #endif
1022
1023     /* rx options */
1024     cmd_AddParmAtOffset(opts, OPT_peer_stats, "-enable_peer_stats", CMD_FLAG,
1025                         CMD_OPTIONAL, "enable RX RPC statistics by peer");
1026     cmd_AddParmAtOffset(opts, OPT_process_stats, "-enable_process_stats", CMD_FLAG,
1027                         CMD_OPTIONAL, "enable RX RPC statistics");
1028     cmd_AddParmAtOffset(opts, OPT_rxbind, "-rxbind", CMD_FLAG,
1029                         CMD_OPTIONAL, "bind only to the primary interface");
1030     cmd_AddParmAtOffset(opts, OPT_rxmaxmtu, "-rxmaxmtu", CMD_SINGLE,
1031                         CMD_OPTIONAL, "maximum MTU for RX");
1032     /* rxkad options */
1033     cmd_AddParmAtOffset(opts, OPT_dotted, "-allow-dotted-principals", CMD_FLAG,
1034                         CMD_OPTIONAL, "permit Kerberos 5 principals with dots");
1035
1036     code = cmd_Parse(argc, argv, &opts);
1037     if (code == CMD_HELP) {
1038         exit(0);
1039     }
1040     if (code)
1041         exit(1);
1042
1043     /* bosserver options */
1044     cmd_OptionAsFlag(opts, OPT_noauth, &noAuth);
1045     cmd_OptionAsFlag(opts, OPT_log, &DoLogging);
1046     cmd_OptionAsFlag(opts, OPT_restricted, &bozo_isrestricted);
1047
1048     if (cmd_OptionPresent(opts, OPT_pidfiles)) {
1049         if (cmd_OptionAsString(opts, OPT_pidfiles, &DoPidFiles) != 0) {
1050             DoPidFiles = strdup(AFSDIR_LOCAL_DIR);
1051             if (!DoPidFiles) {
1052                 fprintf(stderr, "bosserver: Failed to allocate memory\n");
1053                 exit(1);
1054             }
1055         }
1056     }
1057
1058 #ifndef AFS_NT40_ENV
1059     cmd_OptionAsFlag(opts, OPT_nofork, &nofork);
1060
1061     if (cmd_OptionAsString(opts, OPT_cores, &DoCore) == 0) {
1062         if (strcmp(DoCore, "none") == 0) {
1063             free(DoCore);
1064             DoCore = NULL;
1065         }
1066     }
1067 #endif
1068
1069     /* general server options */
1070     cmd_OptionAsString(opts, OPT_auditinterface, &auditIface);
1071     cmd_OptionAsList(opts, OPT_auditlog, &auditLogList);
1072
1073     cmd_OptionAsFlag(opts, OPT_transarc_logs, &DoTransarcLogs);
1074
1075 #ifndef AFS_NT40_ENV
1076     if (cmd_OptionPresent(opts, OPT_syslog)) {
1077         DoSyslog = 1;
1078         cmd_OptionAsInt(opts, OPT_syslog, &DoSyslogFacility);
1079     }
1080 #endif
1081
1082     /* rx options */
1083     cmd_OptionAsFlag(opts, OPT_peer_stats, &DoPeerRPCStats);
1084     cmd_OptionAsFlag(opts, OPT_process_stats, &DoProcessRPCStats);
1085     cmd_OptionAsFlag(opts, OPT_rxbind, &rxBind);
1086     cmd_OptionAsInt(opts, OPT_rxmaxmtu, &rxMaxMTU);
1087
1088     /* rxkad options */
1089     cmd_OptionAsFlag(opts, OPT_dotted, &rxkadDisableDotCheck);
1090
1091 #ifndef AFS_NT40_ENV
1092     if (geteuid() != 0) {
1093         printf("bosserver: must be run as root.\n");
1094         exit(1);
1095     }
1096 #endif
1097
1098     /* create useful dirs */
1099     i = CreateDirs(DoCore);
1100     if (i) {
1101         printf("bosserver: could not set up directories, code %d\n", i);
1102         exit(1);
1103     }
1104
1105     if (!DoSyslog) {
1106         /* Support logging to named pipes by not renaming. */
1107         if (DoTransarcLogs
1108             && (lstat(AFSDIR_SERVER_BOZLOG_FILEPATH, &sb) == 0)
1109             && !(S_ISFIFO(sb.st_mode))) {
1110             if (asprintf(&oldlog, "%s.old", AFSDIR_SERVER_BOZLOG_FILEPATH) < 0) {
1111                 printf("bosserver: out of memory\n");
1112                 exit(1);
1113             }
1114             rk_rename(AFSDIR_SERVER_BOZLOG_FILEPATH, oldlog);
1115             free(oldlog);
1116         }
1117         bozo_logFile = fopen(AFSDIR_SERVER_BOZLOG_FILEPATH, "a");
1118         if (!bozo_logFile) {
1119             printf("bosserver: can't initialize log file (%s).\n",
1120                    AFSDIR_SERVER_BOZLOG_FILEPATH);
1121             exit(1);
1122         }
1123         /* keep log closed normally, so can be removed */
1124         fclose(bozo_logFile);
1125     } else {
1126 #ifndef AFS_NT40_ENV
1127         openlog("bosserver", LOG_PID, DoSyslogFacility);
1128 #endif
1129     }
1130
1131     /*
1132      * go into the background and remove our controlling tty, close open
1133      * file desriptors
1134      */
1135
1136 #ifndef AFS_NT40_ENV
1137     if (!nofork) {
1138         if (daemon(1, 0))
1139             printf("bosserver: warning - daemon() returned code %d\n", errno);
1140     }
1141 #endif /* ! AFS_NT40_ENV */
1142
1143     /* Write current state of directory permissions to log file */
1144     DirAccessOK();
1145
1146     /* chdir to AFS log directory */
1147     if (DoCore)
1148         i = chdir(DoCore);
1149     else
1150         i = chdir(AFSDIR_SERVER_LOGS_DIRPATH);
1151     if (i) {
1152         printf("bosserver: could not change to %s, code %d\n",
1153                DoCore ? DoCore : AFSDIR_SERVER_LOGS_DIRPATH, errno);
1154         exit(1);
1155     }
1156
1157     /* Process the audit related options now that the directory checks are
1158      * done. */
1159     code = osi_audit_cmd_Options(auditIface, auditLogList);
1160     free(auditIface);
1161     if (code)
1162        exit(1);
1163
1164     /* try to read the key from the config file */
1165     tdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
1166     if (!tdir) {
1167         tdir = CreateLocalCellConfig();
1168     }
1169     /* opened the cell databse */
1170     bozo_confdir = tdir;
1171
1172     code = bnode_Init();
1173     if (code) {
1174         printf("bosserver: could not init bnode package, code %d\n", code);
1175         exit(1);
1176     }
1177
1178     bnode_Register("fs", &fsbnode_ops, 3);
1179     bnode_Register("dafs", &dafsbnode_ops, 4);
1180     bnode_Register("simple", &ezbnode_ops, 1);
1181     bnode_Register("cron", &cronbnode_ops, 2);
1182
1183 #if defined(RLIMIT_CORE) && defined(HAVE_GETRLIMIT)
1184     {
1185       struct rlimit rlp;
1186       getrlimit(RLIMIT_CORE, &rlp);
1187       if (!DoCore)
1188           rlp.rlim_cur = 0;
1189       else
1190           rlp.rlim_max = rlp.rlim_cur = RLIM_INFINITY;
1191       setrlimit(RLIMIT_CORE, &rlp);
1192       getrlimit(RLIMIT_CORE, &rlp);
1193       bozo_Log("Core limits now %d %d\n",(int)rlp.rlim_cur,(int)rlp.rlim_max);
1194     }
1195 #endif
1196
1197     /* Read init file, starting up programs. Also starts watcher threads. */
1198     if ((code = ReadBozoFile(0))) {
1199         bozo_Log
1200             ("bosserver: Something is wrong (%d) with the bos configuration file %s; aborting\n",
1201              code, AFSDIR_SERVER_BOZCONF_FILEPATH);
1202         exit(code);
1203     }
1204
1205     if (rxBind) {
1206         host = GetRxBindAddress();
1207     }
1208     for (i = 0; i < 10; i++) {
1209         code = rx_InitHost(host, htons(AFSCONF_NANNYPORT));
1210         if (code) {
1211             bozo_Log("can't initialize rx: code=%d\n", code);
1212             sleep(3);
1213         } else
1214             break;
1215     }
1216     if (i >= 10) {
1217         bozo_Log("Bos giving up, can't initialize rx\n");
1218         exit(code);
1219     }
1220
1221     /* Set some rx config */
1222     if (DoPeerRPCStats)
1223         rx_enablePeerRPCStats();
1224     if (DoProcessRPCStats)
1225         rx_enableProcessRPCStats();
1226
1227     /* Disable jumbograms */
1228     rx_SetNoJumbo();
1229
1230     if (rxMaxMTU != -1) {
1231         if (rx_SetMaxMTU(rxMaxMTU) != 0) {
1232             bozo_Log("bosserver: rxMaxMTU %d is invalid\n", rxMaxMTU);
1233             exit(1);
1234         }
1235     }
1236
1237     code = LWP_CreateProcess(BozoDaemon, BOZO_LWP_STACKSIZE, /* priority */ 1,
1238                              /* param */ NULL , "bozo-the-clown", &bozo_pid);
1239     if (code) {
1240         bozo_Log("Failed to create daemon thread\n");
1241         exit(1);
1242     }
1243
1244     /* initialize audit user check */
1245     osi_audit_set_user_check(bozo_confdir, bozo_IsLocalRealmMatch);
1246
1247     /* Finish audit initialization */
1248     osi_audit_open();
1249
1250     bozo_CreateRxBindFile(host);        /* for local scripts */
1251
1252     /* allow super users to manage RX statistics */
1253     rx_SetRxStatUserOk(bozo_rxstat_userok);
1254
1255     afsconf_SetNoAuthFlag(tdir, noAuth);
1256
1257     bsso.dir = tdir;
1258     bsso.logger = bozo_Log;
1259     afsconf_BuildServerSecurityObjects_int(&bsso, &securityClasses, &numClasses);
1260
1261     if (DoPidFiles) {
1262         bozo_CreatePidFile("bosserver", NULL, getpid());
1263     }
1264
1265     tservice = rx_NewServiceHost(host, 0, /* service id */ 1,
1266                                  "bozo", securityClasses, numClasses,
1267                                  BOZO_ExecuteRequest);
1268     rx_SetMinProcs(tservice, 2);
1269     rx_SetMaxProcs(tservice, 4);
1270     rx_SetStackSize(tservice, BOZO_LWP_STACKSIZE);      /* so gethostbyname works (in cell stuff) */
1271     if (rxkadDisableDotCheck) {
1272         code = rx_SetSecurityConfiguration(tservice, RXS_CONFIG_FLAGS,
1273                                            (void *)RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
1274         if (code) {
1275             bozo_Log("Failed to allow dotted principals: code %d\n", code);
1276             exit(1);
1277         }
1278     }
1279
1280     tservice =
1281         rx_NewServiceHost(host, 0, RX_STATS_SERVICE_ID, "rpcstats",
1282                           securityClasses, numClasses, RXSTATS_ExecuteRequest);
1283     rx_SetMinProcs(tservice, 2);
1284     rx_SetMaxProcs(tservice, 4);
1285     rx_StartServer(1);          /* donate this process */
1286     return 0;
1287 }
1288
1289 void
1290 bozo_Log(const char *format, ...)
1291 {
1292     char tdate[27];
1293     time_t myTime;
1294     va_list ap;
1295
1296     va_start(ap, format);
1297
1298     if (DoSyslog) {
1299 #ifndef AFS_NT40_ENV
1300         vsyslog(LOG_INFO, format, ap);
1301 #endif
1302     } else {
1303         myTime = time(0);
1304         strcpy(tdate, ctime(&myTime));  /* copy out of static area asap */
1305         tdate[24] = ':';
1306
1307         /* log normally closed, so can be removed */
1308
1309         bozo_logFile = fopen(AFSDIR_SERVER_BOZLOG_FILEPATH, "a");
1310         if (bozo_logFile == NULL) {
1311             printf("bosserver: WARNING: problem with %s\n",
1312                    AFSDIR_SERVER_BOZLOG_FILEPATH);
1313             printf("%s ", tdate);
1314             vprintf(format, ap);
1315             fflush(stdout);
1316         } else {
1317             fprintf(bozo_logFile, "%s ", tdate);
1318             vfprintf(bozo_logFile, format, ap);
1319
1320             /* close so rm BosLog works */
1321             fclose(bozo_logFile);
1322         }
1323     }
1324     va_end(ap);
1325 }