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