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