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