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