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