Define WCOREDUMP in salvsync-server.c
[openafs.git] / src / bozo / bnode.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
13
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <dirent.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #ifdef AFS_NT40_ENV
21 #include <io.h>
22 #else
23 #include <sys/file.h>
24 #include <sys/time.h>
25 #endif
26 #ifdef BOZO_SAVE_CORES
27 #include <time.h>
28 #endif
29 #include <sys/stat.h>
30 #include <string.h>
31
32 #include <afs/procmgmt.h>       /* signal(), kill(), wait(), etc. */
33 #include <lwp.h>
34 #include <rx/rx.h>
35 #include <afs/audit.h>
36 #include <afs/afsutil.h>
37 #include <afs/fileutil.h>
38 #include "bnode.h"
39 #include "bosprototypes.h"
40
41 #ifndef WCOREDUMP
42 #define WCOREDUMP(x) ((x) & 0200)
43 #endif
44
45 #define BNODE_LWP_STACKSIZE     (16 * 1024)
46
47 int bnode_waiting = 0;
48 static PROCESS bproc_pid;       /* pid of waker-upper */
49 static struct bnode *allBnodes = 0;     /* list of all bnodes */
50 static struct bnode_proc *allProcs = 0; /* list of all processes for which we're waiting */
51 static struct bnode_type *allTypes = 0; /* list of registered type handlers */
52
53 static struct bnode_stats {
54     int weirdPids;
55 } bnode_stats;
56
57 #ifndef AFS_NT40_ENV
58 extern char **environ;          /* env structure */
59 #endif
60
61 int hdl_notifier(struct bnode_proc *tp);
62
63 /* Remember the name of the process, if any, that failed last */
64 static void
65 RememberProcName(register struct bnode_proc *ap)
66 {
67     register struct bnode *tbnodep;
68
69     tbnodep = ap->bnode;
70     if (tbnodep->lastErrorName) {
71         free(tbnodep->lastErrorName);
72         tbnodep->lastErrorName = NULL;
73     }
74     if (ap->coreName) {
75         tbnodep->lastErrorName = (char *)malloc(strlen(ap->coreName) + 1);
76         strcpy(tbnodep->lastErrorName, ap->coreName);
77     }
78 }
79
80 /* utility for use by BOP_HASCORE functions to determine where a core file might
81  * be stored.
82  */
83 int
84 bnode_CoreName(register struct bnode *abnode, char *acoreName, char *abuffer)
85 {
86     strcpy(abuffer, AFSDIR_SERVER_CORELOG_FILEPATH);
87     if (acoreName) {
88         strcat(abuffer, acoreName);
89         strcat(abuffer, ".");
90     }
91     strcat(abuffer, abnode->name);
92     return 0;
93 }
94
95 /* save core file, if any */
96 static void
97 SaveCore(register struct bnode *abnode, register struct bnode_proc
98          *aproc)
99 {
100     char tbuffer[256];
101     struct stat tstat;
102     register afs_int32 code;
103     char *corefile = NULL;
104 #ifdef BOZO_SAVE_CORES
105     struct timeval Start;
106     struct tm *TimeFields;
107     char FileName[256];
108 #endif
109
110     /* Linux always appends the PID to core dumps from threaded processes, so
111      * we have to scan the directory to find core files under another name. */
112     code = stat(AFSDIR_SERVER_CORELOG_FILEPATH, &tstat);
113     if (code) {
114         DIR *logdir;
115         struct dirent *file;
116         size_t length;
117         unsigned long pid;
118
119         logdir = opendir(AFSDIR_LOGS_DIR);
120         if (logdir == NULL)
121             return;
122         while ((file = readdir(logdir)) != NULL) {
123             if (strncmp(file->d_name, "core.", 5) != 0)
124                 continue;
125             pid = atol(file->d_name + 5);
126             if (pid == aproc->pid) {
127                 length = strlen(AFSDIR_LOGS_DIR) + strlen(file->d_name) + 2;
128                 corefile = malloc(length);
129                 if (corefile == NULL) {
130                     closedir(logdir);
131                     return;
132                 }
133                 snprintf(corefile, length, "%s/%s", AFSDIR_LOGS_DIR,
134                          file->d_name);
135                 code = 0;
136                 break;
137             }
138         }
139         closedir(logdir);
140     }
141     if (code)
142         return;
143
144     bnode_CoreName(abnode, aproc->coreName, tbuffer);
145 #ifdef BOZO_SAVE_CORES
146     FT_GetTimeOfDay(&Start, 0);
147     TimeFields = localtime(&Start.tv_sec);
148     sprintf(FileName, "%s.%d%02d%02d%02d%02d%02d", tbuffer,
149             TimeFields->tm_year + 1900, TimeFields->tm_mon + 1, TimeFields->tm_mday,
150             TimeFields->tm_hour, TimeFields->tm_min, TimeFields->tm_sec);
151     strcpy(tbuffer, FileName);
152 #endif
153     if (corefile == NULL)
154         code = renamefile(AFSDIR_SERVER_CORELOG_FILEPATH, tbuffer);
155     else {
156         code = renamefile(corefile, tbuffer);
157         free(corefile);
158     }
159 }
160
161 int
162 bnode_GetString(register struct bnode *abnode, register char *abuffer,
163                 register afs_int32 alen)
164 {
165     return BOP_GETSTRING(abnode, abuffer, alen);
166 }
167
168 int
169 bnode_GetParm(register struct bnode *abnode, register afs_int32 aindex,
170               register char *abuffer, afs_int32 alen)
171 {
172     return BOP_GETPARM(abnode, aindex, abuffer, alen);
173 }
174
175 int
176 bnode_GetStat(register struct bnode *abnode, register afs_int32 * astatus)
177 {
178     return BOP_GETSTAT(abnode, astatus);
179 }
180
181 int
182 bnode_RestartP(register struct bnode *abnode)
183 {
184     return BOP_RESTARTP(abnode);
185 }
186
187 static int
188 bnode_Check(register struct bnode *abnode)
189 {
190     if (abnode->flags & BNODE_WAIT) {
191         abnode->flags &= ~BNODE_WAIT;
192         LWP_NoYieldSignal(abnode);
193     }
194     return 0;
195 }
196
197 /* tell if an instance has a core file */
198 int
199 bnode_HasCore(register struct bnode *abnode)
200 {
201     return BOP_HASCORE(abnode);
202 }
203
204 /* wait for all bnodes to stabilize */
205 int
206 bnode_WaitAll(void)
207 {
208     register struct bnode *tb;
209     register afs_int32 code;
210     afs_int32 stat;
211
212   retry:
213     for (tb = allBnodes; tb; tb = tb->next) {
214         bnode_Hold(tb);
215         code = BOP_GETSTAT(tb, &stat);
216         if (code) {
217             bnode_Release(tb);
218             return code;
219         }
220         if (stat != tb->goal) {
221             tb->flags |= BNODE_WAIT;
222             LWP_WaitProcess(tb);
223             bnode_Release(tb);
224             goto retry;
225         }
226         bnode_Release(tb);
227     }
228     return 0;
229 }
230
231 /* wait until bnode status is correct */
232 int
233 bnode_WaitStatus(register struct bnode *abnode, int astatus)
234 {
235     register afs_int32 code;
236     afs_int32 stat;
237
238     bnode_Hold(abnode);
239     while (1) {
240         /* get the status */
241         code = BOP_GETSTAT(abnode, &stat);
242         if (code)
243             return code;
244
245         /* otherwise, check if we're done */
246         if (stat == astatus) {
247             bnode_Release(abnode);
248             return 0;           /* done */
249         }
250         if (astatus != abnode->goal) {
251             bnode_Release(abnode);
252             return -1;          /* no longer our goal, don't keep waiting */
253         }
254         /* otherwise, block */
255         abnode->flags |= BNODE_WAIT;
256         LWP_WaitProcess(abnode);
257     }
258 }
259
260 int
261 bnode_SetStat(register struct bnode *abnode, register int agoal)
262 {
263     abnode->goal = agoal;
264     bnode_Check(abnode);
265     BOP_SETSTAT(abnode, agoal);
266     abnode->flags &= ~BNODE_ERRORSTOP;
267     return 0;
268 }
269
270 int
271 bnode_SetGoal(register struct bnode *abnode, register int agoal)
272 {
273     abnode->goal = agoal;
274     bnode_Check(abnode);
275     return 0;
276 }
277
278 int
279 bnode_SetFileGoal(register struct bnode *abnode, register int agoal)
280 {
281     if (abnode->fileGoal == agoal)
282         return 0;               /* already done */
283     abnode->fileGoal = agoal;
284     WriteBozoFile(0);
285     return 0;
286 }
287
288 /* apply a function to all bnodes in the system */
289 int
290 bnode_ApplyInstance(int (*aproc) (struct bnode *tb, void *), void *arock)
291 {
292     register struct bnode *tb, *nb;
293     register afs_int32 code;
294
295     for (tb = allBnodes; tb; tb = nb) {
296         nb = tb->next;
297         code = (*aproc) (tb, arock);
298         if (code)
299             return code;
300     }
301     return 0;
302 }
303
304 struct bnode *
305 bnode_FindInstance(register char *aname)
306 {
307     register struct bnode *tb;
308
309     for (tb = allBnodes; tb; tb = tb->next) {
310         if (!strcmp(tb->name, aname))
311             return tb;
312     }
313     return NULL;
314 }
315
316 static struct bnode_type *
317 FindType(register char *aname)
318 {
319     register struct bnode_type *tt;
320
321     for (tt = allTypes; tt; tt = tt->next) {
322         if (!strcmp(tt->name, aname))
323             return tt;
324     }
325     return (struct bnode_type *)0;
326 }
327
328 int
329 bnode_Register(char *atype, struct bnode_ops *aprocs, int anparms)
330 {
331     register struct bnode_type *tt;
332
333     for (tt = allTypes; tt; tt = tt->next) {
334         if (!strcmp(tt->name, atype))
335             break;
336     }
337     if (!tt) {
338         tt = (struct bnode_type *)malloc(sizeof(struct bnode_type));
339         memset(tt, 0, sizeof(struct bnode_type));
340         tt->next = allTypes;
341         allTypes = tt;
342         tt->name = atype;
343     }
344     tt->ops = aprocs;
345     return 0;
346 }
347
348 afs_int32
349 bnode_Create(char *atype, char *ainstance, struct bnode ** abp, char *ap1,
350              char *ap2, char *ap3, char *ap4, char *ap5, char *notifier,
351              int fileGoal, int rewritefile)
352 {
353     struct bnode_type *type;
354     struct bnode *tb;
355     char *notifierpath = NULL;
356     struct stat tstat;
357
358     if (bnode_FindInstance(ainstance))
359         return BZEXISTS;
360     type = FindType(atype);
361     if (!type)
362         return BZBADTYPE;
363
364     if (notifier && strcmp(notifier, NONOTIFIER)) {
365         /* construct local path from canonical (wire-format) path */
366         if (ConstructLocalBinPath(notifier, &notifierpath)) {
367             bozo_Log("BNODE-Create: Notifier program path invalid '%s'\n",
368                      notifier);
369             return BZNOCREATE;
370         }
371
372         if (stat(notifierpath, &tstat)) {
373             bozo_Log("BNODE-Create: Notifier program '%s' not found\n",
374                      notifierpath);
375             free(notifierpath);
376             return BZNOCREATE;
377         }
378     }
379     tb = (*type->ops->create) (ainstance, ap1, ap2, ap3, ap4, ap5);
380     if (!tb) {
381         free(notifierpath);
382         return BZNOCREATE;
383     }
384     tb->notifier = notifierpath;
385     *abp = tb;
386     tb->type = type;
387
388     /* The fs_create above calls bnode_InitBnode() which always sets the 
389      ** fileGoal to BSTAT_NORMAL .... overwrite it with whatever is passed into
390      ** this function as a parameter... */
391     tb->fileGoal = fileGoal;
392
393     bnode_SetStat(tb, tb->goal);        /* nudge it once */
394
395     if (rewritefile != 0)
396         WriteBozoFile(0);
397
398     return 0;
399 }
400
401 int
402 bnode_DeleteName(char *ainstance)
403 {
404     register struct bnode *tb;
405
406     tb = bnode_FindInstance(ainstance);
407     if (!tb)
408         return BZNOENT;
409
410     return bnode_Delete(tb);
411 }
412
413 int
414 bnode_Hold(register struct bnode *abnode)
415 {
416     abnode->refCount++;
417     return 0;
418 }
419
420 int
421 bnode_Release(register struct bnode *abnode)
422 {
423     abnode->refCount--;
424     if (abnode->refCount == 0 && abnode->flags & BNODE_DELETE) {
425         abnode->flags &= ~BNODE_DELETE; /* we're going for it */
426         bnode_Delete(abnode);
427     }
428     return 0;
429 }
430
431 int
432 bnode_Delete(register struct bnode *abnode)
433 {
434     register afs_int32 code;
435     register struct bnode **lb, *ub;
436     afs_int32 temp;
437
438     if (abnode->refCount != 0) {
439         abnode->flags |= BNODE_DELETE;
440         return 0;
441     }
442
443     /* make sure the bnode is idle before zapping */
444     bnode_Hold(abnode);
445     code = BOP_GETSTAT(abnode, &temp);
446     bnode_Release(abnode);
447     if (code)
448         return code;
449     if (temp != BSTAT_SHUTDOWN)
450         return BZBUSY;
451
452     /* all clear to zap */
453     for (lb = &allBnodes, ub = *lb; ub; lb = &ub->next, ub = *lb) {
454         if (ub == abnode) {
455             /* unthread it from the list */
456             *lb = ub->next;
457             break;
458         }
459     }
460     free(abnode->name);         /* do this first, since bnode fields may be bad after BOP_DELETE */
461     code = BOP_DELETE(abnode);  /* don't play games like holding over this one */
462     WriteBozoFile(0);
463     return code;
464 }
465
466 /* function to tell if there's a timeout coming up */
467 int
468 bnode_PendingTimeout(register struct bnode *abnode)
469 {
470     return (abnode->flags & BNODE_NEEDTIMEOUT);
471 }
472
473 /* function called to set / clear periodic bnode wakeup times */
474 int
475 bnode_SetTimeout(register struct bnode *abnode, afs_int32 atimeout)
476 {
477     if (atimeout != 0) {
478         abnode->nextTimeout = FT_ApproxTime() + atimeout;
479         abnode->flags |= BNODE_NEEDTIMEOUT;
480         abnode->period = atimeout;
481         IOMGR_Cancel(bproc_pid);
482     } else {
483         abnode->flags &= ~BNODE_NEEDTIMEOUT;
484     }
485     return 0;
486 }
487
488 /* used by new bnode creation code to format bnode header */
489 int
490 bnode_InitBnode(register struct bnode *abnode, struct bnode_ops *abnodeops,
491                 char *aname)
492 {
493     struct bnode **lb, *nb;
494
495     /* format the bnode properly */
496     memset(abnode, 0, sizeof(struct bnode));
497     abnode->ops = abnodeops;
498     abnode->name = (char *)malloc(strlen(aname) + 1);
499     if (!abnode->name)
500         return ENOMEM;
501     strcpy(abnode->name, aname);
502     abnode->flags = BNODE_ACTIVE;
503     abnode->fileGoal = BSTAT_NORMAL;
504     abnode->goal = BSTAT_SHUTDOWN;
505
506     /* put the bnode at the end of the list so we write bnode file in same order */
507     for (lb = &allBnodes, nb = *lb; nb; lb = &nb->next, nb = *lb);
508     *lb = abnode;
509
510     return 0;
511 }
512
513 static int
514 DeleteProc(register struct bnode_proc *abproc)
515 {
516     register struct bnode_proc **pb, *tb;
517     struct bnode_proc *nb;
518
519     for (pb = &allProcs, tb = *pb; tb; pb = &tb->next, tb = nb) {
520         nb = tb->next;
521         if (tb == abproc) {
522             *pb = nb;
523             free(tb);
524             return 0;
525         }
526     }
527     return BZNOENT;
528 }
529
530 /* bnode lwp executes this code repeatedly */
531 static void *
532 bproc(void *unused)
533 {
534     register afs_int32 code;
535     register struct bnode *tb;
536     register afs_int32 temp;
537     register struct bnode_proc *tp;
538     struct bnode *nb;
539     int options;                /* must not be register */
540     struct timeval tv;
541     int setAny;
542     int status;
543
544     while (1) {
545         /* first figure out how long to sleep for */
546         temp = 0x7fffffff;      /* afs_int32 time; maxint doesn't work in select */
547         setAny = 0;
548         for (tb = allBnodes; tb; tb = tb->next) {
549             if (tb->flags & BNODE_NEEDTIMEOUT) {
550                 if (tb->nextTimeout < temp) {
551                     setAny = 1;
552                     temp = tb->nextTimeout;
553                 }
554             }
555         }
556         /* now temp has the time at which we should wakeup next */
557
558         /* sleep */
559         if (setAny)
560             temp -= FT_ApproxTime();    /* how many seconds until next event */
561         else
562             temp = 999999;
563         if (temp > 0) {
564             tv.tv_sec = temp;
565             tv.tv_usec = 0;
566             code = IOMGR_Select(0, 0, 0, 0, &tv);
567         } else
568             code = 0;           /* fake timeout code */
569
570         /* figure out why we woke up; child exit or timeouts */
571         FT_GetTimeOfDay(&tv, 0);        /* must do the real gettimeofday once and a while */
572         temp = tv.tv_sec;
573
574         /* check all bnodes to see which ones need timeout events */
575         for (tb = allBnodes; tb; tb = nb) {
576             if ((tb->flags & BNODE_NEEDTIMEOUT) && temp > tb->nextTimeout) {
577                 bnode_Hold(tb);
578                 BOP_TIMEOUT(tb);
579                 bnode_Check(tb);
580                 if (tb->flags & BNODE_NEEDTIMEOUT) {    /* check again, BOP_TIMEOUT could change */
581                     tb->nextTimeout = FT_ApproxTime() + tb->period;
582                 }
583                 nb = tb->next;
584                 bnode_Release(tb);      /* delete may occur here */
585             } else
586                 nb = tb->next;
587         }
588
589         if (code < 0) {
590             /* signalled, probably by incoming signal */
591             while (1) {
592                 options = WNOHANG;
593                 bnode_waiting = options | 0x800000;
594                 code = waitpid((pid_t) - 1, &status, options);
595                 bnode_waiting = 0;
596                 if (code == 0 || code == -1)
597                     break;      /* all done */
598                 /* otherwise code has a process id, which we now search for */
599                 for (tp = allProcs; tp; tp = tp->next)
600                     if (tp->pid == code)
601                         break;
602                 if (tp) {
603                     /* found the pid */
604                     tb = tp->bnode;
605                     bnode_Hold(tb);
606
607                     /* count restarts in last 10 seconds */
608                     if (temp > tb->rsTime + 30) {
609                         /* it's been 10 seconds we've been counting */
610                         tb->rsTime = temp;
611                         tb->rsCount = 0;
612                     }
613
614                     if (WIFSIGNALED(status) == 0) {
615                         /* exited, not signalled */
616                         tp->lastExit = WEXITSTATUS(status);
617                         tp->lastSignal = 0;
618                         if (tp->lastExit) {
619                             tb->errorCode = tp->lastExit;
620                             tb->lastErrorExit = FT_ApproxTime();
621                             RememberProcName(tp);
622                             tb->errorSignal = 0;
623                         }
624                         if (tp->coreName)
625                             bozo_Log("%s:%s exited with code %d\n", tb->name,
626                                      tp->coreName, tp->lastExit);
627                         else
628                             bozo_Log("%s exited with code %d\n", tb->name,
629                                      tp->lastExit);
630                     } else {
631                         /* Signal occurred, perhaps spurious due to shutdown request.
632                          * If due to a shutdown request, don't overwrite last error
633                          * information.
634                          */
635                         tp->lastSignal = WTERMSIG(status);
636                         tp->lastExit = 0;
637                         if (tp->lastSignal != SIGQUIT
638                             && tp->lastSignal != SIGTERM
639                             && tp->lastSignal != SIGKILL) {
640                             tb->errorSignal = tp->lastSignal;
641                             tb->lastErrorExit = FT_ApproxTime();
642                             RememberProcName(tp);
643                         }
644                         if (tp->coreName)
645                             bozo_Log("%s:%s exited on signal %d%s\n",
646                                      tb->name, tp->coreName, tp->lastSignal,
647                                      WCOREDUMP(status) ? " (core dumped)" :
648                                      "");
649                         else
650                             bozo_Log("%s exited on signal %d%s\n", tb->name,
651                                      tp->lastSignal,
652                                      WCOREDUMP(status) ? " (core dumped)" :
653                                      "");
654                         SaveCore(tb, tp);
655                     }
656                     tb->lastAnyExit = FT_ApproxTime();
657
658                     if (tb->notifier) {
659                         bozo_Log("BNODE: Notifier %s will be called\n",
660                                  tb->notifier);
661                         hdl_notifier(tp);
662                     }
663                     BOP_PROCEXIT(tb, tp);
664
665                     bnode_Check(tb);
666                     if (tb->rsCount++ > 10) {
667                         /* 10 in 10 seconds */
668                         tb->flags |= BNODE_ERRORSTOP;
669                         bnode_SetGoal(tb, BSTAT_SHUTDOWN);
670                         bozo_Log
671                             ("BNODE '%s' repeatedly failed to start, perhaps missing executable.\n",
672                              tb->name);
673                     }
674                     bnode_Release(tb);  /* bnode delete can happen here */
675                     DeleteProc(tp);
676                 } else
677                     bnode_stats.weirdPids++;
678             }
679         }
680     }
681     return NULL;
682 }
683
684 static afs_int32
685 SendNotifierData(register int fd, register struct bnode_proc *tp)
686 {
687     register struct bnode *tb = tp->bnode;
688     char buffer[1000], *bufp = buffer, *buf1;
689     register int len;
690
691     /*
692      * First sent out the bnode_proc struct
693      */
694     (void)sprintf(bufp, "BEGIN bnode_proc\n");
695     bufp += strlen(bufp);
696     (void)sprintf(bufp, "comLine: %s\n", tp->comLine);
697     bufp += strlen(bufp);
698     if (!(buf1 = tp->coreName))
699         buf1 = "(null)";
700     (void)sprintf(bufp, "coreName: %s\n", buf1);
701     bufp += strlen(bufp);
702     (void)sprintf(bufp, "pid: %ld\n", afs_printable_int32_ld(tp->pid));
703     bufp += strlen(bufp);
704     (void)sprintf(bufp, "lastExit: %ld\n", afs_printable_int32_ld(tp->lastExit));
705     bufp += strlen(bufp);
706 #ifdef notdef
707     (void)sprintf(bufp, "lastSignal: %ld\n", afs_printable_int32_ld(tp->lastSignal));
708     bufp += strlen(bufp);
709 #endif
710     (void)sprintf(bufp, "flags: %ld\n", afs_printable_int32_ld(tp->flags));
711     bufp += strlen(bufp);
712     (void)sprintf(bufp, "END bnode_proc\n");
713     bufp += strlen(bufp);
714     len = (int)(bufp - buffer);
715     if (write(fd, buffer, len) < 0) {
716         return -1;
717     }
718
719     /*
720      * Now sent out the bnode struct
721      */
722     bufp = buffer;
723     (void)sprintf(bufp, "BEGIN bnode\n");
724     bufp += strlen(bufp);
725     (void)sprintf(bufp, "name: %s\n", tb->name);
726     bufp += strlen(bufp);
727     (void)sprintf(bufp, "rsTime: %ld\n", afs_printable_int32_ld(tb->rsTime));
728     bufp += strlen(bufp);
729     (void)sprintf(bufp, "rsCount: %ld\n", afs_printable_int32_ld(tb->rsCount));
730     bufp += strlen(bufp);
731     (void)sprintf(bufp, "procStartTime: %ld\n", afs_printable_int32_ld(tb->procStartTime));
732     bufp += strlen(bufp);
733     (void)sprintf(bufp, "procStarts: %ld\n", afs_printable_int32_ld(tb->procStarts));
734     bufp += strlen(bufp);
735     (void)sprintf(bufp, "lastAnyExit: %ld\n", afs_printable_int32_ld(tb->lastAnyExit));
736     bufp += strlen(bufp);
737     (void)sprintf(bufp, "lastErrorExit: %ld\n", afs_printable_int32_ld(tb->lastErrorExit));
738     bufp += strlen(bufp);
739     (void)sprintf(bufp, "errorCode: %ld\n", afs_printable_int32_ld(tb->errorCode));
740     bufp += strlen(bufp);
741     (void)sprintf(bufp, "errorSignal: %ld\n", afs_printable_int32_ld(tb->errorSignal));
742     bufp += strlen(bufp);
743 /*
744     (void) sprintf(bufp, "lastErrorName: %s\n", tb->lastErrorName);
745     bufp += strlen(bufp);
746 */
747     (void)sprintf(bufp, "goal: %d\n", tb->goal);
748     bufp += strlen(bufp);
749     (void)sprintf(bufp, "END bnode\n");
750     bufp += strlen(bufp);
751     len = (int)(bufp - buffer);
752     if (write(fd, buffer, len) < 0) {
753         return -1;
754     }
755     return 0;
756 }
757
758 int
759 hdl_notifier(struct bnode_proc *tp)
760 {
761 #ifndef AFS_NT40_ENV            /* NT notifier callout not yet implemented */
762     int code, pid;
763     struct stat tstat;
764
765     if (stat(tp->bnode->notifier, &tstat)) {
766         bozo_Log("BNODE: Failed to find notifier '%s'; ignored\n",
767                  tp->bnode->notifier);
768         return (1);
769     }
770     if ((pid = fork()) == 0) {
771         FILE *fout;
772         struct bnode *tb = tp->bnode;
773         int ec;
774
775 #if defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_SGI51_ENV)
776         ec = setsid();
777 #elif defined(AFS_DARWIN90_ENV)
778         ec = setpgid(0, 0);
779 #elif defined(AFS_LINUX20_ENV) || defined(AFS_AIX_ENV)  
780         ec = setpgrp();
781 #else
782         ec = setpgrp(0, 0);
783 #endif
784         fout = popen(tb->notifier, "w");
785         if (fout == NULL) {
786             bozo_Log("BNODE: Failed to find notifier '%s'; ignored\n",
787                      tb->notifier);
788             perror(tb->notifier);
789             exit(1);
790         }
791         code = SendNotifierData(fileno(fout), tp);
792         pclose(fout);
793         exit(0);
794     } else if (pid < 0) {
795         bozo_Log("Failed to fork creating process to handle notifier '%s'\n",
796                  tp->bnode->notifier);
797         return -1;
798     }
799 #endif /* AFS_NT40_ENV */
800     return (0);
801 }
802
803 /* Called by IOMGR at low priority on IOMGR's stack shortly after a SIGCHLD
804  * occurs.  Wakes up bproc do redo things */
805 void *
806 bnode_SoftInt(void *param)
807 {
808     /* int asignal = (int) param; */
809
810     IOMGR_Cancel(bproc_pid);
811     return 0;
812 }
813
814 /* Called at signal interrupt level; queues function to be called
815  * when IOMGR runs again.
816  */
817 void
818 bnode_Int(int asignal)
819 {
820     if (asignal == SIGQUIT) {
821         IOMGR_SoftSig(bozo_ShutdownAndExit, (void *)(intptr_t)asignal);
822     } else {
823         IOMGR_SoftSig(bnode_SoftInt, (void *)(intptr_t)asignal);
824     }
825 }
826
827
828 /* intialize the whole system */
829 int
830 bnode_Init(void)
831 {
832     PROCESS junk;
833     register afs_int32 code;
834     struct sigaction newaction;
835     static int initDone = 0;
836
837     if (initDone)
838         return 0;
839     initDone = 1;
840     memset(&bnode_stats, 0, sizeof(bnode_stats));
841     LWP_InitializeProcessSupport(1, &junk);     /* just in case */
842     IOMGR_Initialize();
843     code = LWP_CreateProcess(bproc, BNODE_LWP_STACKSIZE,
844                              /* priority */ 1, (void *) /* parm */ 0,
845                              "bnode-manager", &bproc_pid);
846     if (code)
847         return code;
848     memset(&newaction, 0, sizeof(newaction));
849     newaction.sa_handler = bnode_Int;
850     code = sigaction(SIGCHLD, &newaction, NULL);
851     if (code)
852         return errno;
853     code = sigaction(SIGQUIT, &newaction, NULL);
854     if (code)
855         return errno;
856     return code;
857 }
858
859 /* free token list returned by parseLine */
860 int
861 bnode_FreeTokens(register struct bnode_token *alist)
862 {
863     register struct bnode_token *nlist;
864     for (; alist; alist = nlist) {
865         nlist = alist->next;
866         free(alist->key);
867         free(alist);
868     }
869     return 0;
870 }
871
872 static int
873 space(int x)
874 {
875     if (x == 0 || x == ' ' || x == '\t' || x == '\n')
876         return 1;
877     else
878         return 0;
879 }
880
881 int
882 bnode_ParseLine(char *aline, struct bnode_token **alist)
883 {
884     char tbuffer[256];
885     register char *tptr = NULL;
886     int inToken;
887     struct bnode_token *first, *last;
888     register struct bnode_token *ttok;
889     register int tc;
890
891     inToken = 0;                /* not copying token chars at start */
892     first = (struct bnode_token *)0;
893     last = (struct bnode_token *)0;
894     while (1) {
895         tc = *aline++;
896         if (tc == 0 || space(tc)) {     /* terminating null gets us in here, too */
897             if (inToken) {
898                 inToken = 0;    /* end of this token */
899                 *tptr++ = 0;
900                 ttok =
901                     (struct bnode_token *)malloc(sizeof(struct bnode_token));
902                 ttok->next = (struct bnode_token *)0;
903                 ttok->key = (char *)malloc(strlen(tbuffer) + 1);
904                 strcpy(ttok->key, tbuffer);
905                 if (last) {
906                     last->next = ttok;
907                     last = ttok;
908                 } else
909                     last = ttok;
910                 if (!first)
911                     first = ttok;
912             }
913         } else {
914             /* an alpha character */
915             if (!inToken) {
916                 tptr = tbuffer;
917                 inToken = 1;
918             }
919             if (tptr - tbuffer >= sizeof(tbuffer))
920                 return -1;      /* token too long */
921             *tptr++ = tc;
922         }
923         if (tc == 0) {
924             /* last token flushed 'cause space(0) --> true */
925             if (last)
926                 last->next = (struct bnode_token *)0;
927             *alist = first;
928             return 0;
929         }
930     }
931     return 0;
932 }
933
934 #define MAXVARGS            128
935 int
936 bnode_NewProc(struct bnode *abnode, char *aexecString, char *coreName,
937               struct bnode_proc **aproc)
938 {
939     struct bnode_token *tlist, *tt;
940     afs_int32 code;
941     struct bnode_proc *tp;
942     pid_t cpid;
943     char *argv[MAXVARGS];
944     int i;
945
946     code = bnode_ParseLine(aexecString, &tlist);        /* try parsing first */
947     if (code)
948         return code;
949     tp = (struct bnode_proc *)malloc(sizeof(struct bnode_proc));
950     memset(tp, 0, sizeof(struct bnode_proc));
951     tp->next = allProcs;
952     tp->bnode = abnode;
953     tp->comLine = aexecString;
954     tp->coreName = coreName;    /* may be null */
955     abnode->procStartTime = FT_ApproxTime();
956     abnode->procStarts++;
957
958     /* convert linked list of tokens into argv structure */
959     for (tt = tlist, i = 0; i < (MAXVARGS - 1) && tt; tt = tt->next, i++) {
960         argv[i] = tt->key;
961     }
962     argv[i] = NULL;             /* null-terminated */
963
964     cpid = spawnprocve(argv[0], argv, environ, -1);
965     osi_audit(BOSSpawnProcEvent, 0, AUD_STR, aexecString, AUD_END);
966
967     if (cpid == (pid_t) - 1) {
968         bozo_Log("Failed to spawn process for bnode '%s'\n", abnode->name);
969         bnode_FreeTokens(tlist);
970         free(tp);
971         return errno;
972     }
973
974     bnode_FreeTokens(tlist);
975     allProcs = tp;
976     *aproc = tp;
977     tp->pid = cpid;
978     tp->flags = BPROC_STARTED;
979     tp->flags &= ~BPROC_EXITED;
980     bnode_Check(abnode);
981     return 0;
982 }
983
984 int
985 bnode_StopProc(register struct bnode_proc *aproc, int asignal)
986 {
987     register int code;
988     if (!(aproc->flags & BPROC_STARTED) || (aproc->flags & BPROC_EXITED))
989         return BZNOTACTIVE;
990
991     osi_audit(BOSStopProcEvent, 0, AUD_STR, (aproc ? aproc->comLine : NULL),
992               AUD_END);
993
994     code = kill(aproc->pid, asignal);
995     bnode_Check(aproc->bnode);
996     return code;
997 }
998
999 int
1000 bnode_Deactivate(register struct bnode *abnode)
1001 {
1002     register struct bnode **pb, *tb;
1003     struct bnode *nb;
1004     if (!(abnode->flags & BNODE_ACTIVE))
1005         return BZNOTACTIVE;
1006     for (pb = &allBnodes, tb = *pb; tb; tb = nb) {
1007         nb = tb->next;
1008         if (tb == abnode) {
1009             *pb = nb;
1010             tb->flags &= ~BNODE_ACTIVE;
1011             return 0;
1012         }
1013     }
1014     return BZNOENT;
1015 }