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