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