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