c8bb725fe987fb05d82c4637f06fd8b3201d8af5
[openafs.git] / src / bucoord / main.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16
17 #include <afs/cmd.h>
18 #include <rx/rx.h>
19 #include <rx/rx_globals.h>
20 #include <lwp.h>
21 #include <afs/bubasics.h>
22 #include <afs/afsutil.h>
23 #include <afs/auth.h>
24 #include <afs/cellconfig.h>
25 #include <afs/keys.h>
26 #include <ubik.h>
27 #include <afs/cmd.h>
28 #include <rx/rxkad.h>
29 #include <afs/volser.h>         /*VLDB_MAXSERVERS */
30 #include <afs/com_err.h>
31 #include <lock.h>
32 #include <afs/budb.h>
33 #include <afs/kautils.h>
34 #include <afs/vlserver.h>
35 #include <afs/butm.h>
36 #include <afs/butx.h>
37 #include <afs/tcdata.h>
38
39 #include "bc.h"                 /*Backup Coordinator structs and defs */
40 #include "bucoord_internal.h"
41 #include "bucoord_prototypes.h"
42
43 int localauth, interact;
44 char tcell[64];
45
46 /*
47  * Global configuration information for the Backup Coordinator.
48  */
49 struct bc_config *bc_globalConfig;      /*Ptr to global BC configuration info */
50
51 struct ubik_client *cstruct;    /* Ptr to Ubik client structure */
52 time_t tokenExpires;            /* The token's expiration time */
53
54 static const char *DefaultConfDir;      /*Default backup config directory */
55 static int bcInit = 0;          /* backupInit called yet ? */
56 char *whoami = "backup";
57
58 /* dummy routine for the audit work.  It should do nothing since audits */
59 /* occur at the server level and bos is not a server. */
60 int
61 osi_audit(void)
62 {
63     return 0;
64 }
65
66 /*
67  * Initialize all the error tables that may be used by com_err
68  * in this module.
69  */
70 void
71 InitErrTabs(void)
72 {
73     initialize_ACFG_error_table();
74     initialize_KA_error_table();
75     initialize_RXK_error_table();
76     initialize_CMD_error_table();
77     initialize_VL_error_table();
78     initialize_BUTM_error_table();
79     initialize_VOLS_error_table();
80     initialize_BUTC_error_table();
81     initialize_BUTX_error_table();
82     initialize_BUDB_error_table();
83     initialize_BUCD_error_table();
84     initialize_KTC_error_table();
85 }
86
87 /*
88  * got to account for the errors which are volume related but
89  * not dealt with by standard errno and com_err stuff.
90  */
91 void
92 bc_HandleMisc(afs_int32 code)
93 {
94     if (((code <= VMOVED) && (code >= VSALVAGE)) || (code < 0)) {
95         switch (code) {
96         case -1:
97             fprintf(STDERR, "Possible communication failure\n");
98             break;
99         case VSALVAGE:
100             fprintf(STDERR, "Volume needs salvage\n");
101             break;
102         case VNOVNODE:
103             fprintf(STDERR, "Bad vnode number quoted\n");
104             break;
105         case VNOVOL:
106             fprintf(STDERR,
107                     "Volume not attached, does not exist, or not on line\n");
108             break;
109         case VVOLEXISTS:
110             fprintf(STDERR, "Volume already exists\n");
111             break;
112         case VNOSERVICE:
113             fprintf(STDERR, "Volume is not in service\n");
114             break;
115         case VOFFLINE:
116             fprintf(STDERR, "Volume is off line\n");
117             break;
118         case VONLINE:
119             fprintf(STDERR, "Volume is already on line\n");
120             break;
121         case VDISKFULL:
122             fprintf(STDERR, "Partition is full\n");
123             break;
124         case VOVERQUOTA:
125             fprintf(STDERR, "Volume max quota exceeded\n");
126             break;
127         case VBUSY:
128             fprintf(STDERR, "Volume temporarily unavailable\n");
129             break;
130         case VMOVED:
131             fprintf(STDERR, "Volume has moved to another server\n");
132             break;
133         default:
134             break;
135         }
136     }
137     return;
138 }
139
140 /* Return true if line is all whitespace */
141 static int
142 LineIsBlank(char *aline)
143 {
144     int tc;
145
146     while ((tc = *aline++))
147         if ((tc != ' ') && (tc != '\t') && (tc != '\n'))
148             return (0);
149
150     return (1);
151 }
152
153
154 /* bc_InitTextConfig
155  *      initialize configuration information that is stored as text blocks
156  */
157
158 afs_int32
159 bc_InitTextConfig(void)
160 {
161     udbClientTextP ctPtr;
162     int i;
163
164     extern struct bc_config *bc_globalConfig;
165
166     mkdir(DefaultConfDir, 777); /* temporary */
167
168     /* initialize the client text structures */
169     ctPtr = &bc_globalConfig->configText[0];
170
171     for (i = 0; i < TB_NUM; i++) {
172         memset(ctPtr, 0, sizeof(*ctPtr));
173         ctPtr->textType = i;
174         ctPtr->textVersion = -1;
175         ctPtr++;
176     }
177
178     return (0);
179 }
180
181 /*----------------------------------------------------------------------------
182  * backupInit
183  *
184  * Description:
185  *      Routine that is called when the backup coordinator is invoked, responsible for
186  *      basic initialization and some command line parsing.
187  *
188  * Returns:
189  *      Zero (but may exit the entire program on error!)
190  *
191  * Environment:
192  *      Nothing interesting.
193  *
194  * Side Effects:
195  *      Initializes this program.
196  *----------------------------------------------------------------------------
197  */
198
199 static int
200 backupInit(void)
201 {
202     afs_int32 code;
203     static int initd = 0;       /* ever called? */
204     PROCESS watcherPid;
205     PROCESS pid;                /* LWP process ID */
206
207     /* Initialization */
208     initialize_CMD_error_table();
209
210     /* don't run more than once */
211     if (initd) {
212         afs_com_err(whoami, 0, "Backup already initialized.");
213         return 0;
214     }
215     initd = 1;
216
217     code = bc_InitConfig((char *)DefaultConfDir);
218     if (code) {
219         afs_com_err(whoami, code,
220                 "Can't initialize from config files in directory '%s'",
221                 DefaultConfDir);
222         return (code);
223     }
224
225     /*
226      * Set up Rx.
227      */
228     code = LWP_InitializeProcessSupport(LWP_NORMAL_PRIORITY, &pid);
229     if (code) {
230         afs_com_err(whoami, code, "; Can't initialize LWP");
231         return (code);
232     }
233
234     code = rx_Init(htons(0));
235     if (code) {
236         afs_com_err(whoami, code, "; Can't initialize Rx");
237         return (code);
238     }
239
240     rx_SetRxDeadTime(60);
241
242     /* VLDB initialization */
243     code = vldbClientInit(0, localauth, tcell, &cstruct, &tokenExpires);
244     if (code)
245         return (code);
246
247     /* Backup database initialization */
248     code = udbClientInit(0, localauth, tcell);
249     if (code)
250         return (code);
251
252     /* setup status monitoring thread */
253     initStatus();
254     code =
255         LWP_CreateProcess(statusWatcher, 20480, LWP_NORMAL_PRIORITY,
256                           (void *)2, "statusWatcher", &watcherPid);
257     if (code) {
258         afs_com_err(whoami, code, "; Can't create status monitor task");
259         return (code);
260     }
261
262     return (0);
263 }
264
265 /*----------------------------------------------------------------------------
266  * MyBeforeProc
267  *
268  * Description:
269  *      Make sure we mark down when we need to exit the Backup Coordinator.
270  *      Specifically, we don't want to continue when our help and apropos
271  *      routines are called from the command line.
272  *
273  * Arguments:
274  *      as : Ptr to the command syntax descriptor.
275  *
276  * Returns:
277  *      0.
278  *
279  * Environment:
280  *      This routine is called right before each of the Backup Coordinator
281  *      opcode routines are invoked.
282  *
283  *----------------------------------------------------------------------------
284  */
285
286 static int
287 MyBeforeProc(struct cmd_syndesc *as, void *arock)
288 {
289     afs_int32 code;
290
291     /* Handling the command line opcode */
292     if (!bcInit) {
293         localauth = ((as && as->parms[14].items) ? 1 : 0);
294         if (as && as->parms[15].items)
295             strcpy(tcell, as->parms[15].items->data);
296         else
297             tcell[0] = '\0';
298
299         code = backupInit();
300         if (code) {
301             afs_com_err(whoami, code, "; Can't initialize backup");
302             exit(1);
303         }
304
305         /* Get initial information from the database */
306         code = bc_InitTextConfig();
307         if (code) {
308             afs_com_err(whoami, code,
309                     "; Can't obtain configuration text from backup database");
310             exit(1);
311         }
312     }
313     bcInit = 1;
314
315     return 0;
316 }
317
318
319 #define MAXV    100
320
321 #include "AFS_component_version_number.c"
322
323 #define MAXRECURSION 20
324 extern int dontExecute;         /* declared in commands.c */
325 extern char *loadFile;          /* declared in commands.c */
326 char lineBuffer[1024];          /* Line typed in by user or read from load file */
327
328 /*
329  * This will dispatch a command.  It holds a recursive loop for the
330  * "dump -file" option. This option reads backup commands from a file.
331  *
332  * Cannot put this code on other side of cmd_Dispatch call (in
333  * commands.c) because when make a dispatch call when in a dispatch
334  * call, environment is mucked up.
335  *
336  * To avoid multiple processes stepping on each other in the dispatch code,
337  * put a lock around it so only 1 process gets in at a time.
338  */
339
340 struct Lock dispatchLock;       /* lock on the Dispatch call */
341 #define lock_Dispatch()     ObtainWriteLock(&dispatchLock)
342 #define unlock_Dispatch()   ReleaseWriteLock(&dispatchLock)
343
344 afs_int32
345 doDispatch(afs_int32 targc,
346            char *targv[MAXV],
347            afs_int32 dispatchCount) /* to prevent infinite recursion */
348 {
349     char *sargv[MAXV];
350     afs_int32 sargc;
351     afs_int32 code, c;
352     FILE *fd;
353     int i;
354     int lineNumber;
355     int noExecute;              /* local capy of global variable */
356     char *internalLoadFile;
357
358     lock_Dispatch();
359
360     loadFile = NULL;
361     code = cmd_Dispatch(targc, targv);
362     internalLoadFile = loadFile;
363
364     unlock_Dispatch();
365
366     if (internalLoadFile) {     /* Load a file in */
367         if (dispatchCount > MAXRECURSION) {     /* Beware recursive loops. */
368             afs_com_err(whoami, 0, "Potential recursion: will not load file %s",
369                     internalLoadFile);
370             code = -1;
371             goto done;
372         }
373
374         fd = fopen(internalLoadFile, "r");      /* Open the load file */
375         if (!fd) {
376             afs_com_err(whoami, errno, "; Cannot open file %s", internalLoadFile);
377             code = -1;
378             goto done;
379         }
380
381         noExecute = dontExecute;
382         if (noExecute)
383             printf("Would have executed the following commands:\n");
384
385         lineNumber = 0;
386         while (fgets(lineBuffer, sizeof(lineBuffer) - 1, fd)) { /* Get commands from file */
387             lineNumber++;
388
389             i = strlen(lineBuffer) - 1;
390             if (lineBuffer[i] == '\n')  /* Drop return at end of line */
391                 lineBuffer[i] = '\0';
392
393             if (noExecute)
394                 printf("        %s\n", lineBuffer);     /* echo */
395             else
396                 printf("------> %s\n", lineBuffer);     /* echo */
397
398             if (!LineIsBlank(lineBuffer) &&     /* Skip if blank line */
399                 (lineBuffer[0] != '#') &&       /*      or comment    */
400                 (!noExecute)) { /*      or no execute */
401                 c = cmd_ParseLine(lineBuffer, sargv, &sargc, MAXV);
402                 if (c) {
403                     afs_com_err(whoami, c, "; Can't parse line");
404                 } else {
405                     doDispatch(sargc, sargv, dispatchCount + 1);        /* Recursive - ignore error */
406                     cmd_FreeArgv(sargv);        /* Free up arguments */
407                 }
408             }
409         }
410
411         fclose(fd);
412     }
413
414   done:
415     if (internalLoadFile)
416         free(internalLoadFile);
417     return (code);
418 }
419
420 int
421 bc_interactCmd(struct cmd_syndesc *as, void *arock)
422 {
423     interact = 1;
424     return 0;
425 }
426
427 static void
428 add_std_args(struct cmd_syndesc *ts)
429 {
430     cmd_Seek(ts, 14);
431     cmd_AddParm(ts, "-localauth", CMD_FLAG, CMD_OPTIONAL,
432                 "local authentication");
433     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
434 }
435
436 int
437 main(int argc, char **argv)
438 {                               /*main */
439     char *targv[MAXV];          /*Ptr to parsed argv stuff */
440     afs_int32 targc;            /*Num parsed arguments */
441     afs_int32 code;             /*Return code */
442     struct cmd_syndesc *ts;     /*Ptr to parsed command line */
443     int i;
444
445
446 #ifdef  AFS_AIX32_ENV
447     /*
448      * The following signal action for AIX is necessary so that in case of a
449      * crash (i.e. core is generated) we can include the user's data section
450      * in the core dump. Unfortunately, by default, only a partial core is
451      * generated which, in many cases, isn't too useful.
452      */
453     struct sigaction nsa;
454
455     sigemptyset(&nsa.sa_mask);
456     nsa.sa_handler = SIG_DFL;
457     nsa.sa_flags = SA_FULLDUMP;
458     sigaction(SIGSEGV, &nsa, NULL);
459 #endif
460     Lock_Init(&dispatchLock);
461     InitErrTabs();              /* init all the error tables which may be used */
462
463     /* setup the default backup dir */
464     DefaultConfDir = AFSDIR_SERVER_BACKUP_DIRPATH;
465     /* Get early warning if the command is interacive mode or not */
466     interact = (((argc < 2) || (argv[1][0] == '-')) ? 1 : 0);
467
468     cmd_SetBeforeProc(MyBeforeProc, NULL);
469
470     ts = cmd_CreateSyntax("dump", bc_DumpCmd, NULL, "start dump");
471     cmd_AddParm(ts, "-volumeset", CMD_SINGLE, CMD_OPTIONAL,
472                 "volume set name");
473     cmd_AddParm(ts, "-dump", CMD_SINGLE, CMD_OPTIONAL, "dump level name");
474     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
475                 "TC port offset");
476     cmd_AddParm(ts, "-at", CMD_LIST, CMD_OPTIONAL, "Date/time to start dump");
477     cmd_AddParm(ts, "-append", CMD_FLAG, CMD_OPTIONAL,
478                 "append to existing dump set");
479     cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL,
480                 "list what would be done, don't do it");
481     cmd_AddParmAlias(ts, 5, "-n");
482     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "load file");
483     if (!interact)
484         add_std_args(ts);
485
486     ts = cmd_CreateSyntax("volrestore", bc_VolRestoreCmd, NULL,
487                           "restore volume");
488     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED,
489                 "destination machine");
490     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED,
491                 "destination partition");
492     cmd_AddParm(ts, "-volume", CMD_LIST, CMD_REQUIRED,
493                 "volume(s) to restore");
494     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
495                 "new volume name extension");
496     cmd_AddParm(ts, "-date", CMD_LIST, CMD_OPTIONAL,
497                 "date from which to restore");
498     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offsets");
499     cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL,
500                 "list what would be done, don't do it");
501     cmd_AddParmAlias(ts, 6, "-n");
502     cmd_AddParm(ts, "-usedump", CMD_SINGLE, CMD_OPTIONAL,
503                 "specify the dumpID to restore from");
504     if (!interact)
505         add_std_args(ts);
506
507     ts = cmd_CreateSyntax("diskrestore", bc_DiskRestoreCmd, NULL,
508                           "restore partition");
509     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED,
510                 "machine to restore");
511     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED,
512                 "partition to restore");
513     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offset");
514     cmd_Seek(ts, 8);
515     cmd_AddParm(ts, "-newserver", CMD_SINGLE, CMD_OPTIONAL,
516                 "destination machine");
517     cmd_AddParm(ts, "-newpartition", CMD_SINGLE, CMD_OPTIONAL,
518                 "destination partition");
519     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
520                 "new volume name extension");
521     cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL,
522                 "list what would be done, don't do it");
523     cmd_AddParmAlias(ts, 11, "-n");
524     if (!interact)
525         add_std_args(ts);
526
527     ts = cmd_CreateSyntax("quit", bc_QuitCmd, NULL, "leave the program");
528
529     ts = cmd_CreateSyntax("volsetrestore", bc_VolsetRestoreCmd, NULL,
530                           "restore a set of volumes");
531     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume set name");
532     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "file name");
533     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offset");
534     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
535                 "new volume name extension");
536     cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL,
537                 "list what would be done, don't do it");
538     cmd_AddParmAlias(ts, 4, "-n");
539     if (!interact)
540         add_std_args(ts);
541
542     ts = cmd_CreateSyntax("addhost", bc_AddHostCmd, NULL, "add host to config");
543     cmd_AddParm(ts, "-tapehost", CMD_SINGLE, CMD_REQUIRED,
544                 "tape machine name");
545     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
546                 "TC port offset");
547     if (!interact)
548         add_std_args(ts);
549
550     ts = cmd_CreateSyntax("delhost", bc_DeleteHostCmd, NULL,
551                           "delete host to config");
552     cmd_AddParm(ts, "-tapehost", CMD_SINGLE, CMD_REQUIRED,
553                 "tape machine name");
554     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
555                 "TC port offset");
556     if (!interact)
557         add_std_args(ts);
558
559     ts = cmd_CreateSyntax("listhosts", bc_ListHostsCmd, NULL,
560                           "list config hosts");
561     if (!interact)
562         add_std_args(ts);
563
564     ts = cmd_CreateSyntax("jobs", bc_JobsCmd, NULL, "list running jobs");
565
566     ts = cmd_CreateSyntax("kill", bc_KillCmd, NULL, "kill running job");
567     cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_REQUIRED,
568                 "job ID or dump set name");
569
570     ts = cmd_CreateSyntax("listvolsets", bc_ListVolSetCmd, NULL,
571                           "list volume sets");
572     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume set name");
573     if (!interact)
574         add_std_args(ts);
575
576     ts = cmd_CreateSyntax("listdumps", bc_ListDumpScheduleCmd, NULL,
577                           "list dump schedules");
578     if (!interact)
579         add_std_args(ts);
580
581     ts = cmd_CreateSyntax("addvolset", bc_AddVolSetCmd, NULL,
582                           "create a new volume set");
583     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
584     cmd_AddParm(ts, "-temporary", CMD_FLAG, CMD_OPTIONAL,
585                 "temporary volume set");
586     if (!interact)
587         add_std_args(ts);
588
589     ts = cmd_CreateSyntax("status", bc_GetTapeStatusCmd, NULL,
590                           "get tape coordinator status");
591     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
592                 "TC port offset");
593     if (!interact)
594         add_std_args(ts);
595
596     ts = cmd_CreateSyntax("delvolset", bc_DeleteVolSetCmd, NULL,
597                           "delete a volume set");
598     cmd_AddParm(ts, "-name", CMD_LIST, CMD_REQUIRED, "volume set name");
599     if (!interact)
600         add_std_args(ts);
601
602     ts = cmd_CreateSyntax("addvolentry", bc_AddVolEntryCmd, NULL,
603                           "add a new volume entry");
604     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
605     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED, "machine name");
606     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED, "partition name");
607     cmd_AddParm(ts, "-volumes", CMD_SINGLE, CMD_REQUIRED,
608                 "volume name (regular expression)");
609     if (!interact)
610         add_std_args(ts);
611
612     ts = cmd_CreateSyntax("delvolentry", bc_DeleteVolEntryCmd, NULL,
613                           "delete a volume set sub-entry");
614     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
615     cmd_AddParm(ts, "-entry", CMD_SINGLE, CMD_REQUIRED, "volume set index");
616     if (!interact)
617         add_std_args(ts);
618
619     ts = cmd_CreateSyntax("adddump", bc_AddDumpCmd, NULL, "add dump schedule");
620     cmd_AddParm(ts, "-dump", CMD_LIST, CMD_REQUIRED, "dump level name");
621     cmd_AddParm(ts, "-expires", CMD_LIST, CMD_OPTIONAL, "expiration date");
622     if (!interact)
623         add_std_args(ts);
624
625     ts = cmd_CreateSyntax("deldump", bc_DeleteDumpCmd, NULL,
626                           "delete dump schedule");
627     cmd_AddParm(ts, "-dump", CMD_SINGLE, CMD_REQUIRED, "dump level name");
628     if (!interact)
629         add_std_args(ts);
630
631     ts = cmd_CreateSyntax("labeltape", bc_LabelTapeCmd, NULL, "label a tape");
632     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL,
633                 "AFS tape name, defaults to NULL");
634     cmd_AddParm(ts, "-size", CMD_SINGLE, CMD_OPTIONAL,
635                 "tape size in Kbytes, defaults to size in tapeconfig");
636     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
637                 "TC port offset");
638     cmd_AddParm(ts, "-pname", CMD_SINGLE, CMD_OPTIONAL,
639                 "permanent tape name");
640     if (!interact)
641         add_std_args(ts);
642
643     ts = cmd_CreateSyntax("readlabel", bc_ReadLabelCmd, NULL,
644                           "read the label on tape");
645     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
646                 "TC port offset");
647     if (!interact)
648         add_std_args(ts);
649
650     ts = cmd_CreateSyntax("scantape", bc_ScanDumpsCmd, NULL,
651                           "dump information recovery from tape");
652     cmd_AddParm(ts, "-dbadd", CMD_FLAG, CMD_OPTIONAL,
653                 "add information to the database");
654     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
655                 "TC port offset");
656     if (!interact)
657         add_std_args(ts);
658
659     ts = cmd_CreateSyntax("volinfo", bc_dblookupCmd, NULL,
660                           "query the backup database");
661     cmd_AddParm(ts, "-volume", CMD_SINGLE, CMD_REQUIRED, "volume name");
662     if (!interact)
663         add_std_args(ts);
664
665     ts = cmd_CreateSyntax("setexp", bc_SetExpCmd, NULL,
666                           "set/clear dump expiration dates");
667     cmd_AddParm(ts, "-dump", CMD_LIST, CMD_REQUIRED, "dump level name");
668     cmd_AddParm(ts, "-expires", CMD_LIST, CMD_OPTIONAL, "expiration date");
669     if (!interact)
670         add_std_args(ts);
671
672     ts = cmd_CreateSyntax("savedb", bc_saveDbCmd, NULL, "save backup database");
673     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
674                 "TC port offset");
675     cmd_AddParm(ts, "-archive", CMD_LIST, CMD_OPTIONAL, "date time");
676     if (!interact)
677         add_std_args(ts);
678
679     ts = cmd_CreateSyntax("restoredb", bc_restoreDbCmd, NULL,
680                           "restore backup database");
681     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
682                 "TC port offset");
683     if (!interact)
684         add_std_args(ts);
685
686     ts = cmd_CreateSyntax("dumpinfo", bc_dumpInfoCmd, NULL,
687                           "provide information about a dump in the database");
688     cmd_AddParm(ts, "-ndumps", CMD_SINGLE, CMD_OPTIONAL, "no. of dumps");
689     cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "dump id");
690     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL,
691                 "detailed description");
692     if (!interact)
693         add_std_args(ts);
694
695     ts = cmd_CreateSyntax("dbverify", bc_dbVerifyCmd, NULL,
696                           "check ubik database integrity");
697     cmd_AddParm(ts, "-detail", CMD_FLAG, CMD_OPTIONAL, "additional details");
698     if (!interact)
699         add_std_args(ts);
700
701     ts = cmd_CreateSyntax("deletedump", bc_deleteDumpCmd, NULL,
702                           "delete dumps from the database");
703     cmd_AddParm(ts, "-dumpid", CMD_LIST, CMD_OPTIONAL, "dump id");
704     cmd_AddParm(ts, "-from", CMD_LIST, CMD_OPTIONAL, "date time");
705     cmd_AddParm(ts, "-to", CMD_LIST, CMD_OPTIONAL, "date time");
706     cmd_AddParm(ts, "-port", CMD_SINGLE, CMD_OPTIONAL, "TC port offset");
707     cmd_AddParm(ts, "-groupid", CMD_SINGLE, CMD_OPTIONAL, "group ID");
708     cmd_AddParm(ts, "-dbonly", CMD_FLAG, CMD_OPTIONAL,
709                 "delete the dump from the backup database only");
710     cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
711                 "always delete from backup database");
712     cmd_AddParm(ts, "-noexecute", CMD_FLAG, CMD_OPTIONAL|CMD_HIDDEN, "");
713     cmd_AddParm(ts, "-dryrun", CMD_FLAG, CMD_OPTIONAL,
714                 "list the dumps, don't delete anything");
715     cmd_AddParmAlias(ts, 8, "-n");
716
717     if (!interact)
718         add_std_args(ts);
719
720     ts = cmd_CreateSyntax("interactive", bc_interactCmd, NULL,
721                           "enter interactive mode");
722     add_std_args(ts);
723
724     /*
725      * Now execute the command.
726      */
727
728     targc = 0;
729     targv[targc++] = argv[0];
730     if (interact)
731         targv[targc++] = "interactive";
732     for (i = 1; i < argc; i++)
733         targv[targc++] = argv[i];
734
735     code = doDispatch(targc, targv, 1);
736
737     if (!interact || !bcInit) { /* Non-interactive mode */
738         if (code)
739             exit(-1);
740         if (bcInit)
741             code = bc_WaitForNoJobs();  /* wait for any jobs to finish */
742         exit(code);             /* and exit */
743     }
744
745     /* Iterate on command lines, interpreting user commands (interactive mode) */
746     while (1) {
747         int ret;
748
749         printf("backup> ");
750         fflush(stdout);
751
752
753         while ((ret = LWP_GetLine(lineBuffer, sizeof(lineBuffer))) == 0)
754             printf("%s: Command line too long\n", whoami);      /* line too long */
755
756         if (ret == -1)
757             return 0;           /* Got EOF */
758
759         if (!LineIsBlank(lineBuffer)) {
760             code = cmd_ParseLine(lineBuffer, targv, &targc, MAXV);
761             if (code)
762                 afs_com_err(whoami, code, "; Can't parse line: '%s'",
763                         afs_error_message(code));
764             else {
765                 doDispatch(targc, targv, 1);
766                 cmd_FreeArgv(targv);
767             }
768         }
769     }
770 }                               /*main */