compiler-warnings-20031119
[openafs.git] / src / bucoord / commands.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 <afs/cmd.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #else
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <strings.h>
26 #endif
27 #include <errno.h>
28 #include <afs/com_err.h>
29 #include <afs/budb.h>
30 #include <afs/butc.h>
31 #include <afs/bubasics.h>       /* PA */
32 #include <afs/volser.h>
33 #include <afs/voldefs.h>        /* PA */
34 #include <afs/vldbint.h>        /* PA */
35 #include <afs/ktime.h>          /* PA */
36 #include <time.h>
37
38 #include <string.h>
39 #include <lock.h>
40 #include <afs/butc.h>
41 #include <afs/tcdata.h>
42 #include <afs/butx.h>
43 #include "bc.h"
44 #include "error_macros.h"
45
46
47 extern struct bc_config *bc_globalConfig;
48 extern char *ktime_GetDateUsage();
49 extern struct bc_dumpSchedule *bc_FindDumpSchedule();
50 extern struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *cfg,
51                                              char *name);
52 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
53 extern struct ubik_client *cstruct;
54 extern int bc_Dumper();         /* function to do dumps */
55 extern int bc_Restorer();       /* function to do restores */
56 extern void bc_HandleMisc();
57 extern afs_int32 ScanDumpHdr();
58 extern afs_int32 ScanTapeVolume();
59 extern char *whoami;
60 extern int VL_ListEntry();
61 extern int VL_ListAttributesN2();
62 extern struct ktc_token ttoken;
63 extern char *tailCompPtr();
64 extern statusP createStatusNode();
65
66 char *loadFile;
67 extern afs_int32 lastTaskCode;
68
69 #ifdef AFS_DEC_ENV
70 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
71 #else
72 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
73 #endif
74
75 int
76 bc_EvalVolumeSet(aconfig, avs, avols, uclient)
77      struct bc_config *aconfig;
78      register struct bc_volumeSet *avs;
79      struct bc_volumeDump **avols;
80      struct ubik_client *uclient;
81 {                               /*bc_EvalVolumeSet */
82     int code;
83     int a, b, c;
84     static afs_int32 use = 2;
85
86     if (use == 2) {             /* Use EvalVolumeSet2() */
87         code = EvalVolumeSet2(aconfig, avs, avols, uclient);
88         if (code == RXGEN_OPCODE)
89             use = 1;
90     }
91     if (use == 1) {             /* Use EvalVolumeSet1() */
92         code = EvalVolumeSet1(aconfig, avs, avols, uclient);
93     }
94     return code;
95 }                               /*bc_EvalVolumeSet */
96
97 struct partitionsort {
98     afs_int32 part;
99     struct bc_volumeDump *vdlist;
100     struct bc_volumeDump *lastvdlist;
101     struct bc_volumeDump *dupvdlist;
102     struct bc_volumeEntry *vole;
103     struct partitionsort *next;
104 };
105 struct serversort {
106     afs_uint32 ipaddr;
107     struct partitionsort *partitions;
108     struct serversort *next;
109 };
110
111 afs_int32
112 getSPEntries(server, partition, serverlist, ss, ps)
113      afs_uint32 server;
114      afs_int32 partition;
115      struct serversort **serverlist, **ss;
116      struct partitionsort **ps;
117 {
118     if (!(*ss) || ((*ss)->ipaddr != server)) {
119         *ps = 0;
120         for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
121             if ((*ss)->ipaddr == server)
122                 break;
123         }
124     }
125     /* No server entry added. Add one */
126     if (!(*ss)) {
127         *ss = (struct serversort *)malloc(sizeof(struct serversort));
128         if (!(*ss)) {
129             com_err(whoami, BC_NOMEM, "");
130             *ss = 0;
131             return (BC_NOMEM);
132         }
133         memset(*ss, 0, sizeof(struct serversort));
134         (*ss)->ipaddr = server;
135         (*ss)->next = *serverlist;
136         *serverlist = *ss;
137     }
138
139
140     if (!(*ps) || ((*ps)->part != partition)) {
141         for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
142             if ((*ps)->part == partition)
143                 break;
144         }
145     }
146     /* No partition entry added. Add one */
147     if (!(*ps)) {
148         *ps = (struct partitionsort *)malloc(sizeof(struct partitionsort));
149         if (!(*ps)) {
150             com_err(whoami, BC_NOMEM, "");
151             free(*ss);
152             *ps = 0;
153             *ss = 0;
154             return (BC_NOMEM);
155         }
156         memset(*ps, 0, sizeof(struct partitionsort));
157         (*ps)->part = partition;
158         (*ps)->next = (*ss)->partitions;
159         (*ss)->partitions = *ps;
160     }
161     return 0;
162 }
163
164 afs_int32
165 randSPEntries(serverlist, avols)
166      struct serversort *serverlist;
167      struct bc_volumeDump **avols;
168 {
169     struct serversort *ss, **pss, *tss;
170     struct partitionsort *ps, **pps;
171     afs_int32 r;
172     afs_int32 scount, pcount;
173
174     *avols = 0;
175
176     /* Seed random number generator */
177     r = time(0) + getpid();
178     srand(r);
179
180     /* Count number of servers, remove one at a time */
181     for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
182     for (; scount; scount--) {
183         /* Pick a random server in list and remove it */
184         r = (rand() >> 4) % scount;
185         for (pss = &serverlist, ss = serverlist; r;
186              pss = &ss->next, ss = ss->next, r--);
187         *pss = ss->next;
188
189         /* Count number of partitions, remove one at a time */
190         for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
191         for (; pcount; pcount--) {
192             /* Pick a random parition in list and remove it */
193             r = (rand() >> 4) % pcount;
194             for (pps = &ss->partitions, ps = ss->partitions; r;
195                  pps = &ps->next, ps = ps->next, r--);
196             *pps = ps->next;
197
198             ps->lastvdlist->next = *avols;
199             *avols = ps->vdlist;
200             free(ps);
201         }
202         free(ss);
203     }
204 }
205
206 int
207 EvalVolumeSet2(aconfig, avs, avols, uclient)
208      struct bc_config *aconfig;
209      struct bc_volumeSet *avs;
210      struct bc_volumeDump **avols;
211      struct ubik_client *uclient;
212 {                               /*EvalVolumeSet2 */
213     struct bc_volumeEntry *tve;
214     struct bc_volumeDump *tavols;
215     struct VldbListByAttributes attributes;
216     afs_int32 si, nsi;          /* startIndex and nextStartIndex */
217     afs_int32 nentries, e, ei, et, add, l;
218     nbulkentries bulkentries;
219     struct nvldbentry *entries = 0;
220     struct bc_volumeDump *tvd;
221     afs_int32 code = 0, tcode;
222     afs_int32 count = 0;
223     struct serversort *servers = 0, *lastserver = 0, *ss = 0, *nss;
224     struct partitionsort *ps = 0, *nps;
225
226     *avols = (struct bc_volumeDump *)0;
227     bulkentries.nbulkentries_len = 0;
228     bulkentries.nbulkentries_val = 0;
229
230     /* For each of the volume set entries - collect the volumes that match it */
231     for (tve = avs->ventries; tve; tve = tve->next) {
232         /* Put together a call to the vlserver for this vlentry. The 
233          * performance gain is from letting the vlserver expand the
234          * volumeset and not this routine.
235          */
236         attributes.Mask = 0;
237         if (tve->server.sin_addr.s_addr) {      /* The server */
238             attributes.Mask |= VLLIST_SERVER;
239             attributes.server = tve->server.sin_addr.s_addr;
240         }
241         if (tve->partition != -1) {     /* The partition */
242             attributes.Mask |= VLLIST_PARTITION;
243             attributes.partition = tve->partition;
244         }
245
246         /* Now make the call to the vlserver */
247         for (si = 0; si != -1; si = nsi) {
248             nentries = 0;
249             bulkentries.nbulkentries_len = 0;
250             bulkentries.nbulkentries_val = 0;
251             nsi = -1;
252             tcode =
253                 ubik_Call(VL_ListAttributesN2, uclient, 0, &attributes,
254                           tve->name, si, &nentries, &bulkentries, &nsi);
255             if (tcode)
256                 ERROR(tcode);
257
258             /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
259              * it is not complete. The only way to tell if it is not complete
260              * is if et == 0 (which we check for below). Also, if the call didn't
261              * match any entries, then we don't know what version the vlserver
262              * is. In both cases, we return RXGEN_OPCODE and the calling routine
263              * will switch to the EvalVolumeSet1() call.
264              */
265             if (nentries == 0)
266                 ERROR(RXGEN_OPCODE);    /* Use EvalVolumeSet1 */
267
268             /* Step through each entry and add it to the list of volumes */
269             entries = bulkentries.nbulkentries_val;
270             for (e = 0; e < nentries; e++) {
271                 ei = entries[e].matchindex & 0xffff;
272                 et = (entries[e].matchindex >> 16) & 0xffff;
273                 switch (et) {
274                 case ITSRWVOL:{
275                         et = RWVOL;
276                         break;
277                     }
278                 case ITSBACKVOL:{
279                         et = BACKVOL;
280                         break;
281                     }
282                 case ITSROVOL:{
283                         et = ROVOL;
284                         break;
285                     }
286                 default:
287                     ERROR(RXGEN_OPCODE);        /* Use EvalVolumeSet1 */
288                 }
289
290                 /* Find server and partiton structure to hang the entry off of */
291                 tcode =
292                     getSPEntries(entries[e].serverNumber[ei],
293                                  entries[e].serverPartition[ei], &servers,
294                                  &ss, &ps);
295                 if (tcode) {
296                     com_err(whoami, tcode, "");
297                     ERROR(tcode);
298                 }
299
300                 /* Detect if this entry should be added (not a duplicate).
301                  * Use ps->dupvdlist and ps->vole to only search volumes from
302                  * previous volume set entries.
303                  */
304                 add = 1;
305                 if (tve != avs->ventries) {
306                     l = strlen(entries[e].name);
307                     if (ps->vole != tve) {
308                         ps->vole = tve;
309                         ps->dupvdlist = ps->vdlist;
310                     }
311                     for (tavols = ps->dupvdlist; add && tavols;
312                          tavols = tavols->next) {
313                         if (strncmp(tavols->name, entries[e].name, l) == 0) {
314                             if ((strcmp(&entries[e].name[l], ".backup") == 0)
315                                 || (strcmp(&entries[e].name[l], ".readonly")
316                                     == 0)
317                                 || (strcmp(&entries[e].name[l], "") == 0))
318                                 add = 0;
319                         }
320                     }
321                 }
322
323                 if (add) {
324                     /* Allocate a volume dump structure and its name */
325                     tvd = (struct bc_volumeDump *)
326                         malloc(sizeof(struct bc_volumeDump));
327                     if (!tvd) {
328                         com_err(whoami, BC_NOMEM, "");
329                         ERROR(BC_NOMEM);
330                     }
331                     memset(tvd, 0, sizeof(*tvd));
332
333                     tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
334                     if (!(tvd->name)) {
335                         com_err(whoami, BC_NOMEM, "");
336                         free(tvd);
337                         ERROR(BC_NOMEM);
338                     }
339
340                     /* Fill it in and thread onto avols list */
341                     strcpy(tvd->name, entries[e].name);
342                     if (et == BACKVOL)
343                         strcat(tvd->name, ".backup");
344                     else if (et == ROVOL)
345                         strcat(tvd->name, ".readonly");
346                     tvd->vid = entries[e].volumeId[et];
347                     tvd->entry = tve;
348                     tvd->volType = et;
349                     tvd->partition = entries[e].serverPartition[ei];
350                     tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
351                     tvd->server.sin_port = 0;   /* default FS port */
352                     tvd->server.sin_family = AF_INET;
353 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
354                     tvd->server.sin_len = sizeof(struct sockaddr_in);
355 #endif
356
357                     /* String tvd off of partition struct */
358                     tvd->next = ps->vdlist;
359                     ps->vdlist = tvd;
360                     if (!tvd->next)
361                         ps->lastvdlist = tvd;
362
363                     count++;
364                 }
365             }
366
367             /* Free memory allocated during VL call */
368             if (bulkentries.nbulkentries_val) {
369                 free((char *)bulkentries.nbulkentries_val);
370                 bulkentries.nbulkentries_val = 0;
371                 entries = 0;
372             }
373         }
374     }
375
376     /* Randomly link the volumedump entries together */
377     randSPEntries(servers, avols);
378     fprintf(stderr, "Total number of volumes : %u\n", count);
379
380   error_exit:
381     if (bulkentries.nbulkentries_val) {
382         free((char *)bulkentries.nbulkentries_val);
383     }
384     return (code);
385 }                               /*EvalVolumeSet2 */
386
387 /*-----------------------------------------------------------------------------
388  * EvalVolumeSetOld
389  *
390  * Description:
391  *      Takes the entries in a volumeset and expands them into a list of 
392  *      volumes. Every VLDB volume entry is looked at and compared to the
393  *      volumeset entries.
394  *
395  *      When matching a VLDB volume entry to a volumeset entry, 
396  *       1. If the RW volume entry matches, that RW volume is used.
397  *       2. Otherwise, if the BK volume entry matches, the BK volume is used.
398  *       3. Finally, if the RO volume entry matches, the RO volume is used.
399  *      For instance: A volumeset entry of ".* .* user.t.*" will match volume 
400  *                    "user.troy" and "user.troy.backup". The rules will use 
401  *                    the RW volume "user.troy".
402  *
403  *      When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
404  *      that volume is used and matches against any remaining volumeset entries 
405  *      are not even done.
406  *      For instance: A 1st volumeset entry ".* .* .*.backup" will match with
407  *                    "user.troy.backup". Its 2nd volumeset entry ".* .* .*" 
408  *                    would have matched its RW volume "user.troy", but the first 
409  *                    match is used and the second match isn't even done.
410  *
411  * Arguments:
412  *      aconfig : Global configuration info.
413  *      avs     : 
414  *      avols   : Ptr to linked list of entries describing volumes to dump.
415  *      uclient : Ptr to Ubik client structure.
416  *
417  * Returns:
418  *      0 on successful volume set evaluation,
419  *      Lower-level codes otherwise.
420  *
421  * Environment:
422  *      Expand only the single volume set provided (avs); don't go down its chain.
423  *
424  * Side Effects:
425  *      None.
426  *-----------------------------------------------------------------------------
427  */
428 int
429 EvalVolumeSet1(aconfig, avs, avols, uclient)
430      struct bc_config *aconfig;
431      struct bc_volumeSet *avs;
432      struct bc_volumeDump **avols;
433      struct ubik_client *uclient;
434 {                               /*EvalVolumeSet1 */
435     afs_int32 code;             /*Result of various calls */
436     char *errm;
437     struct bc_volumeDump *tvd;  /*Ptr to new dump instance */
438     struct bc_volumeEntry *tve, *ctve;  /*Ptr to new volume entry instance */
439     char patt[256];             /*Composite regex; also, target string */
440     int volType;                /*Type of volume that worked */
441     afs_int32 index;            /*Current VLDB entry index */
442     afs_int32 count;            /*Needed by VL_ListEntry() */
443     afs_int32 next_index;       /*Next index to list */
444     struct vldbentry entry;     /*VLDB entry */
445     int srvpartpair;            /*Loop counter: server/partition pair */
446     afs_int32 total = 0;
447     int found, foundentry;
448     struct serversort *servers = 0, *ss = 0;
449     struct partitionsort *ps = 0;
450
451     *avols = (struct bc_volumeDump *)0;
452     ctve = (struct bc_volumeEntry *)0;  /* no compiled entry */
453
454     /* For each vldb entry.
455      * Variable next_index is set to the index of the next VLDB entry
456      * in the enumeration.
457      */
458     for (index = 0; 1; index = next_index) {    /*w */
459         memset(&entry, 0, sizeof(entry));
460         code = ubik_Call(VL_ListEntry,  /*Routine to invoke */
461                          uclient,       /*Ubik client structure */
462                          0,     /*Ubik flags */
463                          index, /*Current index */
464                          &count,        /*Ptr to working variable */
465                          &next_index,   /*Ptr to next index value to list */
466                          &entry);       /*Ptr to entry to fill */
467         if (code)
468             return code;
469         if (!next_index)
470             break;              /* If the next index is invalid, bail out now. */
471
472         /* For each entry in the volume set */
473         found = 0;              /* No match in volume set yet */
474         for (tve = avs->ventries; tve; tve = tve->next) {       /*ve */
475             /* for each server in the vldb entry */
476             for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) {        /*s */
477                 /* On the same server */
478                 if (tve->server.sin_addr.s_addr
479                     && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
480                                          entry.serverNumber[srvpartpair],
481                                          &code)) {
482                     if (code)
483                         return (code);
484                     continue;
485                 }
486
487
488                 /* On the same partition */
489                 if ((tve->partition != -1)
490                     && (tve->partition != entry.serverPartition[srvpartpair]))
491                     continue;
492
493                 /* If the volume entry is not compiled, then compile it */
494                 if (ctve != tve) {
495                     sprintf(patt, "^%s$", tve->name);
496                     errm = (char *)re_comp(patt);
497                     if (errm) {
498                         com_err(whoami, 0,
499                                 "Can't compile regular expression '%s': %s",
500                                 patt, errm);
501                         return (-1);
502                     }
503                     ctve = tve;
504                 }
505
506                 /* If the RW name matches the volume set entry, take
507                  * it and exit. First choice is to use the RW volume.
508                  */
509                 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
510                     if (entry.flags & RW_EXISTS) {
511                         sprintf(patt, "%s", entry.name);
512                         code = re_exec(patt);
513                         if (code == 1) {
514                             found = 1;
515                             foundentry = srvpartpair;
516                             volType = RWVOL;
517                             break;
518                         }
519                     }
520
521                     /* If the BK name matches the volume set entry, take 
522                      * it and exit. Second choice is to use the BK volume.
523                      */
524                     if (entry.flags & BACK_EXISTS) {
525                         sprintf(patt, "%s.backup", entry.name);
526                         code = re_exec(patt);
527                         if (code == 1) {
528                             found = 1;
529                             foundentry = srvpartpair;
530                             volType = BACKVOL;
531                             break;
532                         }
533                     }
534                 }
535
536                 /* If the RO name matches the volume set entry, remember
537                  * it, but continue searching. Further entries may be
538                  * RW or backup entries that will match.
539                  */
540                 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
541                          && (entry.flags & RO_EXISTS)) {
542                     sprintf(patt, "%s.readonly", entry.name);
543                     code = re_exec(patt);
544                     if (code == 1) {
545                         found = 1;
546                         foundentry = srvpartpair;
547                         volType = ROVOL;
548                     }
549                 }
550
551                 if (code < 0)
552                     com_err(whoami, 0, "Internal error in regex package");
553             }                   /*s */
554
555             /* If found a match, then create a new volume dump entry */
556             if (found) {        /*f */
557                 /* Find server and partition structure to hang the entry off of */
558                 code =
559                     getSPEntries(entry.serverNumber[foundentry],
560                                  entry.serverPartition[foundentry], &servers,
561                                  &ss, &ps);
562                 if (code) {
563                     com_err(whoami, code, "");
564                     return (code);
565                 }
566
567                 total++;
568                 tvd = (struct bc_volumeDump *)
569                     malloc(sizeof(struct bc_volumeDump));
570                 if (!tvd) {
571                     com_err(whoami, BC_NOMEM, "");
572                     return (BC_NOMEM);
573                 }
574                 memset(tvd, 0, sizeof(*tvd));
575
576                 tvd->name = (char *)malloc(strlen(entry.name) + 10);
577                 if (!(tvd->name)) {
578                     com_err(whoami, BC_NOMEM, "");
579                     free(tvd);
580                     return (BC_NOMEM);
581                 }
582
583                 strcpy(tvd->name, entry.name);
584                 if (volType == BACKVOL)
585                     strcat(tvd->name, ".backup");
586                 else if (volType == ROVOL)
587                     strcat(tvd->name, ".readonly");
588                 tvd->vid = entry.volumeId[volType];
589                 tvd->entry = tve;
590                 tvd->volType = volType;
591                 tvd->partition = entry.serverPartition[foundentry];
592                 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
593                 tvd->server.sin_port = 0;       /* default FS port */
594                 tvd->server.sin_family = AF_INET;
595
596                 /* String tvd off of partition struct */
597                 tvd->next = ps->vdlist;
598                 ps->vdlist = tvd;
599                 if (!tvd->next)
600                     ps->lastvdlist = tvd;
601
602                 break;
603             }                   /*f */
604         }                       /*ve */
605     }                           /*w */
606
607     /* Randomly link the volumedump entries together */
608     randSPEntries(servers, avols);
609
610     fprintf(stderr, "Total number of volumes : %u\n", total);
611     return (0);
612 }                               /*EvalVolumeSet1 */
613
614 /* compactDateString
615  *      print out a date in compact format, 16 chars, format is
616  *      mm/dd/yyyy hh:mm
617  * entry:
618  *      date_long - ptr to a long containing the time
619  * exit:
620  *      ptr to a string containing a representation of the date
621  */
622 char *
623 compactDateString(date_long, string, size)
624      afs_int32 *date_long, size;
625      char *string;
626 {
627     struct tm *ltime;
628
629     if (!string)
630         return 0;
631
632     if (*date_long == NEVERDATE) {
633         sprintf(string, "NEVER");
634     } else {
635         ltime = localtime(date_long);
636         /* prints date in U.S. format of mm/dd/yyyy */
637         strftime(string, size, "%m/%d/%Y %H:%M", ltime);
638     }
639     return (string);
640 }
641
642 afs_int32
643 bc_SafeATOI(anum)
644      char *anum;
645 {
646     afs_int32 total = 0;
647
648     for (; *anum; anum++) {
649         if ((*anum < '0') || (*anum > '9'))
650             return -1;
651         total = (10 * total) + (afs_int32) (*anum - '0');
652     }
653     return total;
654 }
655
656 /* bc_FloatATOI:
657  *    Take a string and parse it for a number (could be float) followed
658  *    by a character representing the units (K,M,G,T). Default is 'K'.
659  *    Return the size in KBytes.
660  */
661 afs_int32
662 bc_FloatATOI(anum)
663      char *anum;
664 {
665     float total = 0;
666     afs_int32 rtotal;
667     afs_int32 fraction = 0;     /* > 0 if past the decimal */
668
669     for (; *anum; anum++) {
670         if ((*anum == 't') || (*anum == 'T')) {
671             total *= 1024 * 1024 * 1024;
672             break;
673         }
674         if ((*anum == 'g') || (*anum == 'G')) {
675             total *= 1024 * 1024;
676             break;
677         }
678         if ((*anum == 'm') || (*anum == 'M')) {
679             total *= 1024;
680             break;
681         }
682         if ((*anum == 'k') || (*anum == 'K')) {
683             break;
684         }
685         if (*anum == '.') {
686             fraction = 10;
687             continue;
688         }
689         if ((*anum < '0') || (*anum > '9'))
690             return -1;
691
692         if (!fraction) {
693             total = (10. * total) + (float)(*anum - '0');
694         } else {
695             total += ((float)(*anum - '0')) / (float)fraction;
696             fraction *= 10;
697         }
698     }
699
700     total += 0.5;               /* Round up */
701     if (total > 0x7fffffff)     /* Don't go over 2G */
702         total = 0x7fffffff;
703     rtotal = (afs_int32) total;
704     return (rtotal);
705 }
706
707 /* make a copy of a string so that it can be freed later */
708 char *
709 bc_CopyString(astring)
710      char *astring;
711 {
712     afs_int32 tlen;
713     char *tp;
714
715     if (!astring)
716         return (NULL);          /* propagate null strings easily */
717     tlen = strlen(astring);
718     tp = (char *)malloc(tlen + 1);      /* don't forget the terminating null */
719     if (!tp) {
720         com_err(whoami, BC_NOMEM, "");
721         return (tp);
722     }
723     strcpy(tp, astring);
724     return tp;
725 }
726
727 /* concatParams
728  * 
729  *    Concatenates the parameters of an option and returns the string.
730  *
731  */
732
733 char *
734 concatParams(itemPtr)
735      struct cmd_item *itemPtr;
736 {
737     struct cmd_item *tempPtr;
738     afs_int32 length = 0;
739     char *string;
740
741     /* compute the length of string required */
742     for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
743         length += strlen(tempPtr->data);
744         length++;               /* space or null terminator */
745     }
746
747     if (length == 0) {          /* no string (0 length) */
748         com_err(whoami, 0, "Can't have zero length date and time string");
749         return (NULL);
750     }
751
752     string = (char *)malloc(length);    /* allocate the string */
753     if (!string) {
754         com_err(whoami, BC_NOMEM, "");
755         return (NULL);
756     }
757     string[0] = 0;
758
759     tempPtr = itemPtr;          /* now assemble the string */
760     while (tempPtr) {
761         strcat(string, tempPtr->data);
762         tempPtr = tempPtr->next;
763         if (tempPtr)
764             strcat(string, " ");
765     }
766
767     return (string);            /* return the string */
768 }
769
770 /* printIfStatus
771  *      print out an interface status node as received from butc
772  */
773
774 printIfStatus(statusPtr)
775      struct tciStatusS *statusPtr;
776 {
777     printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
778     if (statusPtr->nKBytes)
779         printf("%ld Kbytes transferred", statusPtr->nKBytes);
780     if (strlen(statusPtr->volumeName) != 0) {
781         if (statusPtr->nKBytes)
782             printf(", ");
783         printf("volume %s", statusPtr->volumeName);
784     }
785
786     /* orphan */
787
788     if (statusPtr->flags & ABORT_REQUEST)
789         printf(" [abort request rcvd]");
790
791     if (statusPtr->flags & ABORT_DONE)
792         printf(" [abort complete]");
793
794     if (statusPtr->flags & OPR_WAIT)
795         printf(" [operator wait]");
796
797     if (statusPtr->flags & CALL_WAIT)
798         printf(" [callout in progress]");
799
800     if (statusPtr->flags & DRIVE_WAIT)
801         printf(" [drive wait]");
802
803     if (statusPtr->flags & TASK_DONE)
804         printf(" [done]");
805     printf("\n");
806 }
807
808 afs_int32
809 getPortOffset(port)
810      char *port;
811 {
812     afs_int32 portOffset;
813
814     portOffset = bc_SafeATOI(port);
815
816     if (portOffset < 0) {
817         com_err(whoami, 0, "Can't decode port offset '%s'", port);
818         return (-1);
819     } else if (portOffset > BC_MAXPORTOFFSET) {
820         com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
821                 BC_MAXPORTOFFSET);
822         return (-1);
823     }
824     return (portOffset);
825 }
826
827 /* bc_GetTapeStatusCmd
828  *      display status of all tasks on a particular tape coordinator
829  */
830
831 bc_GetTapeStatusCmd(as, arock)
832      struct cmd_syndesc *as;
833      char *arock;
834 {
835     afs_int32 code;
836     afs_int32 index, dumpID;
837     struct rx_connection *tconn;
838     afs_int32 portOffset = 0;
839
840     int ntasks;
841     afs_uint32 flags;
842     afs_uint32 taskId;
843     struct tciStatusS status;
844
845     code = bc_UpdateHosts();
846     if (code) {
847         com_err(whoami, code, "; Can't retrieve tape hosts");
848         return (code);
849     }
850
851     if (as->parms[0].items) {
852         portOffset = getPortOffset(as->parms[0].items->data);
853         if (portOffset < 0)
854             return (BC_BADARG);
855     }
856
857     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
858     if (code)
859         return (code);
860
861     flags = TSK_STAT_FIRST;
862     taskId = 0;
863     ntasks = 0;
864
865     while ((flags & TSK_STAT_END) == 0) {
866         code = TC_ScanStatus(tconn, &taskId, &status, &flags);
867         if (code) {
868             if (code == TC_NOTASKS)
869                 break;
870             com_err(whoami, code, "; Can't get status from butc");
871             return (-1);
872         }
873         if ((flags & TSK_STAT_NOTFOUND))
874             break;              /* Can't find the task id */
875         flags &= ~TSK_STAT_FIRST;       /* turn off flag */
876
877         printIfStatus(&status);
878         ntasks++;
879     }
880
881     if (ntasks == 0)
882         printf("Tape coordinator is idle\n");
883
884     if (flags & TSK_STAT_ADSM)
885         printf("TSM Tape coordinator\n");
886     else if (flags & TSK_STAT_XBSA)
887         printf("XBSA Tape coordinator\n");
888
889     return (0);
890 }
891
892 extern struct Lock dispatchLock;
893
894 /* bc_WaitForNoJobs
895  *      wait for all jobs to terminate
896  */
897 bc_WaitForNoJobs()
898 {
899     afs_int32 wcode = 0;
900     int i;
901     int waitmsg = 1;
902     int usefulJobRunning = 1;
903
904     extern dlqlinkT statusHead;
905     statusP ptr;
906     dlqlinkP dptr;
907
908     com_err(whoami, 0, "waiting for job termination");
909
910     while (usefulJobRunning) {
911         usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
912         if (dispatchLock.excl_locked)
913             usefulJobRunning = 1;
914         for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
915             if (bc_dumpTasks[i].flags & BC_DI_INUSE)
916                 usefulJobRunning = 1;
917         }
918
919         /* Wait 5 seconds and check again */
920         if (usefulJobRunning)
921             IOMGR_Sleep(5);
922     }
923     return (lastTaskCode);
924 }
925
926 /* bc_JobsCmd
927  *      print status on running jobs
928  * parameters
929  *      ignored - a null "as" prints only jobs.
930  */
931
932 bc_JobsCmd(as, arock)
933      struct cmd_syndesc *as;
934      char *arock;
935 {
936     int i;
937     afs_int32 prevTime;
938     struct bc_dumpTask *td;
939     dlqlinkP ptr;
940     statusP statusPtr;
941     char ds[50];
942
943     statusP youngest;
944     dlqlinkT atJobsHead;
945     extern dlqlinkT statusHead;
946
947     dlqInit(&atJobsHead);
948
949     lock_Status();
950     ptr = (&statusHead)->dlq_next;
951     while (ptr != &statusHead) {
952         statusPtr = (statusP) ptr;
953         ptr = ptr->dlq_next;
954
955         if (statusPtr->scheduledDump) {
956             dlqUnlink((dlqlinkP) statusPtr);
957             dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
958         } else {
959             printf("Job %d:", statusPtr->jobNumber);
960
961             printf(" %s", statusPtr->taskName);
962
963             if (statusPtr->dbDumpId)
964                 printf(": DumpID %u", statusPtr->dbDumpId);
965             if (statusPtr->nKBytes)
966                 printf(", %ld Kbytes", statusPtr->nKBytes);
967             if (strlen(statusPtr->volumeName) != 0)
968                 printf(", volume %s", statusPtr->volumeName);
969
970             if (statusPtr->flags & CONTACT_LOST)
971                 printf(" [butc contact lost]");
972
973             if (statusPtr->flags & ABORT_REQUEST)
974                 printf(" [abort request]");
975
976             if (statusPtr->flags & ABORT_SENT)
977                 printf(" [abort sent]");
978
979             if (statusPtr->flags & OPR_WAIT)
980                 printf(" [operator wait]");
981
982             if (statusPtr->flags & CALL_WAIT)
983                 printf(" [callout in progress]");
984
985             if (statusPtr->flags & DRIVE_WAIT)
986                 printf(" [drive wait]");
987             printf("\n");
988         }
989     }
990
991     /* 
992      * Now print the scheduled dumps.
993      */
994     if (!dlqEmpty(&statusHead) && as)
995         printf("\n");           /* blank line between running and scheduled dumps */
996
997     prevTime = 0;
998     while (!dlqEmpty(&atJobsHead)) {
999         ptr = (&atJobsHead)->dlq_next;
1000         youngest = (statusP) ptr;
1001
1002         ptr = ptr->dlq_next;
1003         while (ptr != &atJobsHead) {    /* Find the dump that starts the earliest */
1004             statusPtr = (statusP) ptr;
1005             if (statusPtr->scheduledDump < youngest->scheduledDump)
1006                 youngest = statusPtr;
1007             ptr = ptr->dlq_next;
1008         }
1009
1010         /* Print token expiration time */
1011         if ((ttoken.endTime > prevTime)
1012             && (ttoken.endTime <= youngest->scheduledDump) && as
1013             && (ttoken.endTime != NEVERDATE)) {
1014             if (ttoken.endTime > time(0)) {
1015                 compactDateString(&ttoken.endTime, ds, 50);
1016                 printf("       %16s: TOKEN EXPIRATION\n", ds);
1017             } else {
1018                 printf("       TOKEN HAS EXPIRED\n");
1019             }
1020         }
1021         prevTime = youngest->scheduledDump;
1022
1023         /* Print the info */
1024         compactDateString(&youngest->scheduledDump, ds, 50);
1025         printf("Job %d:", youngest->jobNumber);
1026         printf(" %16s: %s", ds, youngest->cmdLine);
1027         printf("\n");
1028
1029         /* return to original list */
1030         dlqUnlink((dlqlinkP) youngest);
1031         dlqLinkb(&statusHead, (dlqlinkP) youngest);
1032     }
1033
1034     /* Print token expiration time if havn't already */
1035     if ((ttoken.endTime == NEVERDATE) && as)
1036         printf("     : TOKEN NEVER EXPIRES\n");
1037     else if ((ttoken.endTime > prevTime) && as) {
1038         if (ttoken.endTime > time(0)) {
1039             compactDateString(&ttoken.endTime, ds, 50);
1040             printf("       %16s: TOKEN EXPIRATION\n", ds);
1041         } else {
1042             printf("     : TOKEN HAS EXPIRED\n");
1043         }
1044     }
1045
1046     unlock_Status();
1047     return 0;
1048 }
1049
1050 bc_KillCmd(as, arock)
1051      struct cmd_syndesc *as;
1052      char *arock;
1053 {
1054     afs_int32 i;
1055     afs_int32 slot;
1056     struct bc_dumpTask *td;
1057     char *tp;
1058     char tbuffer[256];
1059     afs_int32 code;
1060
1061     dlqlinkP ptr;
1062     statusP statusPtr;
1063
1064     extern dlqlinkT statusHead;
1065     extern statusP findStatus();
1066
1067
1068     tp = as->parms[0].items->data;
1069     if (strchr(tp, '.') == 0) {
1070         slot = bc_SafeATOI(tp);
1071         if (slot == -1) {
1072             com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1073             return -1;
1074         }
1075
1076         lock_Status();
1077         ptr = (&statusHead)->dlq_next;
1078         while (ptr != &statusHead) {
1079             statusPtr = (statusP) ptr;
1080             if (statusPtr->jobNumber == slot) {
1081                 statusPtr->flags |= ABORT_REQUEST;
1082                 unlock_Status();
1083                 return (0);
1084             }
1085             ptr = ptr->dlq_next;
1086         }
1087         unlock_Status();
1088
1089         fprintf(stderr, "Job %d not found\n", slot);
1090         return (-1);
1091     } else {
1092         /* vol.dump */
1093         td = bc_dumpTasks;
1094         for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1095             if (td->flags & BC_DI_INUSE) {
1096                 /* compute name */
1097                 strcpy(tbuffer, td->volSetName);
1098                 strcat(tbuffer, ".");
1099                 strcat(tbuffer, tailCompPtr(td->dumpName));
1100                 if (strcmp(tbuffer, tp) == 0)
1101                     break;
1102             }
1103         }
1104         if (i >= BC_MAXSIMDUMPS) {
1105             com_err(whoami, 0, "Can't find job %s", tp);
1106             return -1;
1107         }
1108
1109         lock_Status();
1110         statusPtr = findStatus(td->dumpID);
1111
1112         if (statusPtr == 0) {
1113             com_err(whoami, 0, "Can't locate status - internal error");
1114             unlock_Status();
1115             return (-1);
1116         }
1117         statusPtr->flags |= ABORT_REQUEST;
1118         unlock_Status();
1119         return (0);
1120     }
1121     return 0;
1122 }
1123
1124 /* restore a volume or volumes */
1125 bc_VolRestoreCmd(as, arock)
1126      struct cmd_syndesc *as;
1127      char *arock;
1128 {
1129     /*
1130      * parm 0 is the new server to restore to
1131      * parm 1 is the new partition to restore to
1132      * parm 2 is volume(s) to restore
1133      * parm 3 is the new extension, if any, for the volume name.
1134      * parm 4 gives the new volume # to restore this volume as (removed).
1135      * parm 4 date is a string representing the date
1136      *
1137      * We handle four types of restores.  If old is set, then we restore the
1138      * volume with the same name and ID.  If old is not set, we allocate
1139      * a new volume ID for the restored volume.  If a new extension is specified,
1140      * we add that extension to the volume name of the restored volume.
1141      */
1142     struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1143     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1144     struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1145     struct bc_volumeDump *tvol; /* temp for same */
1146     struct sockaddr_in destServ;        /* machine to which to restore volumes */
1147     afs_int32 destPartition;    /* partition to which to restore volumes */
1148     char *tp;
1149     struct cmd_item *ti;
1150     afs_int32 code;
1151     int oldFlag;
1152     afs_int32 fromDate;
1153     char *newExt, *timeString;
1154     afs_int32 i, portRemain;
1155     afs_int32 *ports = NULL;
1156     afs_int32 portCount = 0;
1157     int dontExecute;
1158
1159     code = bc_UpdateHosts();
1160     if (code) {
1161         com_err(whoami, code, "; Can't retrieve tape hosts");
1162         return (code);
1163     }
1164
1165     /* specified other destination host */
1166     if (as->parms[0].items) {
1167         tp = as->parms[0].items->data;
1168         if (bc_ParseHost(tp, &destServ)) {
1169             com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1170             return -1;
1171         }
1172     }
1173
1174     /* specified other destination partition */
1175     if (as->parms[1].items) {
1176         tp = as->parms[1].items->data;
1177         if (bc_GetPartitionID(tp, &destPartition)) {
1178             com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1179             return -1;
1180         }
1181     }
1182
1183     for (ti = as->parms[2].items; ti; ti = ti->next) {
1184         /* build list of volume items */
1185         tvol = (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1186         if (!tvol) {
1187             com_err(whoami, BC_NOMEM, "");
1188             return BC_NOMEM;
1189         }
1190         memset(tvol, 0, sizeof(struct bc_volumeDump));
1191
1192         tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1193         if (!tvol->name) {
1194             com_err(whoami, BC_NOMEM, "");
1195             return BC_NOMEM;
1196         }
1197         strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1198         tvol->entry = &tvolumeEntry;
1199
1200         if (lastVol)
1201             lastVol->next = tvol;       /* thread onto end of list */
1202         else
1203             volsToRestore = tvol;
1204         lastVol = tvol;
1205     }
1206
1207     if (as->parms[4].items) {
1208         timeString = concatParams(as->parms[4].items);
1209         if (!timeString)
1210             return (-1);
1211
1212         code = ktime_DateToLong(timeString, &fromDate);
1213         free(timeString);
1214         if (code) {
1215             com_err(whoami, 0, "Can't parse restore date and time");
1216             com_err(whoami, 0, "%s", ktime_GetDateUsage());
1217             return code;
1218         }
1219     } else {
1220         fromDate = 0x7fffffff;  /* latest one */
1221     }
1222
1223     newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1224     oldFlag = 0;
1225
1226     /* Read all the port offsets into the ports array. The first element in the
1227      * array is for full restore and the rest are for incremental restores 
1228      */
1229     if (as->parms[5].items) {
1230         for (ti = as->parms[5].items; ti; ti = ti->next)
1231             portCount++;
1232         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1233         if (!ports) {
1234             com_err(whoami, BC_NOMEM, "");
1235             return BC_NOMEM;
1236         }
1237
1238         for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1239             ports[i] = getPortOffset(ti->data);
1240             if (ports[i] < 0)
1241                 return (BC_BADARG);
1242         }
1243     }
1244
1245     dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1246
1247     /*
1248      * Perform the call to start the restore.
1249      */
1250     code =
1251         bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1252                        &destServ, destPartition, fromDate, newExt, oldFlag,
1253                        /*parentDump */ 0, /*dumpLevel */ 0,
1254                        bc_Restorer, ports, portCount,
1255                        /*dumpSched */ 0, /*append */ 0, dontExecute);
1256     if (code)
1257         com_err(whoami, code, "; Failed to queue restore");
1258
1259     return (code);
1260 }
1261
1262 /* restore a whole partition or server */
1263
1264 /* bc_DiskRestoreCmd
1265  *      restore a whole partition
1266  * params:
1267  *      first, reqd - machine (server) to restore
1268  *      second, reqd - partition to restore
1269  *      various optional
1270  */
1271
1272 bc_DiskRestoreCmd(as, arock)
1273      struct cmd_syndesc *as;
1274      char *arock;
1275 {
1276     struct bc_volumeSet tvolumeSet;     /* temporary volume set for EvalVolumeSet call */
1277     struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1278     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1279     struct sockaddr_in destServ;        /* machine to which to restore volumes */
1280     afs_int32 destPartition;    /* partition to which to restore volumes */
1281     char *tp;
1282     afs_int32 code;
1283     int oldFlag;
1284     afs_int32 fromDate;
1285     char *newExt;
1286     afs_int32 *ports = NULL;
1287     afs_int32 portCount = 0;
1288     int dontExecute;
1289     struct bc_volumeDump *prev, *tvol, *nextvol;
1290     struct cmd_item *ti;
1291     afs_int32 i;
1292
1293     /* parm 0 is the server to restore
1294      * parm 1 is the partition to restore
1295      
1296      * parm 8 and above as in VolRestoreCmd:
1297      * parm 8 is the new server to restore to
1298      * parm 9 is the new partition to restore to
1299      */
1300
1301     code = bc_UpdateVolumeSet();
1302     if (code) {
1303         com_err(whoami, code, "; Can't retrieve volume sets");
1304         return (code);
1305     }
1306     code = bc_UpdateHosts();
1307     if (code) {
1308         com_err(whoami, code, "; Can't retrieve tape hosts");
1309         return (code);
1310     }
1311
1312     /* create a volume set corresponding to the volume pattern we've been given */
1313     memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1314     memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1315     tvolumeSet.name = "TempVolumeSet";
1316     tvolumeSet.ventries = &tvolumeEntry;
1317     tvolumeEntry.serverName = as->parms[0].items->data;
1318     tvolumeEntry.partname = as->parms[1].items->data;
1319
1320     if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1321         com_err(whoami, 0, "Can't parse partition '%s'",
1322                 tvolumeEntry.partname);
1323         return -1;
1324     }
1325
1326     if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1327         com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1328         return -1;
1329     }
1330
1331     /* specified other destination host */
1332     if (as->parms[8].items) {
1333         tp = as->parms[8].items->data;
1334         if (bc_ParseHost(tp, &destServ)) {
1335             com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1336             return -1;
1337         }
1338     } else                      /* use destination host == original host */
1339         memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1340
1341     /* specified other destination partition */
1342     if (as->parms[9].items) {
1343         tp = as->parms[9].items->data;
1344         if (bc_GetPartitionID(tp, &destPartition)) {
1345             com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1346             return -1;
1347         }
1348     } else                      /* use original partition */
1349         destPartition = tvolumeEntry.partition;
1350
1351     tvolumeEntry.name = ".*";   /* match all volumes (this should be a parameter) */
1352
1353     if (as->parms[2].items) {
1354         for (ti = as->parms[2].items; ti; ti = ti->next)
1355             portCount++;
1356         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1357         if (!ports) {
1358             com_err(whoami, BC_NOMEM, "");
1359             return BC_NOMEM;
1360         }
1361
1362         for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1363             ports[i] = getPortOffset(ti->data);
1364             if (ports[i] < 0)
1365                 return (BC_BADARG);
1366         }
1367     }
1368
1369     newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1370     dontExecute = (as->parms[11].items ? 1 : 0);        /* -n */
1371
1372     /*
1373      * Expand out the volume set into its component list of volumes, by calling VLDB server.
1374      */
1375     code =
1376         bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1377                          cstruct);
1378     if (code) {
1379         com_err(whoami, code, "; Failed to evaluate volume set");
1380         return (-1);
1381     }
1382
1383     /* Since we want only RW volumes, remove any 
1384      * BK or RO volumes from the list.
1385      */
1386     for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1387         nextvol = tvol->next;
1388         if (BackupName(tvol->name)) {
1389             if (tvol->name)
1390                 fprintf(stderr,
1391                         "Will not restore volume %s (its RW does not reside on the partition)\n",
1392                         tvol->name);
1393
1394             if (tvol == volsToRestore) {
1395                 volsToRestore = nextvol;
1396             } else {
1397                 prev->next = nextvol;
1398             }
1399             if (tvol->name)
1400                 free(tvol->name);
1401             free(tvol);
1402         } else {
1403             prev = tvol;
1404         }
1405     }
1406
1407     fromDate = 0x7fffffff;      /* last one before this date */
1408     oldFlag = 1;                /* do restore same volid (and name) */
1409
1410     /*
1411      * Perform the call to start the dump.
1412      */
1413     code =
1414         bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1415                        &destServ, destPartition, fromDate, newExt, oldFlag,
1416                        /*parentDump */ 0, /*dumpLevel */ 0,
1417                        bc_Restorer, ports, portCount,
1418                        /*dumpSched */ 0, /*append */ 0, dontExecute);
1419     if (code)
1420         com_err(whoami, code, "; Failed to queue restore");
1421
1422     return (code);
1423 }
1424
1425 /* bc_VolsetRestoreCmd
1426  *      restore a volumeset or list of volumes.
1427  */
1428
1429 bc_VolsetRestoreCmd(as, arock)
1430      struct cmd_syndesc *as;
1431      char *arock;
1432 {
1433     int oldFlag;
1434     long fromDate;
1435     char *newExt;
1436
1437     int dontExecute;
1438     afs_int32 *ports = NULL;
1439     afs_int32 portCount = 0;
1440     afs_int32 code = 0;
1441     afs_int32 portoffset = 0;
1442     char *volsetName;
1443     struct bc_volumeSet *volsetPtr;     /* Ptr to list of generated volume info */
1444     struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1445     struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1446     struct sockaddr_in destServer;      /* machine to which to restore volume */
1447     afs_int32 destPartition;    /* partition to which to restore volumes */
1448     struct cmd_item *ti;
1449     afs_int32 i;
1450
1451     code = bc_UpdateVolumeSet();
1452     if (code) {
1453         com_err(whoami, code, "; Can't retrieve volume sets");
1454         return (code);
1455     }
1456     code = bc_UpdateHosts();
1457     if (code) {
1458         com_err(whoami, code, "; Can't retrieve tape hosts");
1459         return (code);
1460     }
1461
1462     if (as->parms[0].items) {
1463         if (as->parms[1].items) {
1464             com_err(whoami, 0, "Can't have both -name and -file options");
1465             return (-1);
1466         }
1467
1468         volsetName = as->parms[0].items->data;
1469         volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1470         if (!volsetPtr) {
1471             com_err(whoami, 0,
1472                     "Can't find volume set '%s' in backup database",
1473                     volsetName);
1474             return (-1);
1475         }
1476
1477         /* Expand out the volume set into its component list of volumes. */
1478         code =
1479             bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1480                              cstruct);
1481         if (code) {
1482             com_err(whoami, code, "; Failed to evaluate volume set");
1483             return (-1);
1484         }
1485     } else if (as->parms[1].items) {
1486         FILE *fd;
1487         char line[256];
1488         char server[50], partition[50], volume[50], rest[256];
1489         long count;
1490         struct bc_volumeDump *tvol;
1491
1492         fd = fopen(as->parms[1].items->data, "r");
1493         if (!fd) {
1494             com_err(whoami, errno, "; Cannot open file '%s'",
1495                     as->parms[1].items->data);
1496             return (-1);
1497         }
1498
1499         while (fgets(line, 255, fd)) {
1500             count =
1501                 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1502
1503             if (count <= 0)
1504                 continue;
1505             if (count < 3) {
1506                 fprintf(stderr,
1507                         "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1508                 fprintf(stderr, "     %s", line);
1509                 continue;
1510             }
1511
1512             if (bc_ParseHost(server, &destServer)) {
1513                 com_err(whoami, 0, "Failed to locate host '%s'", server);
1514                 continue;
1515             }
1516
1517             if (bc_GetPartitionID(partition, &destPartition)) {
1518                 com_err(whoami, 0,
1519                         "Failed to parse destination partition '%s'",
1520                         partition);
1521                 continue;
1522             }
1523
1524             /* Allocate a volumeDump structure and link it in */
1525             tvol =
1526                 (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1527             memset(tvol, 0, sizeof(struct bc_volumeDump));
1528
1529             tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1530             if (!tvol->name) {
1531                 com_err(whoami, BC_NOMEM, "");
1532                 return BC_NOMEM;
1533             }
1534             strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1535             memcpy(&tvol->server, &destServer, sizeof(destServer));
1536             tvol->partition = destPartition;
1537
1538             if (lastVol)
1539                 lastVol->next = tvol;   /* thread onto end of list */
1540             else
1541                 volsToRestore = tvol;
1542             lastVol = tvol;
1543         }
1544         fclose(fd);
1545     } else {
1546         com_err(whoami, 0, "-name or -file option required");
1547         return (-1);
1548     }
1549
1550
1551     /* Get the port offset for the restore */
1552     if (as->parms[2].items) {
1553         for (ti = as->parms[2].items; ti; ti = ti->next)
1554             portCount++;
1555         ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1556         if (!ports) {
1557             com_err(whoami, BC_NOMEM, "");
1558             return BC_NOMEM;
1559         }
1560
1561         for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1562             ports[i] = getPortOffset(ti->data);
1563             if (ports[i] < 0)
1564                 return (BC_BADARG);
1565         }
1566     }
1567
1568     newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1569     dontExecute = (as->parms[4].items ? 1 : 0);
1570
1571     fromDate = 0x7fffffff;      /* last one before this date */
1572     oldFlag = 1;                /* do restore same volid (and name) */
1573
1574     /* Perform the call to start the restore */
1575     code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1576                           /*destserver */ 0, /*destpartition */ 0, fromDate,
1577                           newExt, oldFlag,
1578                           /*parentDump */ 0, /*dumpLevel */ 0,
1579                           bc_Restorer, ports, portCount,
1580                           /*dumpSched */ 0, /*append */ 0, dontExecute);
1581     if (code)
1582         com_err(whoami, code, "; Failed to queue restore");
1583
1584     return code;
1585 }
1586
1587 /*-----------------------------------------------------------------------------
1588  * bc_DumpCmd
1589  *
1590  * Description:
1591  *      Perform a dump of the set of volumes provided.
1592  *      user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1593  *
1594  * Arguments:
1595  *      as      : Parsed command line information.
1596  *      arock   : Ptr to misc stuff; not used.
1597  *
1598  * Returns:
1599  *      -1 on errors,
1600  *      The result of bc_StartDump() otherwise
1601  *
1602  * Environment:
1603  *      Nothing special.
1604  *
1605  * Side Effects:
1606  *      As advertised.
1607  *---------------------------------------------------------------------------
1608  */
1609 int dontExecute;
1610
1611 int
1612 bc_DumpCmd(as, arock)
1613      struct cmd_syndesc *as;
1614      char *arock;
1615 {                               /*bc_DumpCmd */
1616     static char rn[] = "bc_DumpCmd";    /*Routine name */
1617     char *dumpPath, *vsName;    /*Ptrs to various names */
1618     struct bc_volumeSet *tvs;   /*Ptr to list of generated volume info */
1619     struct bc_dumpSchedule *tds, *baseds;       /*Ptr to dump schedule node */
1620     struct bc_volumeDump *tve, *volsToDump;     /*Ptr to individual vols to be dumped */
1621     struct bc_volumeDump *ntve, *tves, *ptves, *rtves;
1622     struct budb_dumpEntry dumpEntry, de, fde;   /* dump entry */
1623     afs_uint32 d;
1624
1625     afs_int32 parent;           /* parent dump */
1626     afs_int32 level;            /* this dump's level # */
1627     afs_int32 problemFindingDump;       /* can't find parent(s) */
1628
1629     afs_int32 *portp = NULL;
1630     afs_int32 portCount = 0;
1631     afs_int32 doAt, atTime;     /* Time a timed-dump is to start at */
1632     afs_int32 length;
1633     char *timeString;
1634     int doAppend;               /* Append the dump to dump set */
1635     afs_int32 code;             /* Return code */
1636     int loadfile;               /* whether to load a file or not */
1637
1638     struct bc_dumpTask *dumpTaskPtr;    /* for dump thread */
1639     afs_int32 dumpTaskSlot;
1640     char *junk;
1641     statusP statusPtr;
1642     int r, nservers, ns, serverfound;
1643
1644     extern struct bc_dumpTask bc_dumpTasks[];
1645     extern afs_int32 bcdb_FindLastVolClone();
1646     extern afs_int32 volImageTime();
1647
1648     code = bc_UpdateDumpSchedule();
1649     if (code) {
1650         com_err(whoami, code, "; Can't retrieve dump schedule");
1651         return (code);
1652     }
1653     code = bc_UpdateVolumeSet();
1654     if (code) {
1655         com_err(whoami, code, "; Can't retrieve volume sets");
1656         return (code);
1657     }
1658     code = bc_UpdateHosts();
1659     if (code) {
1660         com_err(whoami, code, "; Can't retrieve tape hosts");
1661         return (code);
1662     }
1663
1664     /* 
1665      * Some parameters cannot be specified together
1666      * The "-file" option cannot exist with the "-volume", "-dump", 
1667      * "-portoffset", or "-append" option 
1668      */
1669     if (as->parms[6].items) {
1670         loadfile = 1;
1671         if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1672             || as->parms[4].items) {
1673             com_err(whoami, 0, "Invalid option specified with -file option");
1674             return -1;
1675         }
1676     } else {
1677         loadfile = 0;
1678         if (!as->parms[0].items || !as->parms[1].items) {
1679             com_err(whoami, 0,
1680                     "Must specify volume set name and dump level name");
1681             return -1;
1682         }
1683     }
1684
1685     /* 
1686      * Get the time we are to perform this dump
1687      */
1688     if (as->parms[3].items) {
1689         doAt = 1;
1690
1691         timeString = concatParams(as->parms[3].items);
1692         if (!timeString)
1693             return (-1);
1694
1695         /*
1696          * Now parse this string for the time to start.
1697          */
1698         code = ktime_DateToLong(timeString, &atTime);
1699         free(timeString);
1700         if (code) {
1701             com_err(whoami, 0, "Can't parse dump start date and time");
1702             com_err(whoami, 0, "%s", ktime_GetDateUsage());
1703             return (1);
1704         }
1705     } else
1706         doAt = 0;
1707
1708     dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1709
1710     /* 
1711      * If this dump is not a load file, then check the parameters.
1712      */
1713     if (!loadfile) {            /*6 */
1714         vsName = as->parms[0].items->data;      /* get volume set name */
1715         dumpPath = as->parms[1].items->data;    /* get dump path */
1716
1717         /* get the port number, if one was specified */
1718         if (as->parms[2].items) {
1719             portCount = 1;
1720             portp = (afs_int32 *) malloc(sizeof(afs_int32));
1721             if (!portp) {
1722                 com_err(whoami, BC_NOMEM, "");
1723                 return BC_NOMEM;
1724             }
1725
1726             *portp = getPortOffset(as->parms[2].items->data);
1727             if (*portp < 0)
1728                 return (BC_BADARG);
1729         }
1730
1731         doAppend = (as->parms[4].items ? 1 : 0);        /* -append */
1732
1733         /*
1734          * Get a hold of the given volume set and dump set.
1735          */
1736         tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1737         if (!tvs) {
1738             com_err(whoami, 0,
1739                     "Can't find volume set '%s' in backup database", vsName);
1740             return (-1);
1741         }
1742         baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1743         if (!baseds) {
1744             com_err(whoami, 0,
1745                     "Can't find dump schedule '%s' in backup database",
1746                     dumpPath);
1747             return (-1);
1748         }
1749
1750     }
1751
1752     /*6 */
1753     /*
1754      * If given the "-at" option, then add this to the jobs list and return 
1755      * with no error.
1756      *
1757      * Create a status node for this timed dump.
1758      * Fill in the time to dump and the cmd line for the dump leaving off
1759      * the -at option.  If the -n option is there, it is scheduled with 
1760      * the Timed dump as opposed to not scheduling the time dump at all.
1761      */
1762     if (doAt) {
1763         if (atTime < time(0)) {
1764             com_err(whoami, 0,
1765                     "Time of dump is earlier then current time - not added");
1766         } else {
1767             statusPtr = createStatusNode();
1768             lock_Status();
1769             statusPtr->scheduledDump = atTime;
1770
1771             /* Determine length of the dump command */
1772             length = 0;
1773             length += 4;        /* "dump" */
1774             if (loadfile) {
1775                 length += 6;    /* " -file" */
1776                 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1777             } else {
1778                 /* length += 11; *//* " -volumeset" */
1779                 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1780
1781                 /* length += 6; *//* " -dump" */
1782                 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1783
1784                 if (as->parms[2].items) {
1785                     /* length += 12; *//* " -portoffset" */
1786                     length += 1 + strlen(as->parms[2].items->data);     /* " <port>" */
1787                 }
1788
1789                 if (as->parms[4].items)
1790                     length += 8;        /* " -append" */
1791             }
1792
1793             if (dontExecute)
1794                 length += 3;    /* " -n" */
1795             length++;           /* end-of-line */
1796
1797             /* Allocate status block for this timed dump */
1798             sprintf(statusPtr->taskName, "Scheduled Dump");
1799             statusPtr->jobNumber = bc_jobNumber();
1800             statusPtr->scheduledDump = atTime;
1801             statusPtr->cmdLine = (char *)malloc(length);
1802             if (!statusPtr->cmdLine) {
1803                 com_err(whoami, BC_NOMEM, "");
1804                 return BC_NOMEM;
1805             }
1806
1807             /* Now reconstruct the dump command */
1808             statusPtr->cmdLine[0] = 0;
1809             strcat(statusPtr->cmdLine, "dump");
1810             if (loadfile) {
1811                 strcat(statusPtr->cmdLine, " -file");
1812                 strcat(statusPtr->cmdLine, " ");
1813                 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1814             } else {
1815                 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1816                 strcat(statusPtr->cmdLine, " ");
1817                 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1818
1819                 /* strcat(statusPtr->cmdLine, " -dump"); */
1820                 strcat(statusPtr->cmdLine, " ");
1821                 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1822
1823                 if (as->parms[2].items) {
1824                     /* strcat(statusPtr->cmdLine, " -portoffset"); */
1825                     strcat(statusPtr->cmdLine, " ");
1826                     strcat(statusPtr->cmdLine, as->parms[2].items->data);
1827                 }
1828
1829                 if (as->parms[4].items)
1830                     strcat(statusPtr->cmdLine, " -append");
1831             }
1832             if (dontExecute)
1833                 strcat(statusPtr->cmdLine, " -n");
1834
1835             printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1836             if ((atTime > ttoken.endTime) && (ttoken.endTime != NEVERDATE))
1837                 com_err(whoami, 0,
1838                         "Warning: job %d starts after expiration of AFS token",
1839                         statusPtr->jobNumber);
1840
1841             unlock_Status();
1842         }
1843
1844         return (0);
1845     }
1846
1847     /* 
1848      * Read and execute the load file if specified.  The work of reading is done
1849      * in the main routine prior the dispatch call. loadFile and dontExecute are
1850      * global variables so this can take place in main.
1851      */
1852     if (loadfile) {
1853         loadFile = (char *)malloc(strlen(as->parms[6].items->data) + 1);
1854         if (!loadFile) {
1855             com_err(whoami, BC_NOMEM, "");
1856             return BC_NOMEM;
1857         }
1858         strcpy(loadFile, as->parms[6].items->data);
1859         return 0;
1860     }
1861
1862     /* 
1863      * We are doing a real dump (no load file or timed dump).
1864      */
1865     printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1866            dumpPath);
1867
1868     /* For each dump-level above this one, see if the volumeset was dumped
1869      * at the level. We search all dump-levels since a higher dump-level
1870      * may have actually been done AFTER a lower dump level.
1871      */
1872     parent = level = problemFindingDump = 0;
1873     for (tds = baseds->parent; tds; tds = tds->parent) {
1874         /* Find the most recent dump of the volume-set at this dump-level.
1875          * Raise problem flag if didn't find a dump and a parent not yet found.
1876          */
1877         code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1878         if (code) {
1879             if (!parent)
1880                 problemFindingDump = 1; /* skipping a dump level */
1881             continue;
1882         }
1883
1884         /* We found the most recent dump at this level. Now check
1885          * if we should use it by seeing if its full dump hierarchy 
1886          * exists. If it doesn't, we don't want to base our incremental
1887          * off of this dump. 
1888          */
1889         if (!parent || (dumpEntry.id > parent)) {
1890             /* Follow the parent dumps to see if they are all there */
1891             for (d = dumpEntry.parent; d; d = de.parent) {
1892                 code = bcdb_FindDumpByID(d, &de);
1893                 if (code)
1894                     break;
1895             }
1896
1897             /* If we found the entire level, remember it. Otherwise raise flag.
1898              * If we had already found a dump, raise the problem flag.
1899              */
1900             if (!d && !code) {
1901                 if (parent)
1902                     problemFindingDump = 1;
1903                 parent = dumpEntry.id;
1904                 level = dumpEntry.level + 1;
1905                 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1906             } else {
1907                 /* Dump hierarchy not complete so can't base off the latest */
1908                 problemFindingDump = 1;
1909             }
1910         }
1911     }
1912
1913     /* If the problemflag was raise, it means we are not doing the 
1914      * dump at the level we requested it be done at.
1915      */
1916     if (problemFindingDump) {
1917         com_err(whoami, 0,
1918                 "Warning: Doing level %d dump due to missing higher-level dumps",
1919                 level);
1920         if (parent) {
1921             printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1922         }
1923     } else if (parent) {
1924         printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1925     }
1926
1927     /* Expand out the volume set into its component list of volumes. */
1928     code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1929     if (code) {
1930         com_err(whoami, code, "; Failed to evaluate volume set");
1931         return (-1);
1932     }
1933     if (!volsToDump) {
1934         printf("No volumes to dump\n");
1935         return (0);
1936     }
1937
1938     /* Determine what the clone time of the volume was when it was
1939      * last dumped (tve->date). This is the time from when an
1940      * incremental should be done (remains zero if a full dump).
1941      */
1942     if (parent) {
1943         for (tve = volsToDump; tve; tve = tve->next) {
1944             code = bcdb_FindClone(parent, tve->name, &tve->date);
1945             if (code)
1946                 tve->date = 0;
1947
1948             /* Get the time the volume was last cloned and see if the volume has
1949              * changed since then. Only do this when the "-n" flag is specified
1950              * because butc will get the cloneDate at time of dump.
1951              */
1952             if (dontExecute) {
1953                 code =
1954                     volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1955                                  tve->vid, tve->volType, &tve->cloneDate);
1956                 if (code)
1957                     tve->cloneDate = 0;
1958
1959                 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1960                     com_err(whoami, 0,
1961                             "Warning: Timestamp on volume %s unchanged from previous dump",
1962                             tve->name);
1963                 }
1964             }
1965         }
1966     }
1967
1968     if (dontExecute)
1969         printf("Would have dumped the following volumes:\n");
1970     else
1971         printf("Preparing to dump the following volumes:\n");
1972     for (tve = volsToDump; tve; tve = tve->next) {
1973         printf("\t%s (%u)\n", tve->name, tve->vid);
1974     }
1975     if (dontExecute)
1976         return (0);
1977
1978     code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1979                           /*destServer */ 0, /*destPartition */ 0,
1980                           /*fromDate */ 0,
1981                           /*newExt */ 0, /*oldFlag */ 0,
1982                           parent, level, bc_Dumper, portp, /*portCount */ 1,
1983                           baseds, doAppend, dontExecute);
1984     if (code)
1985         com_err(whoami, code, "; Failed to queue dump");
1986
1987     return (code);
1988 }                               /*bc_DumpCmd */
1989
1990
1991 /* bc_QuitCmd
1992  *      terminate the backup process. Insists that that all running backup
1993  *      jobs be terminated before it will quit
1994  * parameters:
1995  *      ignored
1996  */
1997
1998 bc_QuitCmd(as, arock)
1999      struct cmd_syndesc *as;
2000      char *arock;
2001 {
2002     int i;
2003     struct bc_dumpTask *td;
2004     extern dlqlinkT statusHead;
2005     dlqlinkP ptr;
2006     statusP statusPtr;
2007
2008     /* Check the status list for outstanding jobs */
2009     lock_Status();
2010     for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
2011          ptr = ptr->dlq_next) {
2012         statusPtr = (statusP) ptr;
2013         if (!(statusPtr->flags & ABORT_REQUEST)) {
2014             unlock_Status();
2015             com_err(whoami, 0, "Job %d still running (and not aborted)",
2016                     statusPtr->jobNumber);
2017             com_err(whoami, 0,
2018                     "You must at least 'kill' all running jobs before quitting");
2019             return -1;
2020         }
2021     }
2022     unlock_Status();
2023
2024     /* A job still being initialized (but no status structure or job number since it
2025      * has not been handed to a butc process yet)
2026      */
2027     for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
2028         if (td->flags & BC_DI_INUSE) {
2029             com_err(whoami, 0, "A job is still running");
2030             com_err(whoami, 0,
2031                     "You must at least 'kill' all running jobs before quitting");
2032             return -1;
2033         }
2034     }
2035
2036 #ifdef AFS_NT40_ENV
2037     /* close the all temp text files before quitting */
2038     for (i = 0; i < TB_NUM; i++)
2039         bc_closeTextFile(&bc_globalConfig->configText[i],
2040                          &bc_globalConfig->tmpTextFileNames[i][0]);
2041 #endif
2042     exit(lastTaskCode);
2043 }
2044
2045 /* bc_LabelTapeCmd
2046  *      Labels a tape i.e. request the tape coordinator to perform this
2047  *      operation
2048  */
2049
2050 bc_LabelTapeCmd(as, arock)
2051      struct cmd_syndesc *as;
2052      char *arock;
2053 {
2054     char *tapename = 0, *pname = 0;
2055     afs_int32 size;
2056     afs_int32 code;
2057     afs_int32 port = 0;
2058
2059     code = bc_UpdateHosts();
2060     if (code) {
2061         com_err(whoami, code, "; Can't retrieve tape hosts");
2062         return (code);
2063     }
2064
2065     if (as->parms[0].items) {   /* -name */
2066         tapename = as->parms[0].items->data;
2067         if (strlen(tapename) >= TC_MAXTAPELEN) {
2068             com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2069             return -1;
2070         }
2071     }
2072
2073     if (as->parms[3].items) {   /* -pname */
2074         if (tapename) {
2075             com_err(whoami, 0, "Can only specify -name or -pname");
2076             return -1;
2077         }
2078         pname = as->parms[3].items->data;
2079         if (strlen(pname) >= TC_MAXTAPELEN) {
2080             com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2081             return -1;
2082         }
2083     }
2084
2085     if (as->parms[1].items) {
2086         size = bc_FloatATOI(as->parms[1].items->data);
2087         if (size == -1) {
2088             com_err(whoami, 0, "Bad syntax for tape size '%s'",
2089                     as->parms[1].items->data);
2090             return -1;
2091         }
2092     } else
2093         size = 0;
2094
2095     if (as->parms[2].items) {
2096         port = getPortOffset(as->parms[2].items->data);
2097         if (port < 0)
2098             return (BC_BADARG);
2099     }
2100
2101     code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2102     if (code)
2103         return code;
2104     return 0;
2105 }
2106
2107 /* bc_ReadLabelCmd
2108  *      read the label on a tape
2109  * params:
2110  *      optional port number
2111  */
2112
2113 bc_ReadLabelCmd(as, arock)
2114      struct cmd_syndesc *as;
2115      char *arock;
2116 {
2117     afs_int32 code;
2118     afs_int32 port = 0;
2119
2120     code = bc_UpdateHosts();
2121     if (code) {
2122         com_err(whoami, code, "; Can't retrieve tape hosts");
2123         return (code);
2124     }
2125
2126     if (as->parms[0].items) {
2127         port = getPortOffset(as->parms[0].items->data);
2128         if (port < 0)
2129             return (BC_BADARG);
2130     }
2131
2132     code = bc_ReadLabel(bc_globalConfig, port);
2133     if (code)
2134         return code;
2135     return 0;
2136 }
2137
2138 /* bc_ScanDumpsCmd
2139  *      read content information from dump tapes, and if user desires,
2140  *      add it to the database
2141  */
2142
2143 bc_ScanDumpsCmd(as, arock)
2144      struct cmd_syndesc *as;
2145      char *arock;
2146 {
2147     afs_int32 port = 0;
2148     afs_int32 dbAddFlag = 0;
2149     afs_int32 code;
2150
2151     code = bc_UpdateHosts();
2152     if (code) {
2153         com_err(whoami, code, "; Can't retrieve tape hosts");
2154         return (code);
2155     }
2156
2157     /* check for flag */
2158     if (as->parms[0].items != 0) {      /* add scan to database */
2159         dbAddFlag++;
2160     }
2161
2162     /* check for port */
2163     if (as->parms[1].items) {
2164         port = getPortOffset(as->parms[1].items->data);
2165         if (port < 0)
2166             return (BC_BADARG);
2167     }
2168
2169     code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2170     return (code);
2171 }
2172
2173 /* bc_ParseExpiration
2174  *
2175  * Notes:
2176  *      dates are specified as absolute or relative, the syntax is:
2177  *      absolute:       at %d/%d/%d [%d:%d]     where [..] is optional
2178  *      relative:       in [%dy][%dm][%dd]      where at least one component
2179  *                                              must be specified
2180  */
2181
2182 afs_int32
2183 bc_ParseExpiration(paramPtr, expType, expDate)
2184      struct cmd_parmdesc *paramPtr;
2185      afs_int32 *expType;
2186      afs_int32 *expDate;
2187 {
2188     struct cmd_item *itemPtr;
2189     struct ktime_date kt;
2190     char *dateString = 0;
2191     afs_int32 code = 0;
2192
2193     *expType = BC_NO_EXPDATE;
2194     *expDate = 0;
2195
2196     if (!paramPtr->items)
2197         ERROR(0);               /* no expiration specified */
2198
2199     /* some form of expiration date specified. First validate the prefix */
2200     itemPtr = paramPtr->items;
2201
2202     if (strcmp(itemPtr->data, "at") == 0) {
2203         *expType = BC_ABS_EXPDATE;
2204
2205         dateString = concatParams(itemPtr->next);
2206         if (!dateString)
2207             ERROR(1);
2208
2209         code = ktime_DateToLong(dateString, expDate);
2210         if (code)
2211             ERROR(1);
2212     } else if (strcmp(itemPtr->data, "in") == 0) {
2213         *expType = BC_REL_EXPDATE;
2214
2215         dateString = concatParams(itemPtr->next);
2216         if (!dateString)
2217             ERROR(1);
2218
2219         code = ParseRelDate(dateString, &kt);
2220         if (code)
2221             ERROR(1);
2222         *expDate = ktimeRelDate_ToLong(&kt);
2223     } else {
2224         dateString = concatParams(itemPtr);
2225         if (!dateString)
2226             ERROR(1);
2227
2228         if (ktime_DateToLong(dateString, expDate) == 0) {
2229             *expType = BC_ABS_EXPDATE;
2230             code = ktime_DateToLong(dateString, expDate);
2231             if (code)
2232                 ERROR(1);
2233         } else if (ParseRelDate(dateString, &kt) == 0) {
2234             *expType = BC_REL_EXPDATE;
2235             *expDate = ktimeRelDate_ToLong(&kt);
2236         } else {
2237             ERROR(1);
2238         }
2239     }
2240
2241   error_exit:
2242     if (dateString)
2243         free(dateString);
2244     return (code);
2245 }
2246
2247 /* database lookup command and routines */
2248
2249 /* bc_dblookupCmd
2250  *      Currently a single option, volumename to search for. Reports
2251  *      all dumps containing the specified volume
2252  */
2253
2254 bc_dblookupCmd(as, arock)
2255      struct cmd_syndesc *as;
2256      char *arock;
2257 {
2258     struct cmd_item *ciptr;
2259     afs_int32 code;
2260
2261     ciptr = as->parms[0].items;
2262     if (ciptr == 0)             /* no argument specified */
2263         return (-1);
2264
2265     code = DBLookupByVolume(ciptr->data);
2266     return (code);
2267 }
2268
2269
2270
2271 /* for ubik version */
2272
2273 bc_dbVerifyCmd(as, arock)
2274      struct cmd_syndesc *as;
2275      char *arock;
2276 {
2277     afs_int32 status;
2278     afs_int32 orphans;
2279     afs_int32 host;
2280
2281     struct hostent *hostPtr;
2282     int detail;
2283     afs_int32 code = 0;
2284
2285     extern struct udbHandleS udbHandle;
2286
2287     detail = (as->parms[0].items ? 1 : 0);      /* print more details */
2288
2289     code =
2290         ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0, &status, &orphans,
2291                   &host);
2292
2293     if (code) {
2294         com_err(whoami, code, "; Unable to verify database");
2295         return (-1);
2296     }
2297
2298     /* verification call succeeded */
2299
2300     if (status == 0)
2301         printf("Database OK\n");
2302     else
2303         com_err(whoami, status, "; Database is NOT_OK");
2304
2305     if (detail) {
2306         printf("Orphan blocks %d\n", orphans);
2307
2308         if (!host)
2309             printf("Unable to lookup host id\n");
2310         else {
2311             hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2312             if (hostPtr == 0)
2313                 printf("Database checker was %d.%d.%d.%d\n",
2314                        ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2315                        ((host & 0xFF00) >> 8), (host & 0xFF));
2316             else
2317                 printf("Database checker was %s\n", hostPtr->h_name);
2318         }
2319     }
2320     return ((status ? -1 : 0));
2321 }
2322
2323 /* deleteDump:
2324  * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2325  */
2326 deleteDump(dumpid, port, force)
2327      afs_uint32 dumpid;         /* The dumpid to delete */
2328      afs_int32 port;            /* port==-1 means don't go to butc */
2329      afs_int32 force;
2330 {
2331     afs_int32 code = 0, tcode;
2332     struct budb_dumpEntry dumpEntry;
2333     struct rx_connection *tconn = 0;
2334     afs_int32 i, taskflag, xbsadump;
2335     statusP statusPtr = 0;
2336     budb_dumpsList dumps;
2337     afs_uint32 taskId;
2338
2339     /* If the port is set, we will try to send a delete request to the butc */
2340     if (port >= 0) {
2341         tcode = bc_UpdateHosts();
2342         if (tcode) {
2343             com_err(whoami, tcode, "; Can't retrieve tape hosts");
2344             ERROR(tcode);
2345         }
2346
2347         /* Find the dump in the backup database */
2348         tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2349         if (tcode) {
2350             com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2351                     dumpid);
2352             ERROR(tcode);
2353         }
2354         xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2355
2356         /* If dump is to an XBSA server, connect to butc and send it
2357          * the dump to delete. Butc will contact the XBSA server.
2358          * The dump will not be an appended dump because XBSA butc 
2359          * does not support the append option.
2360          */
2361         if (xbsadump && dumpEntry.nVolumes) {
2362             tcode = ConnectButc(bc_globalConfig, port, &tconn);
2363             if (tcode)
2364                 ERROR(tcode);
2365
2366             tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2367             if (tcode) {
2368                 if (tcode == RXGEN_OPCODE)
2369                     tcode = BC_VERSIONFAIL;
2370                 com_err(whoami, tcode,
2371                         "; Unable to delete dumpID %u via butc", dumpid);
2372                 ERROR(tcode);
2373             }
2374
2375             statusPtr = createStatusNode();
2376             lock_Status();
2377             statusPtr->taskId = taskId;
2378             statusPtr->port = port;
2379             statusPtr->jobNumber = bc_jobNumber();
2380             statusPtr->flags |= (SILENT | NOREMOVE);    /* No msg & keep statusPtr */
2381             statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2382             sprintf(statusPtr->taskName, "DeleteDump");
2383             unlock_Status();
2384
2385             /* Wait for task to finish */
2386             taskflag = waitForTask(taskId);
2387             if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2388                 com_err(whoami, BUTX_DELETEOBJFAIL,
2389                         "; Unable to delete dumpID %u via butc", dumpid);
2390                 ERROR(BUTX_DELETEOBJFAIL);      /* the task failed */
2391             }
2392         }
2393     }
2394
2395   error_exit:
2396     if (statusPtr)
2397         deleteStatusNode(statusPtr);    /* Clean up statusPtr - because NOREMOVE */
2398     if (tconn)
2399         rx_DestroyConnection(tconn);    /* Destroy the connection */
2400
2401     /* Remove the dump from the backup database */
2402     if (!code || force) {
2403         dumps.budb_dumpsList_len = 0;
2404         dumps.budb_dumpsList_val = 0;
2405
2406         tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2407         if (tcode) {
2408             com_err(whoami, tcode,
2409                     "; Unable to delete dumpID %u from database", dumpid);
2410             dumps.budb_dumpsList_len = 0;
2411             if (!code)
2412                 code = tcode;
2413         }
2414
2415         /* Display the dumps that were deleted - includes appended dumps */
2416         for (i = 0; i < dumps.budb_dumpsList_len; i++)
2417             printf("     %u%s\n", dumps.budb_dumpsList_val[i],
2418                    (i > 0) ? " Appended Dump" : "");
2419         if (dumps.budb_dumpsList_val)
2420             free(dumps.budb_dumpsList_val);
2421     }
2422
2423     return code;
2424 }
2425
2426 /* bc_deleteDumpCmd
2427  *      Delete a specified dump from the database
2428  * entry:
2429  *      dump id - single required arg as param 0.
2430  */
2431
2432 bc_deleteDumpCmd(as, arock)
2433      struct cmd_syndesc *as;
2434      char *arock;
2435 {
2436     afs_uint32 dumpid;
2437     afs_int32 code = 0;
2438     afs_int32 rcode = 0;
2439     afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2440     struct cmd_item *ti;
2441     afs_uint32 fromTime = 0, toTime = 0, havetime = 0;
2442     char *timeString;
2443     budb_dumpsList dumps, flags;
2444     int i;
2445     afs_int32 port = -1, dbonly = 0, force;
2446     afs_uint32 taskid;
2447
2448     /* Must specify at least one of -dumpid, -from, or -to */
2449     if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2450         && !as->parms[4].items) {
2451         com_err(whoami, 0, "Must specify at least one field");
2452         return (-1);
2453     }
2454
2455     /* Must have -to option with -from option */
2456     if (as->parms[1].items && !as->parms[2].items) {
2457         com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2458         return (-1);
2459     }
2460
2461     /* Get the time to delete from */
2462     if (as->parms[1].items) {   /* -from */
2463         timeString = concatParams(as->parms[1].items);
2464         if (!timeString)
2465             return (-1);
2466
2467         /*
2468          * Now parse this string for the time to start.
2469          */
2470         code = ktime_DateToLong(timeString, &fromTime);
2471         free(timeString);
2472         if (code) {
2473             com_err(whoami, 0, "Can't parse 'from' date and time");
2474             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2475             return (-1);
2476         }
2477         havetime = 1;
2478     }
2479
2480     port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0);  /* -port */
2481     if (as->parms[5].items)     /* -dbonly */
2482         port = -1;
2483
2484     force = (as->parms[6].items ? 1 : 0);
2485
2486     havegroupid = (as->parms[4].items ? 1 : 0);
2487     if (havegroupid)
2488         groupId = atoi(as->parms[4].items->data);
2489
2490     noexecute = (as->parms[7].items ? 1 : 0);
2491
2492     /* Get the time to delete to */
2493     if (as->parms[2].items) {   /* -to */
2494         timeString = concatParams(as->parms[2].items);
2495         if (!timeString)
2496             return (-1);
2497
2498         /*
2499          * Now parse this string for the time to start. Simce
2500          * times are at minute granularity, add 59 seconds.
2501          */
2502         code = ktime_DateToLong(timeString, &toTime);
2503         free(timeString);
2504         if (code) {
2505             com_err(whoami, 0, "Can't parse 'to' date and time");
2506             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2507             return (-1);
2508         }
2509         toTime += 59;
2510         havetime = 1;
2511     }
2512
2513     if (fromTime > toTime) {
2514         com_err(whoami, 0,
2515                 "'-from' date/time cannot be later than '-to' date/time");
2516         return (-1);
2517     }
2518
2519     /* Remove speicific dump ids - if any */
2520     printf("The following dumps %s deleted:\n",
2521            (noexecute ? "would have been" : "were"));
2522     for (ti = as->parms[0].items; ti != 0; ti = ti->next) {     /* -dumpid */
2523         dumpid = atoi(ti->data);
2524         if (!noexecute) {
2525             code = deleteDump(dumpid, port, force);
2526         } else {
2527             printf("     %u\n", dumpid);
2528         }
2529     }
2530
2531     /*
2532      * Now remove dumps between to and from dates.
2533      */
2534     if (havegroupid || havetime) {
2535         dumps.budb_dumpsList_len = 0;
2536         dumps.budb_dumpsList_val = 0;
2537         flags.budb_dumpsList_len = 0;
2538         flags.budb_dumpsList_val = 0;
2539         sflags = 0;
2540         if (havegroupid)
2541             sflags |= BUDB_OP_GROUPID;
2542         if (havetime)
2543             sflags |= BUDB_OP_DATES;
2544
2545         code =
2546             bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2547         if (code) {
2548             com_err(whoami, code,
2549                     "; Error while deleting dumps from %u to %u", fromTime,
2550                     toTime);
2551             rcode = -1;
2552         }
2553
2554         for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2555             if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2556                 continue;
2557
2558             if (!noexecute) {
2559                 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2560                     continue;
2561                 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2562                 /* Ignore code and continue */
2563             } else {
2564                 printf("     %u%s%s\n", dumps.budb_dumpsList_val[i],
2565                        (flags.
2566                         budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2567                        " Appended Dump" : "",
2568                        (flags.
2569                         budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2570                        " Database Dump" : "");
2571
2572             }
2573         }
2574
2575         if (dumps.budb_dumpsList_val)
2576             free(dumps.budb_dumpsList_val);
2577         dumps.budb_dumpsList_len = 0;
2578         dumps.budb_dumpsList_val = 0;
2579         if (flags.budb_dumpsList_val)
2580             free(flags.budb_dumpsList_val);
2581         flags.budb_dumpsList_len = 0;
2582         flags.budb_dumpsList_val = 0;
2583     }
2584
2585     return (rcode);
2586 }
2587
2588 bc_saveDbCmd(as, arock)
2589      struct cmd_syndesc *as;
2590      char *arock;
2591 {
2592     struct rx_connection *tconn;
2593     afs_int32 portOffset = 0;
2594     statusP statusPtr;
2595     afs_uint32 taskId;
2596     afs_int32 code;
2597     afs_uint32 toTime;
2598     char *timeString;
2599
2600     code = bc_UpdateHosts();
2601     if (code) {
2602         com_err(whoami, code, "; Can't retrieve tape hosts");
2603         return (code);
2604     }
2605
2606     if (as->parms[0].items) {
2607         portOffset = getPortOffset(as->parms[0].items->data);
2608         if (portOffset < 0)
2609             return (BC_BADARG);
2610     }
2611
2612     /* Get the time to delete to */
2613     if (as->parms[1].items) {
2614         timeString = concatParams(as->parms[1].items);
2615         if (!timeString)
2616             return (-1);
2617
2618         /*
2619          * Now parse this string for the time. Since
2620          * times are at minute granularity, add 59 seconds.
2621          */
2622         code = ktime_DateToLong(timeString, &toTime);
2623         free(timeString);
2624         if (code) {
2625             com_err(whoami, 0, "Can't parse '-archive' date and time");
2626             com_err(whoami, 0, "%s", ktime_GetDateUsage());
2627             return (-1);
2628         }
2629         toTime += 59;
2630     } else
2631         toTime = 0;
2632
2633     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2634     if (code)
2635         return (code);
2636
2637     code = TC_SaveDb(tconn, toTime, &taskId);
2638     if (code) {
2639         com_err(whoami, code, "; Failed to save database");
2640         goto exit;
2641     }
2642
2643     /* create status monitor block */
2644     statusPtr = createStatusNode();
2645     lock_Status();
2646     statusPtr->taskId = taskId;
2647     statusPtr->port = portOffset;
2648     statusPtr->jobNumber = bc_jobNumber();
2649     statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2650     sprintf(statusPtr->taskName, "SaveDb");
2651     unlock_Status();
2652
2653   exit:
2654     rx_DestroyConnection(tconn);
2655     return (code);
2656 }
2657
2658 bc_restoreDbCmd(as, arock)
2659      struct cmd_syndesc *as;
2660      char *arock;
2661 {
2662     struct rx_connection *tconn;
2663     afs_int32 portOffset = 0;
2664     statusP statusPtr;
2665     afs_uint32 taskId;
2666     afs_int32 code;
2667
2668     code = bc_UpdateHosts();
2669     if (code) {
2670         com_err(whoami, code, "; Can't retrieve tape hosts");
2671         return (code);
2672     }
2673
2674     if (as->parms[0].items) {
2675         portOffset = getPortOffset(as->parms[0].items->data);
2676         if (portOffset < 0)
2677             return (BC_BADARG);
2678     }
2679
2680     code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2681     if (code)
2682         return (code);
2683
2684     code = TC_RestoreDb(tconn, &taskId);
2685     if (code) {
2686         com_err(whoami, code, "; Failed to restore database");
2687         goto exit;
2688     }
2689
2690     /* create status monitor block */
2691     statusPtr = createStatusNode();
2692     lock_Status();
2693     statusPtr->taskId = taskId;
2694     statusPtr->port = portOffset;
2695     statusPtr->jobNumber = bc_jobNumber();
2696     statusPtr->flags &= ~STARTING;      /* clearstatus to examine */
2697     sprintf(statusPtr->taskName, "RestoreDb");
2698     unlock_Status();
2699
2700   exit:
2701     rx_DestroyConnection(tconn);
2702     return (code);
2703 }
2704
2705 /* ----------------------------------
2706  * supporting routines for database examination 
2707  * ----------------------------------
2708  */
2709
2710 /* structures and defines for DBLookupByVolume */
2711
2712 #define DBL_MAX_VOLUMES 20      /* max. for each dump */
2713
2714 /* dumpedVol - saves interesting information so that we can print it out
2715  *      later
2716  */
2717
2718 struct dumpedVol {
2719     struct dumpedVol *next;
2720     afs_int32 dumpID;
2721     afs_int32 initialDumpID;
2722     char tapeName[BU_MAXTAPELEN];
2723     afs_int32 level;
2724     afs_int32 parent;
2725     afs_int32 createTime;
2726     afs_int32 incTime;          /* actually the clone time */
2727 };
2728
2729 /* -----------------------------------------
2730  * routines for examining the database
2731  * -----------------------------------------
2732  */
2733
2734 /* DBLookupByVolume
2735  *      Lookup the volumename in the backup database and print the results
2736  * entry:
2737  *      volumeName - volume to lookup
2738  */
2739
2740 DBLookupByVolume(volumeName)
2741      char *volumeName;
2742 {
2743     struct budb_dumpEntry dumpEntry;
2744     struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2745     afs_int32 numEntries;
2746     afs_int32 tapedumpid;
2747     afs_int32 last, next;
2748
2749     struct dumpedVol *dvptr = 0;
2750     struct dumpedVol *tempPtr = 0;
2751     afs_int32 code = 0;
2752     int i, pass;
2753     char vname[BU_MAXNAMELEN];
2754     char ds[50];
2755
2756     for (pass = 0; pass < 2; pass++) {
2757         /*p */
2758         /* On second pass, search for backup volume */
2759         if (pass == 1) {
2760             if (!BackupName(volumeName)) {
2761                 strcpy(vname, volumeName);
2762                 strcat(vname, ".backup");
2763                 volumeName = vname;
2764             } else {
2765                 continue;
2766             }
2767         }
2768
2769         last = next = 0;
2770         while (next != -1) {    /*w */
2771             code =
2772                 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2773                                   DBL_MAX_VOLUMES, &numEntries);
2774             if (code)
2775                 break;
2776
2777             /* add the volumes to the list */
2778             for (i = 0; i < numEntries; i++) {  /*f */
2779                 struct dumpedVol *insPtr, **prevPtr;
2780
2781                 tempPtr =
2782                     (struct dumpedVol *)malloc(sizeof(struct dumpedVol));
2783                 if (!tempPtr)
2784                     ERROR(BC_NOMEM);
2785
2786                 memset(tempPtr, 0, sizeof(*tempPtr));
2787                 tempPtr->incTime = volumeEntry[i].clone;
2788                 tempPtr->dumpID = volumeEntry[i].dump;
2789                 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2790                         BU_MAXTAPELEN);
2791
2792                 /* check if we need to null terminate it - just for safety */
2793                 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2794                     tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2795
2796                 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2797                 if (code) {
2798                     free(tempPtr);
2799                     ERROR(code);
2800                 }
2801
2802                 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2803                 tempPtr->parent = dumpEntry.parent;
2804                 tempPtr->level = dumpEntry.level;
2805                 tempPtr->createTime = dumpEntry.created;
2806
2807                 /* add volume to list in reverse chronological order */
2808                 prevPtr = &dvptr;
2809                 insPtr = dvptr;
2810
2811                 while ((insPtr != 0)
2812                        && (insPtr->createTime > tempPtr->createTime)
2813                     ) {
2814                     prevPtr = &insPtr->next;
2815                     insPtr = insPtr->next;
2816                 }
2817
2818                 /* now at the right place - insert the block */
2819                 tempPtr->next = *prevPtr;
2820                 *prevPtr = tempPtr;
2821             }                   /*f */
2822
2823             last = next;
2824         }                       /*w */
2825     }                           /*p */
2826
2827     if (dvptr) {
2828         printf
2829             ("DumpID    lvl parentID creation date     clone date       tape name\n");
2830         for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2831             /* For the user, the tape name is its name and initial dump id */
2832             tapedumpid =
2833                 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2834                  dumpID);
2835
2836             /* beware the static items in compactDateString */
2837             compactDateString(&tempPtr->createTime, ds, 50);
2838             printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2839                    tempPtr->parent, ds);
2840             compactDateString(&tempPtr->incTime, ds, 50);
2841             printf("  %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2842         }
2843         code = 0;
2844     }
2845
2846   error_exit:
2847     for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2848         dvptr = dvptr->next;
2849         free(tempPtr);
2850     }
2851
2852     if (code)
2853         com_err(whoami, code, "");
2854     return (code);
2855 }
2856
2857 /* structures for dumpInfo */
2858
2859 struct volumeLink {
2860     struct volumeLink *nextVolume;
2861     struct budb_volumeEntry volumeEntry;
2862 };
2863
2864 struct tapeLink {
2865     struct tapeLink *nextTape;
2866     struct budb_tapeEntry tapeEntry;
2867     struct volumeLink *firstVolume;
2868 };
2869
2870
2871 /* dumpInfo
2872  *      print information about a dump and all its tapes and volumes.
2873  */
2874
2875 afs_int32
2876 dumpInfo(dumpid, detailFlag)
2877      afs_int32 dumpid;
2878      afs_int32 detailFlag;
2879 {
2880     struct budb_dumpEntry dumpEntry;
2881     struct tapeLink *head = 0;
2882     struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2883     struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2884
2885     budb_volumeList vl;
2886     afs_int32 last, next, dbTime;
2887     afs_int32 tapedumpid;
2888     afs_int32 numTapes;
2889
2890     int tapeNumber;
2891     int i;
2892     int dbDump;
2893     afs_int32 code = 0;
2894     char ds[50];
2895
2896     extern struct udbHandleS udbHandle;
2897
2898     tapeLinkPtr = 0;
2899     lastTapeLinkPtr = 0;
2900     volumeLinkPtr = 0;
2901     lastVolumeLinkPtr = 0;
2902
2903     /* first get information about the dump */
2904
2905     code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2906     if (code)
2907         ERROR(code);
2908
2909     /* For the user, the tape name is its name and initial dump id */
2910     tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2911
2912     /* Is this a database dump id or not */
2913     if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2914         dbDump = 1;
2915     else
2916         dbDump = 0;
2917
2918     /* print out the information about the dump */
2919     if (detailFlag) {
2920         printf("\nDump\n");
2921         printf("----\n");
2922         printDumpEntry(&dumpEntry);
2923     } else {
2924         if (dbDump)
2925             printf("Dump: id %u, created: %s\n", dumpEntry.id,
2926                    ctime(&dumpEntry.created));
2927         else
2928             printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2929                    dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2930                    ctime(&dumpEntry.created));
2931     }
2932
2933     if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2934         && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2935         printf("Backup Service: TSM: Server: %s\n",
2936                dumpEntry.tapes.tapeServer);
2937     }
2938
2939     /* now get the list of tapes */
2940     for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) {        /*f */
2941         tapeLinkPtr = (struct tapeLink *)malloc(sizeof(struct tapeLink));
2942         if (!tapeLinkPtr) {
2943             com_err(whoami, BC_NOMEM, "");
2944             ERROR(BC_NOMEM);
2945         }
2946
2947         memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2948         code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2949         if (code) {
2950             code = 0;
2951             free(tapeLinkPtr);
2952             continue;
2953         }
2954
2955         /* add this tape to  previous chain */
2956         if (lastTapeLinkPtr) {
2957             lastTapeLinkPtr->nextTape = tapeLinkPtr;
2958             lastTapeLinkPtr = tapeLinkPtr;
2959         }
2960
2961         if (head == 0) {
2962             head = tapeLinkPtr;
2963             lastTapeLinkPtr = head;
2964         }
2965
2966         next = 0;
2967         while (next != -1) {    /*wn */
2968             vl.budb_volumeList_len = 0;
2969             vl.budb_volumeList_val = 0;
2970             last = next;
2971
2972             /* now get all the volumes in this dump. */
2973             code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name,     /* tape name */
2974                                           dumpid,       /* dumpid (not initial dumpid) */
2975                                           0,    /* end */
2976                                           last, /* last */
2977                                           &next,        /* nextindex */
2978                                           &dbTime,      /* update time */
2979                                           &vl);
2980
2981             if (code) {
2982                 if (code == BUDB_ENDOFLIST) {   /* 0 volumes on tape */
2983                     code = 0;
2984                     break;
2985                 }
2986                 ERROR(code);
2987             }
2988
2989             for (i = 0; i < vl.budb_volumeList_len; i++) {
2990                 link = &tapeLinkPtr->firstVolume;
2991
2992                 volumeLinkPtr =
2993                     (struct volumeLink *)malloc(sizeof(struct volumeLink));
2994                 if (!volumeLinkPtr) {
2995                     com_err(whoami, BC_NOMEM, "");
2996                     ERROR(BC_NOMEM);
2997                 }
2998                 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
2999
3000                 memcpy(&volumeLinkPtr->volumeEntry,
3001                        &vl.budb_volumeList_val[i],
3002                        sizeof(struct budb_volumeEntry));
3003
3004                 /* now insert it onto the right place */
3005                 while ((*link != 0)
3006                        && (volumeLinkPtr->volumeEntry.position >
3007                            (*link)->volumeEntry.position)) {
3008                     link = &((*link)->nextVolume);
3009                 }
3010
3011                 /* now link it in */
3012                 volumeLinkPtr->nextVolume = *link;
3013                 *link = volumeLinkPtr;
3014             }
3015
3016             if (vl.budb_volumeList_val)
3017                 free(vl.budb_volumeList_val);
3018         }                       /*wn */
3019     }                           /*f */
3020
3021     for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
3022         if (detailFlag) {
3023             printf("\nTape\n");
3024             printf("----\n");
3025             printTapeEntry(&tapeLinkPtr->tapeEntry);
3026         } else {
3027             printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
3028                    tapedumpid);
3029             printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
3030             compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
3031             printf("created %16s", ds);
3032             if (tapeLinkPtr->tapeEntry.expires != 0) {
3033                 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3034                 printf(", expires %16s", ds);
3035             }
3036             printf("\n\n");
3037         }
3038
3039         /* print out all the volumes */
3040
3041         /* print header for volume listing - db dumps have no volumes */
3042         if ((detailFlag == 0) && !dbDump)
3043             printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3044                    "Volume");
3045
3046         for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
3047              volumeLinkPtr = volumeLinkPtr->nextVolume) {
3048             if (detailFlag) {
3049                 printf("\nVolume\n");
3050                 printf("------\n");
3051                 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3052             } else {
3053                 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3054                     printf("%4d %s %10u %16s\n",
3055                            volumeLinkPtr->volumeEntry.position, ds,
3056                            volumeLinkPtr->volumeEntry.nBytes,
3057                            volumeLinkPtr->volumeEntry.name);
3058             }
3059         }
3060     }
3061
3062   error_exit:
3063     if (code)
3064         com_err("dumpInfo", code, "; Can't get dump information");
3065
3066     /* free all allocated structures */
3067     tapeLinkPtr = head;
3068     while (tapeLinkPtr) {
3069         volumeLinkPtr = tapeLinkPtr->firstVolume;
3070         while (volumeLinkPtr) {
3071             lastVolumeLinkPtr = volumeLinkPtr;
3072             volumeLinkPtr = volumeLinkPtr->nextVolume;
3073             free(lastVolumeLinkPtr);
3074         }
3075
3076         lastTapeLinkPtr = tapeLinkPtr;
3077         tapeLinkPtr = tapeLinkPtr->nextTape;
3078         free(lastTapeLinkPtr);
3079     }
3080     return (code);
3081 }
3082
3083 compareDump(ptr1, ptr2)
3084      struct budb_dumpEntry *ptr1, *ptr2;
3085 {
3086     if (ptr1->created < ptr2->created)
3087         return (-1);
3088     else if (ptr1->created > ptr2->created)
3089         return (1);
3090     return (0);
3091 }
3092
3093 afs_int32
3094 printRecentDumps(ndumps)
3095      int ndumps;
3096 {
3097     afs_int32 code = 0;
3098     afs_int32 nextindex, index = 0;
3099     afs_int32 dbTime;
3100     budb_dumpList dl;
3101     struct budb_dumpEntry *dumpPtr;
3102     int i;
3103     char ds[50];
3104
3105     extern struct udbHandleS udbHandle;
3106     extern compareDump();
3107
3108     do {                        /* while (nextindex != -1) */
3109         /* initialize the dump list */
3110         dl.budb_dumpList_len = 0;
3111         dl.budb_dumpList_val = 0;
3112
3113         /* outline algorithm */
3114         code = ubik_Call(BUDB_GetDumps, udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "",       /* no name */
3115                          0,     /* start */
3116                          ndumps,        /* end */
3117                          index, /* index */
3118                          &nextindex, &dbTime, &dl);
3119         if (code) {
3120             if (code == BUDB_ENDOFLIST)
3121                 return 0;
3122             com_err("dumpInfo", code, "; Can't get dump information");
3123             return (code);
3124         }
3125
3126         /* No need to sort, it's already sorted */
3127
3128         if (dl.budb_dumpList_len && (index == 0))
3129             printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3130                    "parentid", "lv", "created", "nt", "nvols");
3131
3132         dumpPtr = dl.budb_dumpList_val;
3133         for (i = 1; i <= dl.budb_dumpList_len; i++) {
3134             compactDateString(&dumpPtr->created, ds, 50),
3135                 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3136                        dumpPtr->parent, dumpPtr->level, ds,
3137                        dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3138                        dumpPtr->nVolumes, dumpPtr->name);
3139             if (dumpPtr->initialDumpID) /* an appended dump */
3140                 printf(" (%u)", dumpPtr->initialDumpID);
3141             else if (dumpPtr->appendedDumpID)   /* has appended dumps */
3142                 printf(" (%u)", dumpPtr->id);
3143             printf("\n");
3144
3145             dumpPtr++;
3146         }
3147
3148         if (dl.budb_dumpList_val)
3149             free(dl.budb_dumpList_val);
3150         index = nextindex;
3151     } while (nextindex != -1);
3152
3153     return (code);
3154 }
3155
3156 /* bc_dumpInfoCmd 
3157  *      list the dumps and contens of the dumps.
3158  * params:
3159  *      as - name of tape
3160  *      arock -
3161  */
3162 bc_dumpInfoCmd(as, arock)
3163      struct cmd_syndesc *as;
3164      char *arock;
3165 {
3166     afs_int32 dumpid;
3167     afs_int32 detailFlag;
3168     afs_int32 ndumps;
3169     afs_int32 code = 0;
3170
3171     afs_int32 dumpInfo();
3172
3173     if (as->parms[0].items) {
3174         if (as->parms[1].items) {
3175             com_err(whoami, 0,
3176                     "These options are exclusive - select only one");
3177             return (BC_BADARG);
3178         }
3179         ndumps = atoi(as->parms[0].items->data);
3180         if (ndumps <= 0) {
3181             com_err(whoami, 0, "Must provide a positive number");
3182             return -1;
3183         }
3184
3185         code = printRecentDumps(ndumps);
3186     } else if (as->parms[1].items) {
3187         detailFlag = (as->parms[2].items ? 1 : 0);      /* 1 = detailed listing */
3188         dumpid = atoi(as->parms[1].items->data);
3189         code = dumpInfo(dumpid, detailFlag);
3190     } else {
3191         code = printRecentDumps(10);
3192     }
3193
3194     return (code);
3195 }