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