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