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