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