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