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