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