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