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