Bug Summary

File:bucoord/commands.c
Location:line 169, column 9
Description:Access to field 'next' results in a dereference of a null pointer (loaded from variable 'ss')

Annotated Source Code

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