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