modernize-bozo-20020821
[openafs.git] / src / bozo / fsbnodeops.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 <sys/types.h>
16 #include <lwp.h>
17 #include <errno.h>
18 #include <stdio.h>
19 #ifdef  AFS_SUN5_ENV
20 #include <fcntl.h>
21 #endif
22 #ifdef AFS_NT40_ENV
23 #include <io.h>
24 #include <fcntl.h>
25 #else
26 #include <sys/file.h>
27
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34 #endif
35 #include <stdlib.h>
36
37 #endif /* AFS_NT40_ENV */
38 #include <sys/stat.h>
39 #include <afs/procmgmt.h>  /* signal(), kill(), wait(), etc. */
40 #include <afs/afsutil.h>
41 #include "bnode.h"
42
43 static int fs_timeout(), fs_getstat(), fs_setstat(), fs_delete();
44 static int fs_procexit(), fs_getstring(), fs_getparm(), fs_restartp();
45 static int fs_hascore();
46 struct bnode *fs_create();
47 struct bnode *fsmr_create();
48
49 static SetNeedsClock();
50 static NudgeProcs();
51
52 static int emergency = 0;
53
54 /* if this file exists, then we have to salvage the file system */
55 #define SALFILE     "SALVAGE."
56
57 #define POLLTIME        20          /* for handling below */
58 #define SDTIME          60          /* time in seconds given to a process to evaporate */
59
60 /*  basic rules:
61     Normal operation involves having the file server and the vol server both running.
62     
63     If the vol server terminates, it can simply be restarted.
64     
65     If the file server terminates, the disk must salvaged before the file server
66     can be restarted.  In order to restart either the file server or the salvager,
67     the vol server must be shut down.
68     
69     If the file server terminates *normally* (exits after receiving a SIGQUIT)
70     then we don't have to salvage it.
71     
72     The needsSalvage flag is set when the file server is started.  It is cleared
73     if the file server exits when fileSDW is true but fileKillSent is false,
74     indicating that it exited after receiving a quit, but before we sent it a kill.
75     
76     The needsSalvage flag is cleared when the salvager exits.
77 */
78
79 struct bnode_ops fsbnode_ops = {
80     fs_create,
81     fs_timeout,
82     fs_getstat,
83     fs_setstat,
84     fs_delete,
85     fs_procexit,
86     fs_getstring,
87     fs_getparm,
88     fs_restartp,
89     fs_hascore,
90 };
91     
92 struct fsbnode {
93     struct bnode b;
94     afs_int32 timeSDStarted;                /* time shutdown operation started */
95     char *filecmd;                  /* command to start primary file server */
96     char *volcmd;                   /* command to start secondary vol server */
97     char *salcmd;                   /* command to start salvager */
98     char *scancmd;                  /* command to start scanner (MR-AFS) */
99     struct bnode_proc *fileProc;    /* process for file server */
100     struct bnode_proc *volProc;     /* process for vol server */
101     struct bnode_proc *salProc;     /* process for salvager */
102     struct bnode_proc *scanProc;    /* process for scanner (MR-AFS) */
103     afs_int32 lastFileStart;        /* last start for file */
104     afs_int32 lastVolStart;         /* last start for vol */
105     afs_int32 lastScanStart;        /* last start for scanner (MR-AFS) */
106     char fileRunning;               /* file process is running */
107     char volRunning;                /* volser is running */
108     char salRunning;                /* salvager is running */
109     char scanRunning;               /* scanner is running (MR_AFS) */
110     char fileSDW;                   /* file shutdown wait */
111     char volSDW;                    /* vol shutdown wait */
112     char salSDW;                    /* waiting for the salvager to shutdown */
113     char scanSDW;                   /* scanner shutdown wait (MR_AFS) */
114     char fileKillSent;              /* kill signal has been sent */
115     char volKillSent;
116     char salKillSent;
117     char scanKillSent;              /* kill signal has been sent (MR_AFS) */
118     char needsSalvage;              /* salvage before running */
119     char needsClock;                /* do we need clock ticks */
120 };
121
122 /* Function to tell whether this bnode has a core file or not.  You might
123  * think that this could be in bnode.c, and decide what core files to check
124  * for based on the bnode's coreName property, but that doesn't work because
125  * there may not be an active process for a bnode that dumped core at the
126  * time the query is done.
127  */
128 static int fs_hascore(register struct ezbnode *abnode)
129 {
130     char tbuffer[256];
131
132     /* see if file server has a core file */
133     bnode_CoreName(abnode, "file", tbuffer);
134     if (access(tbuffer, 0) == 0) return 1;
135
136     /* see if volserver has a core file */
137     bnode_CoreName(abnode, "vol", tbuffer);
138     if (access(tbuffer, 0) == 0) return 1;
139
140     /* see if salvager left a core file */
141     bnode_CoreName(abnode, "salv", tbuffer);
142     if (access(tbuffer, 0) == 0) return 1;
143
144     /* see if scanner left a core file (MR-AFS) */
145     bnode_CoreName(abnode, "scan", tbuffer);
146     if (access(tbuffer, 0) == 0) return 1;
147
148     /* no one left a core file */
149     return 0;
150 }
151
152 static int fs_restartp (register struct fsbnode *abnode)
153 {
154     struct bnode_token *tt;
155     register afs_int32 code;
156     struct stat tstat;
157     
158     code = bnode_ParseLine(abnode->filecmd, &tt);
159     if (code) return 0;
160     if (!tt) return 0;
161     code = stat(tt->key, &tstat);
162     if (code) {
163         bnode_FreeTokens(tt);
164         return 0;
165     }
166     if (tstat.st_ctime > abnode->lastFileStart) code = 1;
167     else code = 0;
168     bnode_FreeTokens(tt);
169     if (code) return code;
170
171     /* now do same for volcmd */
172     code = bnode_ParseLine(abnode->volcmd, &tt);
173     if (code) return 0;
174     if (!tt) return 0;
175     code = stat(tt->key, &tstat);
176     if (code) {
177         bnode_FreeTokens(tt);
178         return 0;
179     }
180     if (tstat.st_ctime > abnode->lastVolStart) code = 1;
181     else code = 0;
182     bnode_FreeTokens(tt);
183     if (code) return code;
184
185     if (abnode->scancmd) {                     /* Only in MR-AFS */
186         /* now do same for scancmd (MR-AFS) */
187         code = bnode_ParseLine(abnode->scancmd, &tt);
188         if (code) return 0;
189         if (!tt) return 0;
190         code = stat(tt->key, &tstat);
191         if (code) {
192             bnode_FreeTokens(tt);
193             return 0;
194         }
195         if (tstat.st_ctime > abnode->lastScanStart) code = 1;
196         else code = 0;
197         bnode_FreeTokens(tt); 
198     }
199
200     return code;
201 }
202
203 /* set needsSalvage flag, creating file SALVAGE.<instancename> if
204     we need to salvage the file system (so we can tell over panic reboots */
205 static int SetSalFlag(register struct fsbnode *abnode, register int aflag)
206 {
207     char tbuffer[AFSDIR_PATH_MAX];
208     int fd;
209
210     abnode->needsSalvage = aflag;
211     strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/", SALFILE, 
212                abnode->b.name, NULL);
213     if (aflag) {
214         fd = open(tbuffer, O_CREAT | O_TRUNC | O_RDWR, 0666);
215         close(fd);
216     }
217     else {
218         unlink(tbuffer);
219     }
220     return 0;
221 }
222
223 /* set the needsSalvage flag according to the existence of the salvage file */
224 static int RestoreSalFlag(register struct fsbnode *abnode) {
225     char tbuffer[AFSDIR_PATH_MAX];
226
227     strcompose(tbuffer, AFSDIR_PATH_MAX, AFSDIR_SERVER_LOCAL_DIRPATH, "/", SALFILE, 
228                abnode->b.name, NULL);
229     if (access(tbuffer, 0) == 0) {
230         /* file exists, so need to salvage */
231         abnode->needsSalvage = 1;
232     }
233     else {
234         abnode->needsSalvage = 0;
235     }
236     return 0;
237 }
238
239 char *copystr(register char *a) 
240 {
241     register char *b;
242     b = (char *) malloc(strlen(a)+1);
243     strcpy(b, a);
244     return b;
245 }
246
247 static int fs_delete(struct fsbnode *abnode) {
248     free(abnode->filecmd);
249     free(abnode->volcmd);
250     free(abnode->salcmd);
251     if (abnode->scancmd) free(abnode->scancmd);
252     free(abnode);
253     return 0;
254 }
255
256
257 #ifdef AFS_NT40_ENV
258 static void AppendExecutableExtension(char *cmd)
259 {
260     char cmdext[_MAX_EXT];
261
262     _splitpath(cmd, NULL, NULL, NULL, cmdext);
263     if (*cmdext == '\0') {
264         /* no filename extension supplied for cmd; append .exe */
265         strcat(cmd, ".exe");
266     }
267 }
268 #endif /* AFS_NT40_ENV */
269
270
271 struct bnode *fs_create(char *ainstance, char *afilecmd, char *avolcmd, char *asalcmd, char *ascancmd)
272 {
273     struct stat tstat;
274     register struct fsbnode *te;
275     char cmdname[AFSDIR_PATH_MAX];
276     char *fileCmdpath, *volCmdpath, *salCmdpath, *scanCmdpath;
277     int bailout = 0;
278
279     fileCmdpath = volCmdpath = salCmdpath = NULL;
280
281     /* construct local paths from canonical (wire-format) paths */
282     if (ConstructLocalBinPath(afilecmd, &fileCmdpath)) {
283         bozo_Log("BNODE: command path invalid '%s'\n", afilecmd);
284         bailout = 1;
285     }
286     if (ConstructLocalBinPath(avolcmd, &volCmdpath)) {
287         bozo_Log("BNODE: command path invalid '%s'\n", avolcmd);
288         bailout = 1;
289     }
290     if (ConstructLocalBinPath(asalcmd, &salCmdpath)) {
291         bozo_Log("BNODE: command path invalid '%s'\n", asalcmd);
292         bailout = 1;
293     }
294
295     if (ascancmd && strlen(ascancmd)) {
296        if (ConstructLocalBinPath(ascancmd, &scanCmdpath)) {
297             bozo_Log("BNODE: command path invalid '%s'\n", ascancmd);
298             bailout = 1;
299        }
300     }
301
302     if (!bailout) {
303         sscanf(fileCmdpath, "%s", cmdname);
304 #ifdef AFS_NT40_ENV
305         AppendExecutableExtension(cmdname);
306 #endif
307         if (stat(cmdname, &tstat)) {
308             bozo_Log("BNODE: file server binary '%s' not found\n", cmdname);
309             bailout = 1;
310         }
311
312         sscanf(volCmdpath, "%s", cmdname);
313 #ifdef AFS_NT40_ENV
314         AppendExecutableExtension(cmdname);
315 #endif
316         if (stat(cmdname, &tstat)) {
317             bozo_Log("BNODE: volume server binary '%s' not found\n", cmdname);
318             bailout = 1;
319         }
320
321         sscanf(salCmdpath, "%s", cmdname);
322 #ifdef AFS_NT40_ENV
323         AppendExecutableExtension(cmdname);
324 #endif
325         if (stat(cmdname, &tstat)) {
326             bozo_Log("BNODE: salvager binary '%s' not found\n", cmdname);
327             bailout = 1;
328         }
329
330         if (ascancmd && strlen(ascancmd)) {
331             sscanf(scanCmdpath, "%s", cmdname);
332 #ifdef AFS_NT40_ENV
333             AppendExecutableExtension(cmdname);
334 #endif
335             if (stat(cmdname, &tstat)) {
336                 bozo_Log("BNODE: scanner binary '%s' not found\n", cmdname);
337                 bailout = 1;
338             }
339         }
340     }
341
342     if (bailout) {
343         free(fileCmdpath); free(volCmdpath); free(salCmdpath);
344         return NULL;
345     }
346
347     te = (struct fsbnode *) malloc(sizeof(struct fsbnode));
348     memset(te, 0, sizeof(struct fsbnode));
349     te->filecmd = fileCmdpath;
350     te->volcmd = volCmdpath;
351     te->salcmd = salCmdpath;
352     if (ascancmd && strlen(ascancmd))
353        te->scancmd = scanCmdpath;
354     else 
355        te->scancmd = NULL;
356     if (bnode_InitBnode(te, &fsbnode_ops, ainstance) != 0) {
357         free(te);
358         free(fileCmdpath); free(volCmdpath); free(salCmdpath);
359         return NULL;
360     }
361     bnode_SetTimeout(te, POLLTIME);     /* ask for timeout activations every 10 seconds */
362     RestoreSalFlag(te);         /* restore needsSalvage flag based on file's existence */
363     SetNeedsClock(te);          /* compute needsClock field */
364     return (struct bnode *) te;
365 }
366
367 /* called to SIGKILL a process if it doesn't terminate normally */
368 static int fs_timeout(struct fsbnode *abnode) {
369     register afs_int32 now;
370
371     now = FT_ApproxTime();
372     /* shutting down */
373     if (abnode->volSDW) {
374         if (!abnode->volKillSent && now - abnode->timeSDStarted > SDTIME) {
375             bnode_StopProc(abnode->volProc, SIGKILL);
376             abnode->volKillSent = 1;
377             bozo_Log("bos shutdown: volserver failed to shutdown within %d seconds\n",
378                      SDTIME);
379         }
380     }
381     if (abnode->salSDW) {
382         if (!abnode->salKillSent && now - abnode->timeSDStarted > SDTIME) {
383             bnode_StopProc(abnode->salProc, SIGKILL);
384             abnode->salKillSent = 1;
385             bozo_Log("bos shutdown: salvager failed to shutdown within %d seconds\n",
386                      SDTIME);
387         }
388     }
389     if (abnode->fileSDW) {
390         if (!abnode->fileKillSent && now - abnode->timeSDStarted > FSSDTIME) {
391             bnode_StopProc(abnode->fileProc, SIGKILL);
392             abnode->fileKillSent = 1;
393             bozo_Log("bos shutdown: fileserver failed to shutdown within %d seconds\n",
394                      FSSDTIME);
395         }
396     }
397     if (abnode->scanSDW) {
398         if (!abnode->scanKillSent && now - abnode->timeSDStarted > SDTIME) {
399             bnode_StopProc(abnode->scanProc, SIGKILL);
400             abnode->scanKillSent = 1;
401             bozo_Log("bos shutdown: scanner failed to shutdown within %d seconds\n",
402                      SDTIME);
403         }
404     }
405     SetNeedsClock(abnode);
406     return 0;
407 }
408
409 static int fs_getstat(struct fsbnode *abnode, afs_int32 *astatus) 
410 {
411     register afs_int32 temp;
412     if (abnode->volSDW || abnode->fileSDW || abnode->salSDW || abnode->scanSDW)
413         temp = BSTAT_SHUTTINGDOWN;
414     else if (abnode->salRunning) temp = BSTAT_NORMAL;
415     else if (abnode->volRunning && abnode->fileRunning && (!abnode->scancmd || 
416        abnode->scanRunning)) temp = BSTAT_NORMAL;
417     else if (!abnode->salRunning && !abnode->volRunning && !abnode->fileRunning
418         && !abnode->scanRunning) temp = BSTAT_SHUTDOWN;
419     else temp = BSTAT_STARTINGUP;
420     *astatus = temp;
421     return 0;
422 }
423
424 static int fs_setstat(register struct fsbnode *abnode, afs_int32 astatus)
425 {
426     return NudgeProcs(abnode);
427 }
428
429 static int fs_procexit(struct fsbnode *abnode, struct bnode_proc *aproc) 
430 {
431     /* process has exited */
432
433     if (aproc == abnode->volProc) {
434         abnode->volProc = 0;
435         abnode->volRunning = 0;
436         abnode->volSDW = 0;
437         abnode->volKillSent = 0;
438     }
439     else if (aproc == abnode->fileProc) {
440         /* if we were expecting a shutdown and we didn't send a kill signal
441          * and exited (didn't have a signal termination), then we assume that
442          * the file server exited after putting the appropriate volumes safely
443          * offline, and don't salvage next time.
444          */
445         if (abnode->fileSDW && !abnode->fileKillSent && aproc->lastSignal == 0)
446             SetSalFlag(abnode, 0);          /* shut down normally */
447         abnode->fileProc = 0;
448         abnode->fileRunning = 0;
449         abnode->fileSDW = 0;
450         abnode->fileKillSent = 0;
451     }
452     else if (aproc == abnode->salProc) {
453         /* if we didn't shutdown the salvager, then assume it exited ok, and thus
454             that we don't have to salvage again */
455         if (!abnode->salSDW)
456             SetSalFlag(abnode, 0);      /* salvage just completed */
457         abnode->salProc = 0;
458         abnode->salRunning = 0;
459         abnode->salSDW = 0;
460         abnode->salKillSent = 0;
461     }
462     else if (aproc == abnode->scanProc) {
463         abnode->scanProc = 0;
464         abnode->scanRunning = 0;
465         abnode->scanSDW = 0;
466         abnode->scanKillSent = 0;
467     }
468
469     /* now restart anyone who needs to restart */
470     return NudgeProcs(abnode);
471 }
472
473 /* make sure we're periodically checking the state if we need to */
474 static int SetNeedsClock(register struct fsbnode *ab)
475 {
476     if (ab->b.goal == 1 && ab->fileRunning && ab->volRunning
477        && (!ab->scancmd || ab->scanRunning))
478         ab->needsClock = 0; /* running normally */
479     else if (ab->b.goal == 0 && !ab->fileRunning && !ab->volRunning 
480              && !ab->salRunning && !ab->scanRunning)
481         ab->needsClock = 0; /* halted normally */
482     else ab->needsClock = 1;    /* other */
483     if (ab->needsClock && !bnode_PendingTimeout(ab))
484         bnode_SetTimeout(ab, POLLTIME);
485     if (!ab->needsClock) bnode_SetTimeout(ab, 0);
486 }
487
488 static int NudgeProcs(register struct fsbnode *abnode)
489 {
490     struct bnode_proc *tp;   /* not register */
491     register afs_int32 code;
492     afs_int32 now;
493
494     now = FT_ApproxTime();
495     if (abnode->b.goal == 1) {
496         /* we're trying to run the system. If the file server is running, then we
497             are trying to start up the system.  If it is not running, then needsSalvage
498                tells us if we need to run the salvager or not */
499         if (abnode->fileRunning) {
500             if (abnode->salRunning) {
501                 bozo_Log("Salvager running along with file server!\n");
502                 bozo_Log("Emergency shutdown\n");
503                 emergency = 1;
504                 bnode_SetGoal(abnode, BSTAT_SHUTDOWN);
505                 bnode_StopProc(abnode->salProc, SIGKILL);
506                 SetNeedsClock(abnode);
507                 return -1;
508             }
509             if (!abnode->volRunning) {
510                 abnode->lastVolStart = FT_ApproxTime();
511                 code = bnode_NewProc(abnode, abnode->volcmd, "vol", &tp);
512                 if (code == 0) {
513                     abnode->volProc = tp;
514                     abnode->volRunning = 1;
515                 }
516             }
517             if (abnode->scancmd) {
518                 if (!abnode->scanRunning) {
519                     abnode->lastScanStart = FT_ApproxTime();
520                     code = bnode_NewProc(abnode, abnode->scancmd, "scanner", &tp);
521                     if (code == 0) {
522                         abnode->scanProc = tp;
523                         abnode->scanRunning = 1;
524                     }
525                 }
526             }    
527         }
528         else {  /* file is not running */
529             /* see how to start */
530             if (!abnode->needsSalvage) {
531                 /* no crash apparent, just start up normally */
532                 if (!abnode->fileRunning) {
533                     abnode->lastFileStart = FT_ApproxTime();
534                     code = bnode_NewProc(abnode, abnode->filecmd, "file", &tp);
535                     if (code == 0) {
536                         abnode->fileProc = tp;
537                         abnode->fileRunning = 1;
538                         SetSalFlag(abnode, 1);
539                     }
540                 }
541                 if (!abnode->volRunning) {
542                     abnode->lastVolStart = FT_ApproxTime();
543                     code = bnode_NewProc(abnode, abnode->volcmd, "vol", &tp);
544                     if (code == 0) {
545                         abnode->volProc = tp;
546                         abnode->volRunning = 1;
547                     }
548                 }
549                 if (abnode->scancmd && !abnode->scanRunning) {
550                     abnode->lastScanStart = FT_ApproxTime();
551                     code = bnode_NewProc(abnode, abnode->scancmd, "scanner",
552                                          &tp);
553                     if (code == 0) {
554                         abnode->scanProc = tp;
555                         abnode->scanRunning = 1;
556                     }
557                 }
558             }
559             else {  /* needs to be salvaged */
560                 /* make sure file server and volser are gone */
561                 if (abnode->volRunning) {
562                     bnode_StopProc(abnode->volProc, SIGTERM);
563                     if (!abnode->volSDW) abnode->timeSDStarted = now;
564                     abnode->volSDW = 1;
565                 }
566                 if (abnode->fileRunning) {
567                     bnode_StopProc(abnode->fileProc, SIGQUIT);
568                     if (!abnode->fileSDW) abnode->timeSDStarted = now;
569                     abnode->fileSDW = 1;
570                 }
571                 if (abnode->scanRunning) {
572                     bnode_StopProc(abnode->scanProc, SIGTERM);
573                     if (!abnode->scanSDW) abnode->timeSDStarted = now;
574                     abnode->scanSDW = 1;
575                 }
576                 if (abnode->volRunning || abnode->fileRunning 
577                     || abnode->scanRunning) return 0;
578                 /* otherwise, it is safe to start salvager */
579                 if (!abnode->salRunning) {
580                     code = bnode_NewProc(abnode, abnode->salcmd, "salv", &tp);
581                     if (code == 0) {
582                         abnode->salProc = tp;
583                         abnode->salRunning = 1;
584                     }
585                 }
586             }
587         }
588     }
589     else {  /* goal is 0, we're shutting down */
590         /* trying to shutdown */
591         if (abnode->salRunning && !abnode->salSDW) {
592             bnode_StopProc(abnode->salProc, SIGTERM);
593             abnode->salSDW = 1;
594             abnode->timeSDStarted = now;
595         }
596         if (abnode->fileRunning && !abnode->fileSDW) {
597             bnode_StopProc(abnode->fileProc, SIGQUIT);
598             abnode->fileSDW = 1;
599             abnode->timeSDStarted = now;
600         }
601         if (abnode->volRunning && !abnode->volSDW) {
602             bnode_StopProc(abnode->volProc, SIGTERM);
603             abnode->volSDW = 1;
604             abnode->timeSDStarted = now;
605         }
606         if (abnode->scanRunning && !abnode->scanSDW) {
607             bnode_StopProc(abnode->scanProc, SIGTERM);
608             abnode->scanSDW = 1;
609             abnode->timeSDStarted = now;
610         }
611     }
612     SetNeedsClock(abnode);
613     return 0;
614 }
615
616 static int fs_getstring(struct fsbnode *abnode, char *abuffer, afs_int32 alen)
617 {
618     if (alen < 40) return -1;
619     if (abnode->b.goal == 1) {
620         if (abnode->fileRunning) {
621             if (abnode->fileSDW) strcpy(abuffer, "file server shutting down");
622             else if (abnode->scancmd) {
623                 if (!abnode->volRunning && !abnode->scanRunning)
624                     strcpy(abuffer, "file server up; volser and scanner down");
625                 else if (abnode->volRunning && !abnode->scanRunning)
626                     strcpy(abuffer, "file server up; volser up; scanner down");
627                 else if (!abnode->volRunning && abnode->scanRunning)
628                     strcpy(abuffer, "file server up; volser down; scanner up");
629
630                 else strcpy(abuffer, "file server running");
631             } 
632             else if (!abnode->volRunning) 
633                 strcpy(abuffer, "file server up; volser down");
634             else strcpy(abuffer, "file server running");
635         }
636         else if (abnode->salRunning) {
637             strcpy(abuffer, "salvaging file system");
638         }
639         else strcpy(abuffer, "starting file server");
640     }
641     else {
642         /* shutting down */
643         if (abnode->fileRunning || abnode->volRunning || abnode->scanRunning) {
644             strcpy(abuffer, "file server shutting down");
645         }
646         else if (abnode->salRunning)
647             strcpy(abuffer, "salvager shutting down");
648         else strcpy(abuffer, "file server shut down");
649     }
650     return 0;
651 }
652
653 static int fs_getparm(struct fsbnode *abnode, afs_int32 aindex, char *abuffer,
654                   afs_int32 alen)
655 {
656     if (aindex == 0)
657         strcpy(abuffer, abnode->filecmd);
658     else if (aindex == 1)
659         strcpy(abuffer, abnode->volcmd);
660     else if (aindex == 2)
661         strcpy(abuffer, abnode->salcmd);
662     else if (aindex == 3 && abnode->scancmd)
663         strcpy(abuffer, abnode->scancmd);
664     else
665         return BZDOM;
666     return 0;
667 }