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