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