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