1da1b109c860fd25dd013696fefb5e6b422adaf3
[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     code = 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     afs_int32 code;
337
338     /* Handling the command line opcode */
339     if (!bcInit) {
340         localauth = ((as && as->parms[14].items) ? 1 : 0);
341         if (as && as->parms[15].items)
342             strcpy(tcell, as->parms[15].items->data);
343         else
344             tcell[0] = '\0';
345
346         code = backupInit();
347         if (code) {
348             com_err(whoami, code, "; Can't initialize backup");
349             exit(1);
350         }
351
352         /* Get initial information from the database */
353         code = bc_InitTextConfig();
354         if (code) {
355             com_err(whoami, code,
356                     "; Can't obtain configuration text from backup database");
357             exit(1);
358         }
359     }
360     bcInit = 1;
361
362     return 0;
363 }
364
365
366 #define MAXV    100
367
368 #include "AFS_component_version_number.c"
369
370 #define MAXRECURSION 20
371 extern int dontExecute;         /* declared in commands.c */
372 extern char *loadFile;          /* declared in commands.c */
373 char lineBuffer[1024];          /* Line typed in by user or read from load file */
374
375 /* 
376  * This will dispatch a command.  It holds a recursive loop for the
377  * "dump -file" option. This option reads backup commands from a file.
378  *
379  * Cannot put this code on other side of cmd_Dispatch call (in
380  * commands.c) because when make a dispatch call when in a dispatch
381  * call, environment is mucked up.
382  * 
383  * To avoid multiple processes stepping on each other in the dispatch code,
384  * put a lock around it so only 1 process gets in at a time.
385  */
386
387 struct Lock dispatchLock;       /* lock on the Dispatch call */
388 #define lock_Dispatch()     ObtainWriteLock(&dispatchLock)
389 #define unlock_Dispatch()   ReleaseWriteLock(&dispatchLock)
390
391 afs_int32
392 doDispatch(targc, targv, dispatchCount)
393      char *targv[MAXV];
394      afs_int32 targc;
395      afs_int32 dispatchCount;   /* to prevent infinite recursion */
396 {
397     char *sargv[MAXV];
398     afs_int32 sargc;
399     afs_int32 code, c;
400     FILE *fd;
401     int i;
402     int lineNumber;
403     int noExecute;              /* local capy of global variable */
404     char *internalLoadFile;
405
406     lock_Dispatch();
407
408     loadFile = NULL;
409     code = cmd_Dispatch(targc, targv);
410     internalLoadFile = loadFile;
411
412     unlock_Dispatch();
413
414     if (internalLoadFile) {     /* Load a file in */
415         if (dispatchCount > MAXRECURSION) {     /* Beware recursive loops. */
416             com_err(whoami, 0, "Potential recursion: will not load file %s",
417                     internalLoadFile);
418             code = -1;
419             goto done;
420         }
421
422         fd = fopen(internalLoadFile, "r");      /* Open the load file */
423         if (!fd) {
424             com_err(whoami, errno, "; Cannot open file %s", internalLoadFile);
425             code = -1;
426             goto done;
427         }
428
429         noExecute = dontExecute;
430         if (noExecute)
431             printf("Would have executed the following commands:\n");
432
433         lineNumber = 0;
434         while (fgets(lineBuffer, sizeof(lineBuffer) - 1, fd)) { /* Get commands from file */
435             lineNumber++;
436
437             i = strlen(lineBuffer) - 1;
438             if (lineBuffer[i] == '\n')  /* Drop return at end of line */
439                 lineBuffer[i] = '\0';
440
441             if (noExecute)
442                 printf("        %s\n", lineBuffer);     /* echo */
443             else
444                 printf("------> %s\n", lineBuffer);     /* echo */
445
446             if (!LineIsBlank(lineBuffer) &&     /* Skip if blank line */
447                 (lineBuffer[0] != '#') &&       /*      or comment    */
448                 (!noExecute)) { /*      or no execute */
449                 c = cmd_ParseLine(lineBuffer, sargv, &sargc, MAXV);
450                 if (c) {
451                     com_err(whoami, c, "; Can't parse line");
452                 } else {
453                     doDispatch(sargc, sargv, dispatchCount + 1);        /* Recursive - ignore error */
454                     cmd_FreeArgv(sargv);        /* Free up arguments */
455                 }
456             }
457         }
458
459         fclose(fd);
460     }
461
462   done:
463     if (internalLoadFile)
464         free(internalLoadFile);
465     return (code);
466 }
467
468 int
469 bc_interactCmd(as, arock)
470      struct cmd_syndesc *as;
471      char *arock;
472 {
473     interact = 1;
474 }
475
476 static void
477 add_std_args(ts)
478      register struct cmd_syndesc *ts;
479 {
480     cmd_Seek(ts, 14);
481     cmd_AddParm(ts, "-localauth", CMD_FLAG, CMD_OPTIONAL,
482                 "local authentication");
483     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
484 }
485
486
487 main(argc, argv)
488      int argc;
489      char **argv;
490 {                               /*main */
491     char *targv[MAXV];          /*Ptr to parsed argv stuff */
492     afs_int32 targc;            /*Num parsed arguments */
493     afs_int32 code;             /*Return code */
494     register struct cmd_syndesc *ts;    /*Ptr to parsed command line */
495     int i;
496
497
498 #ifdef  AFS_AIX32_ENV
499     /*
500      * The following signal action for AIX is necessary so that in case of a 
501      * crash (i.e. core is generated) we can include the user's data section 
502      * in the core dump. Unfortunately, by default, only a partial core is
503      * generated which, in many cases, isn't too useful.
504      */
505     struct sigaction nsa;
506
507     sigemptyset(&nsa.sa_mask);
508     nsa.sa_handler = SIG_DFL;
509     nsa.sa_flags = SA_FULLDUMP;
510     sigaction(SIGSEGV, &nsa, NULL);
511 #endif
512     Lock_Init(&dispatchLock);
513     InitErrTabs();              /* init all the error tables which may be used */
514
515     /* setup the default backup dir */
516     DefaultConfDir = AFSDIR_SERVER_BACKUP_DIRPATH;
517     /* Get early warning if the command is interacive mode or not */
518     interact = (((argc < 2) || (argv[1][0] == '-')) ? 1 : 0);
519
520     cmd_SetBeforeProc(MyBeforeProc, NULL);
521
522     ts = cmd_CreateSyntax("dump", bc_DumpCmd, 0, "start dump");
523     cmd_AddParm(ts, "-volumeset", CMD_SINGLE, CMD_OPTIONAL,
524                 "volume set name");
525     cmd_AddParm(ts, "-dump", CMD_SINGLE, CMD_OPTIONAL, "dump level name");
526     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
527                 "TC port offset");
528     cmd_AddParm(ts, "-at", CMD_LIST, CMD_OPTIONAL, "Date/time to start dump");
529     cmd_AddParm(ts, "-append", CMD_FLAG, CMD_OPTIONAL,
530                 "append to existing dump set");
531     cmd_AddParm(ts, "-n", CMD_FLAG, CMD_OPTIONAL, "don't really execute it");
532     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "load file");
533     if (!interact)
534         add_std_args(ts);
535
536     ts = cmd_CreateSyntax("volrestore", bc_VolRestoreCmd, 0,
537                           "restore volume");
538     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED,
539                 "destination machine");
540     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED,
541                 "destination partition");
542     cmd_AddParm(ts, "-volume", CMD_LIST, CMD_REQUIRED,
543                 "volume(s) to restore");
544     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
545                 "new volume name extension");
546     cmd_AddParm(ts, "-date", CMD_LIST, CMD_OPTIONAL,
547                 "date from which to restore");
548     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offsets");
549     cmd_AddParm(ts, "-n", CMD_FLAG, CMD_OPTIONAL, "don't really execute it");
550     if (!interact)
551         add_std_args(ts);
552
553     ts = cmd_CreateSyntax("diskrestore", bc_DiskRestoreCmd, 0,
554                           "restore partition");
555     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED,
556                 "machine to restore");
557     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED,
558                 "partition to restore");
559     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offset");
560     cmd_Seek(ts, 8);
561     cmd_AddParm(ts, "-newserver", CMD_SINGLE, CMD_OPTIONAL,
562                 "destination machine");
563     cmd_AddParm(ts, "-newpartition", CMD_SINGLE, CMD_OPTIONAL,
564                 "destination partition");
565     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
566                 "new volume name extension");
567     cmd_AddParm(ts, "-n", CMD_FLAG, CMD_OPTIONAL, "don't really execute it");
568     if (!interact)
569         add_std_args(ts);
570
571     ts = cmd_CreateSyntax("quit", bc_QuitCmd, 0, "leave the program");
572
573     ts = cmd_CreateSyntax("volsetrestore", bc_VolsetRestoreCmd, 0,
574                           "restore a set of volumes");
575     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume set name");
576     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL, "file name");
577     cmd_AddParm(ts, "-portoffset", CMD_LIST, CMD_OPTIONAL, "TC port offset");
578     cmd_AddParm(ts, "-extension", CMD_SINGLE, CMD_OPTIONAL,
579                 "new volume name extension");
580     cmd_AddParm(ts, "-n", CMD_FLAG, CMD_OPTIONAL, "don't really execute it");
581     if (!interact)
582         add_std_args(ts);
583
584     ts = cmd_CreateSyntax("addhost", bc_AddHostCmd, 0, "add host to config");
585     cmd_AddParm(ts, "-tapehost", CMD_SINGLE, CMD_REQUIRED,
586                 "tape machine name");
587     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
588                 "TC port offset");
589     if (!interact)
590         add_std_args(ts);
591
592     ts = cmd_CreateSyntax("delhost", bc_DeleteHostCmd, 0,
593                           "delete host to config");
594     cmd_AddParm(ts, "-tapehost", CMD_SINGLE, CMD_REQUIRED,
595                 "tape machine name");
596     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
597                 "TC port offset");
598     if (!interact)
599         add_std_args(ts);
600
601     ts = cmd_CreateSyntax("listhosts", bc_ListHostsCmd, 0,
602                           "list config hosts");
603     if (!interact)
604         add_std_args(ts);
605
606     ts = cmd_CreateSyntax("jobs", bc_JobsCmd, 0, "list running jobs");
607
608     ts = cmd_CreateSyntax("kill", bc_KillCmd, 0, "kill running job");
609     cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_REQUIRED,
610                 "job ID or dump set name");
611
612     ts = cmd_CreateSyntax("listvolsets", bc_ListVolSetCmd, 0,
613                           "list volume sets");
614     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL, "volume set name");
615     if (!interact)
616         add_std_args(ts);
617
618     ts = cmd_CreateSyntax("listdumps", bc_ListDumpScheduleCmd, 0,
619                           "list dump schedules");
620     if (!interact)
621         add_std_args(ts);
622
623     ts = cmd_CreateSyntax("addvolset", bc_AddVolSetCmd, 0,
624                           "create a new volume set");
625     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
626     cmd_AddParm(ts, "-temporary", CMD_FLAG, CMD_OPTIONAL,
627                 "temporary volume set");
628     if (!interact)
629         add_std_args(ts);
630
631     ts = cmd_CreateSyntax("status", bc_GetTapeStatusCmd, 0,
632                           "get tape coordinator status");
633     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
634                 "TC port offset");
635     if (!interact)
636         add_std_args(ts);
637
638     ts = cmd_CreateSyntax("delvolset", bc_DeleteVolSetCmd, 0,
639                           "delete a volume set");
640     cmd_AddParm(ts, "-name", CMD_LIST, CMD_REQUIRED, "volume set name");
641     if (!interact)
642         add_std_args(ts);
643
644     ts = cmd_CreateSyntax("addvolentry", bc_AddVolEntryCmd, 0,
645                           "add a new volume entry");
646     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
647     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_REQUIRED, "machine name");
648     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_REQUIRED, "partition name");
649     cmd_AddParm(ts, "-volumes", CMD_SINGLE, CMD_REQUIRED,
650                 "volume name (regular expression)");
651     if (!interact)
652         add_std_args(ts);
653
654     ts = cmd_CreateSyntax("delvolentry", bc_DeleteVolEntryCmd, 0,
655                           "delete a volume set sub-entry");
656     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_REQUIRED, "volume set name");
657     cmd_AddParm(ts, "-entry", CMD_SINGLE, CMD_REQUIRED, "volume set index");
658     if (!interact)
659         add_std_args(ts);
660
661     ts = cmd_CreateSyntax("adddump", bc_AddDumpCmd, 0, "add dump schedule");
662     cmd_AddParm(ts, "-dump", CMD_LIST, CMD_REQUIRED, "dump level name");
663     cmd_AddParm(ts, "-expires", CMD_LIST, CMD_OPTIONAL, "expiration date");
664     if (!interact)
665         add_std_args(ts);
666
667     ts = cmd_CreateSyntax("deldump", bc_DeleteDumpCmd, 0,
668                           "delete dump schedule");
669     cmd_AddParm(ts, "-dump", CMD_SINGLE, CMD_REQUIRED, "dump level name");
670     if (!interact)
671         add_std_args(ts);
672
673     ts = cmd_CreateSyntax("labeltape", bc_LabelTapeCmd, 0, "label a tape");
674     cmd_AddParm(ts, "-name", CMD_SINGLE, CMD_OPTIONAL,
675                 "AFS tape name, defaults to NULL");
676     cmd_AddParm(ts, "-size", CMD_SINGLE, CMD_OPTIONAL,
677                 "tape size in Kbytes, defaults to size in tapeconfig");
678     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
679                 "TC port offset");
680     cmd_AddParm(ts, "-pname", CMD_SINGLE, CMD_OPTIONAL,
681                 "permanent tape name");
682     if (!interact)
683         add_std_args(ts);
684
685     ts = cmd_CreateSyntax("readlabel", bc_ReadLabelCmd, 0,
686                           "read the label on tape");
687     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
688                 "TC port offset");
689     if (!interact)
690         add_std_args(ts);
691
692     ts = cmd_CreateSyntax("scantape", bc_ScanDumpsCmd, 0,
693                           "dump information recovery from tape");
694     cmd_AddParm(ts, "-dbadd", CMD_FLAG, CMD_OPTIONAL,
695                 "add information to the database");
696     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
697                 "TC port offset");
698     if (!interact)
699         add_std_args(ts);
700
701     ts = cmd_CreateSyntax("volinfo", bc_dblookupCmd, 0,
702                           "query the backup database");
703     cmd_AddParm(ts, "-volume", CMD_SINGLE, CMD_REQUIRED, "volume name");
704     if (!interact)
705         add_std_args(ts);
706
707     ts = cmd_CreateSyntax("setexp", bc_SetExpCmd, 0,
708                           "set/clear dump expiration dates");
709     cmd_AddParm(ts, "-dump", CMD_LIST, CMD_REQUIRED, "dump level name");
710     cmd_AddParm(ts, "-expires", CMD_LIST, CMD_OPTIONAL, "expiration date");
711     if (!interact)
712         add_std_args(ts);
713
714     ts = cmd_CreateSyntax("savedb", bc_saveDbCmd, 0, "save backup database");
715     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
716                 "TC port offset");
717     cmd_AddParm(ts, "-archive", CMD_LIST, CMD_OPTIONAL, "date time");
718     if (!interact)
719         add_std_args(ts);
720
721     ts = cmd_CreateSyntax("restoredb", bc_restoreDbCmd, 0,
722                           "restore backup database");
723     cmd_AddParm(ts, "-portoffset", CMD_SINGLE, CMD_OPTIONAL,
724                 "TC port offset");
725     if (!interact)
726         add_std_args(ts);
727
728     ts = cmd_CreateSyntax("dumpinfo", bc_dumpInfoCmd, 0,
729                           "provide information about a dump in the database");
730     cmd_AddParm(ts, "-ndumps", CMD_SINGLE, CMD_OPTIONAL, "no. of dumps");
731     cmd_AddParm(ts, "-id", CMD_SINGLE, CMD_OPTIONAL, "dump id");
732     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL,
733                 "detailed description");
734     if (!interact)
735         add_std_args(ts);
736
737     ts = cmd_CreateSyntax("dbverify", bc_dbVerifyCmd, 0,
738                           "check ubik database integrity");
739     cmd_AddParm(ts, "-detail", CMD_FLAG, CMD_OPTIONAL, "additional details");
740     if (!interact)
741         add_std_args(ts);
742
743     ts = cmd_CreateSyntax("deletedump", bc_deleteDumpCmd, 0,
744                           "delete dumps from the database");
745     cmd_AddParm(ts, "-dumpid", CMD_LIST, CMD_OPTIONAL, "dump id");
746     cmd_AddParm(ts, "-from", CMD_LIST, CMD_OPTIONAL, "date time");
747     cmd_AddParm(ts, "-to", CMD_LIST, CMD_OPTIONAL, "date time");
748     cmd_AddParm(ts, "-port", CMD_SINGLE, CMD_OPTIONAL, "TC port offset");
749     cmd_AddParm(ts, "-groupid", CMD_SINGLE, CMD_OPTIONAL, "group ID");
750     cmd_AddParm(ts, "-dbonly", CMD_FLAG, CMD_OPTIONAL,
751                 "delete the dump from the backup database only");
752     cmd_AddParm(ts, "-force", CMD_FLAG, CMD_OPTIONAL,
753                 "always delete from backup database");
754     cmd_AddParm(ts, "-noexecute", CMD_FLAG, CMD_OPTIONAL,
755                 "Just list the dumps");
756
757     if (!interact)
758         add_std_args(ts);
759
760     ts = cmd_CreateSyntax("interactive", bc_interactCmd, 0,
761                           "enter interactive mode");
762     add_std_args(ts);
763
764     /*
765      * Now execute the command.
766      */
767
768     targc = 0;
769     targv[targc++] = argv[0];
770     if (interact)
771         targv[targc++] = "interactive";
772     for (i = 1; i < argc; i++)
773         targv[targc++] = argv[i];
774
775     code = doDispatch(targc, targv, 1);
776
777     if (!interact || !bcInit) { /* Non-interactive mode */
778         if (code)
779             exit(-1);
780         if (bcInit)
781             code = bc_WaitForNoJobs();  /* wait for any jobs to finish */
782         exit(code);             /* and exit */
783     }
784
785     /* Iterate on command lines, interpreting user commands (interactive mode) */
786     while (1) {
787         int ret;
788
789         printf("backup> ");
790         fflush(stdout);
791
792
793         while ((ret = LWP_GetLine(lineBuffer, sizeof(lineBuffer))) == 0)
794             printf("%s: Command line too long\n", whoami);      /* line too long */
795
796         if (ret == -1)
797             return 0;           /* Got EOF */
798
799         if (!LineIsBlank(lineBuffer)) {
800             code = cmd_ParseLine(lineBuffer, targv, &targc, MAXV);
801             if (code)
802                 com_err(whoami, code, "; Can't parse line: '%s'",
803                         error_message(code));
804             else {
805                 doDispatch(targc, targv, 1);
806                 cmd_FreeArgv(targv);
807             }
808         }
809     }
810 }                               /*main */