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