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