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