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