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