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