2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #ifdef HAVE_POSIX_REGEX /* use POSIX regexp library */
21 #include <afs/com_err.h>
22 #include <afs/afsutil.h>
24 #include <afs/budb_prototypes.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 */
33 #include <afs/tcdata.h>
35 #include <afs/vsutils_prototypes.h>
38 #include "error_macros.h"
39 #include "bucoord_internal.h"
40 #include "bucoord_prototypes.h"
42 extern struct bc_config *bc_globalConfig;
43 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
44 extern struct ubik_client *cstruct;
48 extern afs_int32 lastTaskCode;
50 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
52 static int EvalVolumeSet1(struct bc_config *aconfig, struct bc_volumeSet *avs,
53 struct bc_volumeDump **avols,
54 struct ubik_client *uclient);
56 static int EvalVolumeSet2(struct bc_config *aconfig, struct bc_volumeSet *avs,
57 struct bc_volumeDump **avols,
58 struct ubik_client *uclient);
59 static int DBLookupByVolume(char *volumeName);
62 bc_EvalVolumeSet(struct bc_config *aconfig,
63 struct bc_volumeSet *avs,
64 struct bc_volumeDump **avols,
65 struct ubik_client *uclient)
66 { /*bc_EvalVolumeSet */
68 static afs_int32 use = 2;
70 if (use == 2) { /* Use EvalVolumeSet2() */
71 code = EvalVolumeSet2(aconfig, avs, avols, uclient);
72 if (code == RXGEN_OPCODE)
75 if (use == 1) { /* Use EvalVolumeSet1() */
76 code = EvalVolumeSet1(aconfig, avs, avols, uclient);
79 } /*bc_EvalVolumeSet */
81 struct partitionsort {
83 struct bc_volumeDump *vdlist;
84 struct bc_volumeDump *lastvdlist;
85 struct bc_volumeDump *dupvdlist;
86 struct bc_volumeEntry *vole;
87 struct partitionsort *next;
91 struct partitionsort *partitions;
92 struct serversort *next;
96 getSPEntries(afs_uint32 server, afs_int32 partition,
97 struct serversort **serverlist,
98 struct serversort **ss,
99 struct partitionsort **ps)
101 if (!(*ss) || ((*ss)->ipaddr != server)) {
103 for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
104 if ((*ss)->ipaddr == server)
108 /* No server entry added. Add one */
110 *ss = calloc(1, sizeof(struct serversort));
112 afs_com_err(whoami, BC_NOMEM, NULL);
116 (*ss)->ipaddr = server;
117 (*ss)->next = *serverlist;
122 if (!(*ps) || ((*ps)->part != partition)) {
123 for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
124 if ((*ps)->part == partition)
128 /* No partition entry added. Add one */
130 *ps = calloc(1, sizeof(struct partitionsort));
132 afs_com_err(whoami, BC_NOMEM, NULL);
138 (*ps)->part = partition;
139 (*ps)->next = (*ss)->partitions;
140 (*ss)->partitions = *ps;
146 randSPEntries(struct serversort *serverlist,
147 struct bc_volumeDump **avols)
149 struct serversort *ss, **pss;
150 struct partitionsort *ps, **pps;
152 afs_int32 scount, pcount;
156 /* Seed random number generator */
157 r = time(0) + getpid();
160 /* Count number of servers, remove one at a time */
161 for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
162 for (; scount; scount--) {
163 /* Pick a random server in list and remove it */
164 r = (rand() >> 4) % scount;
165 for (pss = &serverlist, ss = serverlist; r;
166 pss = &ss->next, ss = ss->next, r--);
169 /* Count number of partitions, remove one at a time */
170 for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
171 for (; pcount; pcount--) {
172 /* Pick a random parition in list and remove it */
173 r = (rand() >> 4) % pcount;
174 for (pps = &ss->partitions, ps = ss->partitions; r;
175 pps = &ps->next, ps = ps->next, r--);
178 ps->lastvdlist->next = *avols;
188 EvalVolumeSet2(struct bc_config *aconfig,
189 struct bc_volumeSet *avs,
190 struct bc_volumeDump **avols,
191 struct ubik_client *uclient)
192 { /*EvalVolumeSet2 */
193 struct bc_volumeEntry *tve;
194 struct bc_volumeDump *tavols;
195 struct VldbListByAttributes attributes;
196 afs_int32 si, nsi; /* startIndex and nextStartIndex */
197 afs_int32 nentries, e, ei, et, add, l;
198 nbulkentries bulkentries;
199 struct nvldbentry *entries = 0;
200 struct bc_volumeDump *tvd;
201 afs_int32 code = 0, tcode;
203 struct serversort *servers = 0, *ss = 0;
204 struct partitionsort *ps = 0;
206 *avols = (struct bc_volumeDump *)0;
207 bulkentries.nbulkentries_len = 0;
208 bulkentries.nbulkentries_val = 0;
210 /* For each of the volume set entries - collect the volumes that match it */
211 for (tve = avs->ventries; tve; tve = tve->next) {
212 /* Put together a call to the vlserver for this vlentry. The
213 * performance gain is from letting the vlserver expand the
214 * volumeset and not this routine.
217 if (tve->server.sin_addr.s_addr) { /* The server */
218 attributes.Mask |= VLLIST_SERVER;
219 attributes.server = tve->server.sin_addr.s_addr;
221 if (tve->partition != -1) { /* The partition */
222 attributes.Mask |= VLLIST_PARTITION;
223 attributes.partition = tve->partition;
226 /* Now make the call to the vlserver */
227 for (si = 0; si != -1; si = nsi) {
229 bulkentries.nbulkentries_len = 0;
230 bulkentries.nbulkentries_val = 0;
233 ubik_VL_ListAttributesN2(uclient, 0, &attributes,
234 tve->name, si, &nentries, &bulkentries, &nsi);
238 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
239 * it is not complete. The only way to tell if it is not complete
240 * is if et == 0 (which we check for below). Also, if the call didn't
241 * match any entries, then we don't know what version the vlserver
242 * is. In both cases, we return RXGEN_OPCODE and the calling routine
243 * will switch to the EvalVolumeSet1() call.
246 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
250 if (nentries > bulkentries.nbulkentries_len)
251 nentries = bulkentries.nbulkentries_len;
253 /* Step through each entry and add it to the list of volumes */
254 entries = bulkentries.nbulkentries_val;
255 for (e = 0; e < nentries; e++) {
256 ei = entries[e].matchindex & 0xffff;
257 et = (entries[e].matchindex >> 16) & 0xffff;
272 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
275 /* Find server and partiton structure to hang the entry off of */
277 getSPEntries(entries[e].serverNumber[ei],
278 entries[e].serverPartition[ei], &servers,
281 afs_com_err(whoami, tcode, NULL);
285 /* Detect if this entry should be added (not a duplicate).
286 * Use ps->dupvdlist and ps->vole to only search volumes from
287 * previous volume set entries.
290 if (tve != avs->ventries) {
291 l = strlen(entries[e].name);
292 if (ps->vole != tve) {
294 ps->dupvdlist = ps->vdlist;
296 for (tavols = ps->dupvdlist; add && tavols;
297 tavols = tavols->next) {
298 if (strncmp(tavols->name, entries[e].name, l) == 0) {
299 if ((strcmp(&entries[e].name[l], ".backup") == 0)
300 || (strcmp(&entries[e].name[l], ".readonly")
302 || (strcmp(&entries[e].name[l], "") == 0))
309 /* Allocate a volume dump structure and its name */
310 tvd = calloc(1, sizeof(struct bc_volumeDump));
312 afs_com_err(whoami, BC_NOMEM, NULL);
316 tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
318 afs_com_err(whoami, BC_NOMEM, NULL);
323 /* Fill it in and thread onto avols list */
324 strcpy(tvd->name, entries[e].name);
326 strcat(tvd->name, ".backup");
327 else if (et == ROVOL)
328 strcat(tvd->name, ".readonly");
329 tvd->vid = entries[e].volumeId[et];
332 tvd->partition = entries[e].serverPartition[ei];
333 tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
334 tvd->server.sin_port = 0; /* default FS port */
335 tvd->server.sin_family = AF_INET;
336 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
337 tvd->server.sin_len = sizeof(struct sockaddr_in);
340 /* String tvd off of partition struct */
341 tvd->next = ps->vdlist;
344 ps->lastvdlist = tvd;
350 /* Free memory allocated during VL call */
351 if (bulkentries.nbulkentries_val) {
352 free(bulkentries.nbulkentries_val);
353 bulkentries.nbulkentries_val = 0;
359 /* Randomly link the volumedump entries together */
360 randSPEntries(servers, avols);
361 fprintf(stderr, "Total number of volumes : %u\n", count);
364 if (bulkentries.nbulkentries_val) {
365 free(bulkentries.nbulkentries_val);
368 } /*EvalVolumeSet2 */
370 /*-----------------------------------------------------------------------------
374 * Takes the entries in a volumeset and expands them into a list of
375 * volumes. Every VLDB volume entry is looked at and compared to the
378 * When matching a VLDB volume entry to a volumeset entry,
379 * 1. If the RW volume entry matches, that RW volume is used.
380 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
381 * 3. Finally, if the RO volume entry matches, the RO volume is used.
382 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
383 * "user.troy" and "user.troy.backup". The rules will use
384 * the RW volume "user.troy".
386 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
387 * that volume is used and matches against any remaining volumeset entries
389 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
390 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
391 * would have matched its RW volume "user.troy", but the first
392 * match is used and the second match isn't even done.
395 * aconfig : Global configuration info.
397 * avols : Ptr to linked list of entries describing volumes to dump.
398 * uclient : Ptr to Ubik client structure.
401 * 0 on successful volume set evaluation,
402 * Lower-level codes otherwise.
405 * Expand only the single volume set provided (avs); don't go down its chain.
409 *-----------------------------------------------------------------------------
412 EvalVolumeSet1(struct bc_config *aconfig,
413 struct bc_volumeSet *avs,
414 struct bc_volumeDump **avols,
415 struct ubik_client *uclient)
416 { /*EvalVolumeSet1 */
417 afs_int32 code; /*Result of various calls */
418 struct bc_volumeDump *tvd; /*Ptr to new dump instance */
419 struct bc_volumeEntry *tve, *ctve; /*Ptr to new volume entry instance */
420 char patt[256]; /*Composite regex; also, target string */
421 int volType = 0; /*Type of volume that worked */
422 afs_int32 index; /*Current VLDB entry index */
423 afs_int32 count; /*Needed by VL_ListEntry() */
424 afs_int32 next_index; /*Next index to list */
425 struct vldbentry entry; /*VLDB entry */
426 int srvpartpair; /*Loop counter: server/partition pair */
430 struct serversort *servers = 0, *ss = 0;
431 struct partitionsort *ps = 0;
432 #ifdef HAVE_POSIX_REGEX
434 int need_regfree = 0;
439 *avols = (struct bc_volumeDump *)0;
440 ctve = (struct bc_volumeEntry *)0; /* no compiled entry */
442 /* For each vldb entry.
443 * Variable next_index is set to the index of the next VLDB entry
444 * in the enumeration.
446 for (index = 0; 1; index = next_index) { /*w */
447 memset(&entry, 0, sizeof(entry));
448 code = ubik_VL_ListEntry(uclient, /*Ubik client structure */
450 index, /*Current index */
451 &count, /*Ptr to working variable */
452 &next_index, /*Ptr to next index value to list */
453 &entry); /*Ptr to entry to fill */
457 break; /* If the next index is invalid, bail out now. */
459 /* For each entry in the volume set */
460 found = 0; /* No match in volume set yet */
461 for (tve = avs->ventries; tve; tve = tve->next) { /*ve */
462 /* for each server in the vldb entry */
463 for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) { /*s */
464 /* On the same server */
465 if (tve->server.sin_addr.s_addr
466 && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
467 entry.serverNumber[srvpartpair],
475 /* On the same partition */
476 if ((tve->partition != -1)
477 && (tve->partition != entry.serverPartition[srvpartpair]))
480 /* If the volume entry is not compiled, then compile it */
482 sprintf(patt, "^%s$", tve->name);
483 #ifdef HAVE_POSIX_REGEX
484 if (regcomp(&re, patt, REG_NOSUB) != 0) {
485 afs_com_err(whoami, 0, "Can't compile regular expression '%s'", patt);
490 errm = (char *)re_comp(patt);
492 afs_com_err(whoami, 0,
493 "Can't compile regular expression '%s': %s",
501 /* If the RW name matches the volume set entry, take
502 * it and exit. First choice is to use the RW volume.
504 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
505 if (entry.flags & RW_EXISTS) {
506 sprintf(patt, "%s", entry.name);
507 #ifdef HAVE_POSIX_REGEX
508 code = regexec(&re, patt, 0, NULL, 0);
511 code = re_exec(patt);
515 foundentry = srvpartpair;
521 /* If the BK name matches the volume set entry, take
522 * it and exit. Second choice is to use the BK volume.
524 if (entry.flags & BACK_EXISTS) {
525 sprintf(patt, "%s.backup", entry.name);
526 #ifdef HAVE_POSIX_REGEX
527 code = regexec(&re, patt, 0, NULL, 0);
530 code = re_exec(patt);
534 foundentry = srvpartpair;
541 /* If the RO name matches the volume set entry, remember
542 * it, but continue searching. Further entries may be
543 * RW or backup entries that will match.
545 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
546 && (entry.flags & RO_EXISTS)) {
547 sprintf(patt, "%s.readonly", entry.name);
548 #ifdef HAVE_POSIX_REGEX
549 code = regexec(&re, patt, 0, NULL, 0);
552 code = re_exec(patt);
556 foundentry = srvpartpair;
562 afs_com_err(whoami, 0, "Internal error in regex package");
565 /* If found a match, then create a new volume dump entry */
567 /* Find server and partition structure to hang the entry off of */
569 getSPEntries(entry.serverNumber[foundentry],
570 entry.serverPartition[foundentry], &servers,
573 afs_com_err(whoami, code, NULL);
578 tvd = calloc(1, sizeof(struct bc_volumeDump));
580 afs_com_err(whoami, BC_NOMEM, NULL);
584 tvd->name = (char *)malloc(strlen(entry.name) + 10);
586 afs_com_err(whoami, BC_NOMEM, NULL);
591 strcpy(tvd->name, entry.name);
592 if (volType == BACKVOL)
593 strcat(tvd->name, ".backup");
594 else if (volType == ROVOL)
595 strcat(tvd->name, ".readonly");
596 tvd->vid = entry.volumeId[volType];
598 tvd->volType = volType;
599 tvd->partition = entry.serverPartition[foundentry];
600 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
601 tvd->server.sin_port = 0; /* default FS port */
602 tvd->server.sin_family = AF_INET;
604 /* String tvd off of partition struct */
605 tvd->next = ps->vdlist;
608 ps->lastvdlist = tvd;
614 #ifdef HAVE_POSIX_REGEX
619 /* Randomly link the volumedump entries together */
620 randSPEntries(servers, avols);
622 fprintf(stderr, "Total number of volumes : %u\n", total);
624 } /*EvalVolumeSet1 */
627 compactTimeString(time_t *date, char *string, afs_int32 size)
634 if (*date == NEVERDATE) {
635 sprintf(string, "NEVER");
637 ltime = localtime(date);
638 strftime(string, size, "%m/%d/%Y %H:%M", ltime);
644 * print out a date in compact format, 16 chars, format is
647 * date_long - ptr to a long containing the time
649 * ptr to a string containing a representation of the date
652 compactDateString(afs_uint32 *date_long, char *string, afs_int32 size)
654 time_t t = *date_long;
655 return compactTimeString(&t, string, size);
660 bc_SafeATOI(char *anum)
664 for (; *anum; anum++) {
665 if ((*anum < '0') || (*anum > '9'))
667 total = (10 * total) + (afs_int32) (*anum - '0');
673 * Take a string and parse it for a number (could be float) followed
674 * by a character representing the units (K,M,G,T). Default is 'K'.
675 * Return the size in KBytes.
678 bc_FloatATOI(char *anum)
682 afs_int32 fraction = 0; /* > 0 if past the decimal */
684 for (; *anum; anum++) {
685 if ((*anum == 't') || (*anum == 'T')) {
686 total *= 1024 * 1024 * 1024;
689 if ((*anum == 'g') || (*anum == 'G')) {
690 total *= 1024 * 1024;
693 if ((*anum == 'm') || (*anum == 'M')) {
697 if ((*anum == 'k') || (*anum == 'K')) {
704 if ((*anum < '0') || (*anum > '9'))
708 total = (10. * total) + (float)(*anum - '0');
710 total += ((float)(*anum - '0')) / (float)fraction;
715 total += 0.5; /* Round up */
716 if (total > 0x7fffffff) /* Don't go over 2G */
718 rtotal = (afs_int32) total;
722 /* make a copy of a string so that it can be freed later */
724 bc_CopyString(char *astring)
729 return (NULL); /* propagate null strings easily */
730 tp = strdup(astring);
732 afs_com_err(whoami, BC_NOMEM, NULL);
740 * Concatenates the parameters of an option and returns the string.
745 concatParams(struct cmd_item *itemPtr)
747 struct cmd_item *tempPtr;
748 afs_int32 length = 0;
751 /* compute the length of string required */
752 for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
753 length += strlen(tempPtr->data);
754 length++; /* space or null terminator */
757 if (length == 0) { /* no string (0 length) */
758 afs_com_err(whoami, 0, "Can't have zero length date and time string");
762 string = (char *)malloc(length); /* allocate the string */
764 afs_com_err(whoami, BC_NOMEM, NULL);
769 tempPtr = itemPtr; /* now assemble the string */
771 strcat(string, tempPtr->data);
772 tempPtr = tempPtr->next;
777 return (string); /* return the string */
781 * print out an interface status node as received from butc
785 printIfStatus(struct tciStatusS *statusPtr)
787 printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
788 if (statusPtr->nKBytes)
789 printf("%ld Kbytes transferred", (long unsigned int) statusPtr->nKBytes);
790 if (strlen(statusPtr->volumeName) != 0) {
791 if (statusPtr->nKBytes)
793 printf("volume %s", statusPtr->volumeName);
798 if (statusPtr->flags & ABORT_REQUEST)
799 printf(" [abort request rcvd]");
801 if (statusPtr->flags & ABORT_DONE)
802 printf(" [abort complete]");
804 if (statusPtr->flags & OPR_WAIT)
805 printf(" [operator wait]");
807 if (statusPtr->flags & CALL_WAIT)
808 printf(" [callout in progress]");
810 if (statusPtr->flags & DRIVE_WAIT)
811 printf(" [drive wait]");
813 if (statusPtr->flags & TASK_DONE)
819 getPortOffset(char *port)
821 afs_int32 portOffset;
823 portOffset = bc_SafeATOI(port);
825 if (portOffset < 0) {
826 afs_com_err(whoami, 0, "Can't decode port offset '%s'", port);
828 } else if (portOffset > BC_MAXPORTOFFSET) {
829 afs_com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
836 /* bc_GetTapeStatusCmd
837 * display status of all tasks on a particular tape coordinator
840 bc_GetTapeStatusCmd(struct cmd_syndesc *as, void *arock)
843 struct rx_connection *tconn;
844 afs_int32 portOffset = 0;
849 struct tciStatusS status;
851 code = bc_UpdateHosts();
853 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
857 if (as->parms[0].items) {
858 portOffset = getPortOffset(as->parms[0].items->data);
863 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
867 flags = TSK_STAT_FIRST;
871 while ((flags & TSK_STAT_END) == 0) {
872 code = TC_ScanStatus(tconn, &taskId, &status, &flags);
874 if (code == TC_NOTASKS)
876 afs_com_err(whoami, code, "; Can't get status from butc");
879 if ((flags & TSK_STAT_NOTFOUND))
880 break; /* Can't find the task id */
881 flags &= ~TSK_STAT_FIRST; /* turn off flag */
883 printIfStatus(&status);
888 printf("Tape coordinator is idle\n");
890 if (flags & TSK_STAT_ADSM)
891 printf("TSM Tape coordinator\n");
892 else if (flags & TSK_STAT_XBSA)
893 printf("XBSA Tape coordinator\n");
898 extern struct Lock dispatchLock;
901 * wait for all jobs to terminate
904 bc_WaitForNoJobs(void)
907 int usefulJobRunning = 1;
908 int printWaiting = 1;
910 extern dlqlinkT statusHead;
912 while (usefulJobRunning) {
913 usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
914 if (dispatchLock.excl_locked)
915 usefulJobRunning = 1;
916 for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
917 if (bc_dumpTasks[i].flags & BC_DI_INUSE)
918 usefulJobRunning = 1;
921 /* Wait 5 seconds and check again */
922 if (usefulJobRunning) {
924 afs_com_err(whoami, 0, "waiting for job termination");
930 return (lastTaskCode);
934 * print status on running jobs
936 * ignored - a null "as" prints only jobs.
939 bc_JobsCmd(struct cmd_syndesc *as, void *arock)
948 extern dlqlinkT statusHead;
950 dlqInit(&atJobsHead);
953 ptr = (&statusHead)->dlq_next;
954 while (ptr != &statusHead) {
955 statusPtr = (statusP) ptr;
958 if (statusPtr->scheduledDump) {
959 dlqUnlink((dlqlinkP) statusPtr);
960 dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
962 printf("Job %d:", statusPtr->jobNumber);
964 printf(" %s", statusPtr->taskName);
966 if (statusPtr->dbDumpId)
967 printf(": DumpID %u", statusPtr->dbDumpId);
968 if (statusPtr->nKBytes)
969 printf(", %ld Kbytes", afs_printable_int32_ld(statusPtr->nKBytes));
970 if (strlen(statusPtr->volumeName) != 0)
971 printf(", volume %s", statusPtr->volumeName);
973 if (statusPtr->flags & CONTACT_LOST)
974 printf(" [butc contact lost]");
976 if (statusPtr->flags & ABORT_REQUEST)
977 printf(" [abort request]");
979 if (statusPtr->flags & ABORT_SENT)
980 printf(" [abort sent]");
982 if (statusPtr->flags & OPR_WAIT)
983 printf(" [operator wait]");
985 if (statusPtr->flags & CALL_WAIT)
986 printf(" [callout in progress]");
988 if (statusPtr->flags & DRIVE_WAIT)
989 printf(" [drive wait]");
995 * Now print the scheduled dumps.
997 if (!dlqEmpty(&statusHead) && as)
998 printf("\n"); /* blank line between running and scheduled dumps */
1001 while (!dlqEmpty(&atJobsHead)) {
1002 ptr = (&atJobsHead)->dlq_next;
1003 youngest = (statusP) ptr;
1005 ptr = ptr->dlq_next;
1006 while (ptr != &atJobsHead) { /* Find the dump that starts the earliest */
1007 statusPtr = (statusP) ptr;
1008 if (statusPtr->scheduledDump < youngest->scheduledDump)
1009 youngest = statusPtr;
1010 ptr = ptr->dlq_next;
1013 /* Print token expiration time */
1014 if ((tokenExpires > prevTime)
1015 && (tokenExpires <= youngest->scheduledDump) && as
1016 && (tokenExpires != NEVERDATE)) {
1017 if (tokenExpires > time(0)) {
1018 compactTimeString(&tokenExpires, ds, 50);
1019 printf(" %16s: TOKEN EXPIRATION\n", ds);
1021 printf(" TOKEN HAS EXPIRED\n");
1024 prevTime = youngest->scheduledDump;
1026 /* Print the info */
1027 compactDateString(&youngest->scheduledDump, ds, 50);
1028 printf("Job %d:", youngest->jobNumber);
1029 printf(" %16s: %s", ds, youngest->cmdLine);
1032 /* return to original list */
1033 dlqUnlink((dlqlinkP) youngest);
1034 dlqLinkb(&statusHead, (dlqlinkP) youngest);
1037 /* Print token expiration time if havn't already */
1038 if ((tokenExpires == NEVERDATE) && as)
1039 printf(" : TOKEN NEVER EXPIRES\n");
1040 else if ((tokenExpires > prevTime) && as) {
1041 if (tokenExpires > time(0)) {
1042 compactTimeString(&tokenExpires, ds, 50);
1043 printf(" %16s: TOKEN EXPIRATION\n", ds);
1045 printf(" : TOKEN HAS EXPIRED\n");
1054 bc_KillCmd(struct cmd_syndesc *as, void *arock)
1058 struct bc_dumpTask *td;
1065 extern dlqlinkT statusHead;
1067 tp = as->parms[0].items->data;
1068 if (strchr(tp, '.') == 0) {
1069 slot = bc_SafeATOI(tp);
1071 afs_com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1076 ptr = (&statusHead)->dlq_next;
1077 while (ptr != &statusHead) {
1078 statusPtr = (statusP) ptr;
1079 if (statusPtr->jobNumber == slot) {
1080 statusPtr->flags |= ABORT_REQUEST;
1084 ptr = ptr->dlq_next;
1088 fprintf(stderr, "Job %d not found\n", slot);
1093 for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1094 if (td->flags & BC_DI_INUSE) {
1096 strcpy(tbuffer, td->volSetName);
1097 strcat(tbuffer, ".");
1098 strcat(tbuffer, tailCompPtr(td->dumpName));
1099 if (strcmp(tbuffer, tp) == 0)
1103 if (i >= BC_MAXSIMDUMPS) {
1104 afs_com_err(whoami, 0, "Can't find job %s", tp);
1109 statusPtr = findStatus(td->dumpID);
1111 if (statusPtr == 0) {
1112 afs_com_err(whoami, 0, "Can't locate status - internal error");
1116 statusPtr->flags |= ABORT_REQUEST;
1122 /* restore a volume or volumes */
1124 bc_VolRestoreCmd(struct cmd_syndesc *as, void *arock)
1127 * parm 0 is the new server to restore to
1128 * parm 1 is the new partition to restore to
1129 * parm 2 is volume(s) to restore
1130 * parm 3 is the new extension, if any, for the volume name.
1131 * parm 4 gives the new volume # to restore this volume as (removed).
1132 * parm 4 date is a string representing the date
1134 * We handle four types of restores. If old is set, then we restore the
1135 * volume with the same name and ID. If old is not set, we allocate
1136 * a new volume ID for the restored volume. If a new extension is specified,
1137 * we add that extension to the volume name of the restored volume.
1139 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1140 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1141 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1142 struct bc_volumeDump *tvol; /* temp for same */
1143 struct sockaddr_in destServ; /* machine to which to restore volumes */
1144 afs_int32 destPartition; /* partition to which to restore volumes */
1146 struct cmd_item *ti;
1150 afs_int32 dumpID = 0;
1151 char *newExt, *timeString;
1153 afs_int32 *ports = NULL;
1154 afs_int32 portCount = 0;
1157 code = bc_UpdateHosts();
1159 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1163 /* specified other destination host */
1164 if (as->parms[0].items) {
1165 tp = as->parms[0].items->data;
1166 if (bc_ParseHost(tp, &destServ)) {
1167 afs_com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1172 /* specified other destination partition */
1173 if (as->parms[1].items) {
1174 tp = as->parms[1].items->data;
1175 if (bc_GetPartitionID(tp, &destPartition)) {
1176 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1181 for (ti = as->parms[2].items; ti; ti = ti->next) {
1182 /* build list of volume items */
1183 tvol = calloc(1, sizeof(struct bc_volumeDump));
1185 afs_com_err(whoami, BC_NOMEM, NULL);
1189 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1191 afs_com_err(whoami, BC_NOMEM, NULL);
1194 strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1195 tvol->entry = &tvolumeEntry;
1198 lastVol->next = tvol; /* thread onto end of list */
1200 volsToRestore = tvol;
1204 if (as->parms[4].items) {
1205 timeString = concatParams(as->parms[4].items);
1209 code = ktime_DateToLong(timeString, &fromDate);
1212 afs_com_err(whoami, 0, "Can't parse restore date and time");
1213 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1217 fromDate = 0x7fffffff; /* latest one */
1220 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1223 /* Read all the port offsets into the ports array. The first element in the
1224 * array is for full restore and the rest are for incremental restores
1226 if (as->parms[5].items) {
1227 for (ti = as->parms[5].items; ti; ti = ti->next)
1229 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1231 afs_com_err(whoami, BC_NOMEM, NULL);
1235 for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1236 ports[i] = getPortOffset(ti->data);
1242 dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1244 if (as->parms[7].items)
1246 dumpID = atoi(as->parms[7].items->data);
1252 * Perform the call to start the restore.
1255 bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1256 &destServ, destPartition, fromDate, newExt, oldFlag,
1257 /*parentDump */ dumpID, /*dumpLevel */ 0,
1258 bc_Restorer, ports, portCount,
1259 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1261 afs_com_err(whoami, code, "; Failed to queue restore");
1266 /* restore a whole partition or server */
1268 /* bc_DiskRestoreCmd
1269 * restore a whole partition
1271 * first, reqd - machine (server) to restore
1272 * second, reqd - partition to restore
1277 bc_DiskRestoreCmd(struct cmd_syndesc *as, void *arock)
1279 struct bc_volumeSet tvolumeSet; /* temporary volume set for EvalVolumeSet call */
1280 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1281 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1282 struct sockaddr_in destServ; /* machine to which to restore volumes */
1283 afs_int32 destPartition; /* partition to which to restore volumes */
1289 afs_int32 *ports = NULL;
1290 afs_int32 portCount = 0;
1292 struct bc_volumeDump *prev, *tvol, *nextvol;
1293 struct cmd_item *ti;
1296 /* parm 0 is the server to restore
1297 * parm 1 is the partition to restore
1299 * parm 8 and above as in VolRestoreCmd:
1300 * parm 8 is the new server to restore to
1301 * parm 9 is the new partition to restore to
1304 code = bc_UpdateVolumeSet();
1306 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1309 code = bc_UpdateHosts();
1311 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1315 /* create a volume set corresponding to the volume pattern we've been given */
1316 memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1317 memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1318 tvolumeSet.name = "TempVolumeSet";
1319 tvolumeSet.ventries = &tvolumeEntry;
1320 tvolumeEntry.serverName = as->parms[0].items->data;
1321 tvolumeEntry.partname = as->parms[1].items->data;
1323 if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1324 afs_com_err(whoami, 0, "Can't parse partition '%s'",
1325 tvolumeEntry.partname);
1329 if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1330 afs_com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1334 /* specified other destination host */
1335 if (as->parms[8].items) {
1336 tp = as->parms[8].items->data;
1337 if (bc_ParseHost(tp, &destServ)) {
1338 afs_com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1341 } else /* use destination host == original host */
1342 memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1344 /* specified other destination partition */
1345 if (as->parms[9].items) {
1346 tp = as->parms[9].items->data;
1347 if (bc_GetPartitionID(tp, &destPartition)) {
1348 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1351 } else /* use original partition */
1352 destPartition = tvolumeEntry.partition;
1354 tvolumeEntry.name = ".*"; /* match all volumes (this should be a parameter) */
1356 if (as->parms[2].items) {
1357 for (ti = as->parms[2].items; ti; ti = ti->next)
1359 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1361 afs_com_err(whoami, BC_NOMEM, NULL);
1365 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1366 ports[i] = getPortOffset(ti->data);
1372 newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1373 dontExecute = (as->parms[11].items ? 1 : 0); /* -n */
1376 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1379 bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1382 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1386 /* Since we want only RW volumes, remove any
1387 * BK or RO volumes from the list.
1389 for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1390 nextvol = tvol->next;
1391 if (BackupName(tvol->name)) {
1394 "Will not restore volume %s (its RW does not reside on the partition)\n",
1397 if (tvol == volsToRestore) {
1398 volsToRestore = nextvol;
1400 prev->next = nextvol;
1410 fromDate = 0x7fffffff; /* last one before this date */
1411 oldFlag = 1; /* do restore same volid (and name) */
1414 * Perform the call to start the dump.
1417 bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1418 &destServ, destPartition, fromDate, newExt, oldFlag,
1419 /*parentDump */ 0, /*dumpLevel */ 0,
1420 bc_Restorer, ports, portCount,
1421 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1423 afs_com_err(whoami, code, "; Failed to queue restore");
1428 /* bc_VolsetRestoreCmd
1429 * restore a volumeset or list of volumes.
1433 bc_VolsetRestoreCmd(struct cmd_syndesc *as, void *arock)
1440 afs_int32 *ports = NULL;
1441 afs_int32 portCount = 0;
1444 struct bc_volumeSet *volsetPtr; /* Ptr to list of generated volume info */
1445 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1446 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1447 struct sockaddr_in destServer; /* machine to which to restore volume */
1448 afs_int32 destPartition; /* partition to which to restore volumes */
1449 struct cmd_item *ti;
1452 code = bc_UpdateVolumeSet();
1454 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1457 code = bc_UpdateHosts();
1459 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1463 if (as->parms[0].items) {
1464 if (as->parms[1].items) {
1465 afs_com_err(whoami, 0, "Can't have both -name and -file options");
1469 volsetName = as->parms[0].items->data;
1470 volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1472 afs_com_err(whoami, 0,
1473 "Can't find volume set '%s' in backup database",
1478 /* Expand out the volume set into its component list of volumes. */
1480 bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1483 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1486 } else if (as->parms[1].items) {
1489 char server[50], partition[50], volume[50], rest[256];
1491 struct bc_volumeDump *tvol;
1493 fd = fopen(as->parms[1].items->data, "r");
1495 afs_com_err(whoami, errno, "; Cannot open file '%s'",
1496 as->parms[1].items->data);
1500 while (fgets(line, 255, fd)) {
1502 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1508 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1509 fprintf(stderr, " %s", line);
1513 if (bc_ParseHost(server, &destServer)) {
1514 afs_com_err(whoami, 0, "Failed to locate host '%s'", server);
1518 if (bc_GetPartitionID(partition, &destPartition)) {
1519 afs_com_err(whoami, 0,
1520 "Failed to parse destination partition '%s'",
1525 /* Allocate a volumeDump structure and link it in */
1526 tvol = calloc(1, sizeof(struct bc_volumeDump));
1528 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1530 afs_com_err(whoami, BC_NOMEM, NULL);
1533 strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1534 memcpy(&tvol->server, &destServer, sizeof(destServer));
1535 tvol->partition = destPartition;
1538 lastVol->next = tvol; /* thread onto end of list */
1540 volsToRestore = tvol;
1545 afs_com_err(whoami, 0, "-name or -file option required");
1550 /* Get the port offset for the restore */
1551 if (as->parms[2].items) {
1552 for (ti = as->parms[2].items; ti; ti = ti->next)
1554 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1556 afs_com_err(whoami, BC_NOMEM, NULL);
1560 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1561 ports[i] = getPortOffset(ti->data);
1567 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1568 dontExecute = (as->parms[4].items ? 1 : 0);
1570 fromDate = 0x7fffffff; /* last one before this date */
1571 oldFlag = 1; /* do restore same volid (and name) */
1573 /* Perform the call to start the restore */
1574 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1575 /*destserver */ NULL, /*destpartition */ 0, fromDate,
1577 /*parentDump */ 0, /*dumpLevel */ 0,
1578 bc_Restorer, ports, portCount,
1579 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1581 afs_com_err(whoami, code, "; Failed to queue restore");
1586 /*-----------------------------------------------------------------------------
1590 * Perform a dump of the set of volumes provided.
1591 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1594 * as : Parsed command line information.
1595 * arock : Ptr to misc stuff; not used.
1599 * The result of bc_StartDump() otherwise
1606 *---------------------------------------------------------------------------
1611 bc_DumpCmd(struct cmd_syndesc *as, void *arock)
1613 char *dumpPath = NULL;
1614 char *vsName = NULL; /*Ptrs to various names */
1615 struct bc_volumeSet *tvs = NULL; /*Ptr to list of generated volume info */
1616 struct bc_dumpSchedule *tds;
1617 struct bc_dumpSchedule *baseds = NULL; /*Ptr to dump schedule node */
1618 struct bc_volumeDump *tve, *volsToDump; /*Ptr to individual vols to be dumped */
1619 struct budb_dumpEntry dumpEntry, de, fde; /* dump entry */
1622 afs_int32 parent; /* parent dump */
1623 afs_int32 level; /* this dump's level # */
1624 afs_int32 problemFindingDump; /* can't find parent(s) */
1626 afs_int32 *portp = NULL;
1627 afs_int32 doAt, atTime; /* Time a timed-dump is to start at */
1630 int doAppend = 0; /* Append the dump to dump set */
1631 afs_int32 code; /* Return code */
1632 int loadfile; /* whether to load a file or not */
1636 extern struct bc_dumpTask bc_dumpTasks[];
1638 code = bc_UpdateDumpSchedule();
1640 afs_com_err(whoami, code, "; Can't retrieve dump schedule");
1643 code = bc_UpdateVolumeSet();
1645 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1648 code = bc_UpdateHosts();
1650 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1655 * Some parameters cannot be specified together
1656 * The "-file" option cannot exist with the "-volume", "-dump",
1657 * "-portoffset", or "-append" option
1659 if (as->parms[6].items) {
1661 if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1662 || as->parms[4].items) {
1663 afs_com_err(whoami, 0, "Invalid option specified with -file option");
1668 if (!as->parms[0].items || !as->parms[1].items) {
1669 afs_com_err(whoami, 0,
1670 "Must specify volume set name and dump level name");
1676 * Get the time we are to perform this dump
1678 if (as->parms[3].items) {
1681 timeString = concatParams(as->parms[3].items);
1686 * Now parse this string for the time to start.
1688 code = ktime_DateToLong(timeString, &atTime);
1691 afs_com_err(whoami, 0, "Can't parse dump start date and time");
1692 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1698 dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1701 * If this dump is not a load file, then check the parameters.
1703 if (!loadfile) { /*6 */
1704 vsName = as->parms[0].items->data; /* get volume set name */
1705 dumpPath = as->parms[1].items->data; /* get dump path */
1707 /* get the port number, if one was specified */
1708 if (as->parms[2].items) {
1709 portp = (afs_int32 *) malloc(sizeof(afs_int32));
1711 afs_com_err(whoami, BC_NOMEM, NULL);
1715 *portp = getPortOffset(as->parms[2].items->data);
1720 doAppend = (as->parms[4].items ? 1 : 0); /* -append */
1723 * Get a hold of the given volume set and dump set.
1725 tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1727 afs_com_err(whoami, 0,
1728 "Can't find volume set '%s' in backup database", vsName);
1731 baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1733 afs_com_err(whoami, 0,
1734 "Can't find dump schedule '%s' in backup database",
1743 * If given the "-at" option, then add this to the jobs list and return
1746 * Create a status node for this timed dump.
1747 * Fill in the time to dump and the cmd line for the dump leaving off
1748 * the -at option. If the -n option is there, it is scheduled with
1749 * the Timed dump as opposed to not scheduling the time dump at all.
1752 if (atTime < time(0)) {
1753 afs_com_err(whoami, 0,
1754 "Time of dump is earlier then current time - not added");
1756 statusPtr = createStatusNode();
1758 statusPtr->scheduledDump = atTime;
1760 /* Determine length of the dump command */
1762 length += 4; /* "dump" */
1764 length += 6; /* " -file" */
1765 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1767 /* length += 11; *//* " -volumeset" */
1768 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1770 /* length += 6; *//* " -dump" */
1771 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1773 if (as->parms[2].items) {
1774 /* length += 12; *//* " -portoffset" */
1775 length += 1 + strlen(as->parms[2].items->data); /* " <port>" */
1778 if (as->parms[4].items)
1779 length += 8; /* " -append" */
1783 length += 3; /* " -n" */
1784 length++; /* end-of-line */
1786 /* Allocate status block for this timed dump */
1787 sprintf(statusPtr->taskName, "Scheduled Dump");
1788 statusPtr->jobNumber = bc_jobNumber();
1789 statusPtr->scheduledDump = atTime;
1790 statusPtr->cmdLine = (char *)malloc(length);
1791 if (!statusPtr->cmdLine) {
1792 afs_com_err(whoami, BC_NOMEM, NULL);
1796 /* Now reconstruct the dump command */
1797 statusPtr->cmdLine[0] = 0;
1798 strcat(statusPtr->cmdLine, "dump");
1800 strcat(statusPtr->cmdLine, " -file");
1801 strcat(statusPtr->cmdLine, " ");
1802 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1804 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1805 strcat(statusPtr->cmdLine, " ");
1806 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1808 /* strcat(statusPtr->cmdLine, " -dump"); */
1809 strcat(statusPtr->cmdLine, " ");
1810 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1812 if (as->parms[2].items) {
1813 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1814 strcat(statusPtr->cmdLine, " ");
1815 strcat(statusPtr->cmdLine, as->parms[2].items->data);
1818 if (as->parms[4].items)
1819 strcat(statusPtr->cmdLine, " -append");
1822 strcat(statusPtr->cmdLine, " -n");
1824 printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1825 if ((atTime > tokenExpires) && (tokenExpires != NEVERDATE))
1826 afs_com_err(whoami, 0,
1827 "Warning: job %d starts after expiration of AFS token",
1828 statusPtr->jobNumber);
1837 * Read and execute the load file if specified. The work of reading is done
1838 * in the main routine prior the dispatch call. loadFile and dontExecute are
1839 * global variables so this can take place in main.
1842 loadFile = strdup(as->parms[6].items->data);
1844 afs_com_err(whoami, BC_NOMEM, NULL);
1851 * We are doing a real dump (no load file or timed dump).
1853 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1856 /* For each dump-level above this one, see if the volumeset was dumped
1857 * at the level. We search all dump-levels since a higher dump-level
1858 * may have actually been done AFTER a lower dump level.
1860 parent = level = problemFindingDump = 0;
1861 for (tds = baseds->parent; tds; tds = tds->parent) {
1862 /* Find the most recent dump of the volume-set at this dump-level.
1863 * Raise problem flag if didn't find a dump and a parent not yet found.
1865 code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1868 problemFindingDump = 1; /* skipping a dump level */
1872 /* We found the most recent dump at this level. Now check
1873 * if we should use it by seeing if its full dump hierarchy
1874 * exists. If it doesn't, we don't want to base our incremental
1877 if (!parent || (dumpEntry.id > parent)) {
1878 /* Follow the parent dumps to see if they are all there */
1879 for (d = dumpEntry.parent; d; d = de.parent) {
1880 code = bcdb_FindDumpByID(d, &de);
1885 /* If we found the entire level, remember it. Otherwise raise flag.
1886 * If we had already found a dump, raise the problem flag.
1890 problemFindingDump = 1;
1891 parent = dumpEntry.id;
1892 level = dumpEntry.level + 1;
1893 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1895 /* Dump hierarchy not complete so can't base off the latest */
1896 problemFindingDump = 1;
1901 /* If the problemflag was raise, it means we are not doing the
1902 * dump at the level we requested it be done at.
1904 if (problemFindingDump) {
1905 afs_com_err(whoami, 0,
1906 "Warning: Doing level %d dump due to missing higher-level dumps",
1909 printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1911 } else if (parent) {
1912 printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1915 /* Expand out the volume set into its component list of volumes. */
1916 code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1918 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1922 printf("No volumes to dump\n");
1926 /* Determine what the clone time of the volume was when it was
1927 * last dumped (tve->date). This is the time from when an
1928 * incremental should be done (remains zero if a full dump).
1931 for (tve = volsToDump; tve; tve = tve->next) {
1932 code = bcdb_FindClone(parent, tve->name, &tve->date);
1936 /* Get the time the volume was last cloned and see if the volume has
1937 * changed since then. Only do this when the "-n" flag is specified
1938 * because butc will get the cloneDate at time of dump.
1942 volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1943 tve->vid, tve->volType, &tve->cloneDate);
1947 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1948 afs_com_err(whoami, 0,
1949 "Warning: Timestamp on volume %s unchanged from previous dump",
1957 printf("Would have dumped the following volumes:\n");
1959 printf("Preparing to dump the following volumes:\n");
1960 for (tve = volsToDump; tve; tve = tve->next) {
1961 printf("\t%s (%u)\n", tve->name, tve->vid);
1966 code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1967 /*destServer */ NULL, /*destPartition */ 0,
1969 /*newExt */ NULL, /*oldFlag */ 0,
1970 parent, level, bc_Dumper, portp, /*portCount */ 1,
1971 baseds, doAppend, dontExecute);
1973 afs_com_err(whoami, code, "; Failed to queue dump");
1980 * terminate the backup process. Insists that that all running backup
1981 * jobs be terminated before it will quit
1986 bc_QuitCmd(struct cmd_syndesc *as, void *arock)
1989 struct bc_dumpTask *td;
1990 extern dlqlinkT statusHead;
1994 /* Check the status list for outstanding jobs */
1996 for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
1997 ptr = ptr->dlq_next) {
1998 statusPtr = (statusP) ptr;
1999 if (!(statusPtr->flags & ABORT_REQUEST)) {
2001 afs_com_err(whoami, 0, "Job %d still running (and not aborted)",
2002 statusPtr->jobNumber);
2003 afs_com_err(whoami, 0,
2004 "You must at least 'kill' all running jobs before quitting");
2010 /* A job still being initialized (but no status structure or job number since it
2011 * has not been handed to a butc process yet)
2013 for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
2014 if (td->flags & BC_DI_INUSE) {
2015 afs_com_err(whoami, 0, "A job is still running");
2016 afs_com_err(whoami, 0,
2017 "You must at least 'kill' all running jobs before quitting");
2023 /* close the all temp text files before quitting */
2024 for (i = 0; i < TB_NUM; i++)
2025 bc_closeTextFile(&bc_globalConfig->configText[i],
2026 &bc_globalConfig->tmpTextFileNames[i][0]);
2032 * Labels a tape i.e. request the tape coordinator to perform this
2036 bc_LabelTapeCmd(struct cmd_syndesc *as, void *arock)
2038 char *tapename = 0, *pname = 0;
2043 code = bc_UpdateHosts();
2045 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2049 if (as->parms[0].items) { /* -name */
2050 tapename = as->parms[0].items->data;
2051 if (strlen(tapename) >= TC_MAXTAPELEN) {
2052 afs_com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2057 if (as->parms[3].items) { /* -pname */
2059 afs_com_err(whoami, 0, "Can only specify -name or -pname");
2062 pname = as->parms[3].items->data;
2063 if (strlen(pname) >= TC_MAXTAPELEN) {
2064 afs_com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2069 if (as->parms[1].items) {
2070 size = bc_FloatATOI(as->parms[1].items->data);
2072 afs_com_err(whoami, 0, "Bad syntax for tape size '%s'",
2073 as->parms[1].items->data);
2079 if (as->parms[2].items) {
2080 port = getPortOffset(as->parms[2].items->data);
2085 code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2092 * read the label on a tape
2094 * optional port number
2097 bc_ReadLabelCmd(struct cmd_syndesc *as, void *arock)
2102 code = bc_UpdateHosts();
2104 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2108 if (as->parms[0].items) {
2109 port = getPortOffset(as->parms[0].items->data);
2114 code = bc_ReadLabel(bc_globalConfig, port);
2121 * read content information from dump tapes, and if user desires,
2122 * add it to the database
2125 bc_ScanDumpsCmd(struct cmd_syndesc *as, void *arock)
2128 afs_int32 dbAddFlag = 0;
2131 code = bc_UpdateHosts();
2133 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2137 /* check for flag */
2138 if (as->parms[0].items != 0) { /* add scan to database */
2142 /* check for port */
2143 if (as->parms[1].items) {
2144 port = getPortOffset(as->parms[1].items->data);
2149 code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2153 /* bc_ParseExpiration
2156 * dates are specified as absolute or relative, the syntax is:
2157 * absolute: at %d/%d/%d [%d:%d] where [..] is optional
2158 * relative: in [%dy][%dm][%dd] where at least one component
2163 bc_ParseExpiration(struct cmd_parmdesc *paramPtr, afs_int32 *expType,
2166 struct cmd_item *itemPtr;
2167 struct ktime_date kt;
2168 char *dateString = 0;
2171 *expType = BC_NO_EXPDATE;
2174 if (!paramPtr->items)
2175 ERROR(0); /* no expiration specified */
2177 /* some form of expiration date specified. First validate the prefix */
2178 itemPtr = paramPtr->items;
2180 if (strcmp(itemPtr->data, "at") == 0) {
2181 *expType = BC_ABS_EXPDATE;
2183 dateString = concatParams(itemPtr->next);
2187 code = ktime_DateToLong(dateString, expDate);
2190 } else if (strcmp(itemPtr->data, "in") == 0) {
2191 *expType = BC_REL_EXPDATE;
2193 dateString = concatParams(itemPtr->next);
2197 code = ParseRelDate(dateString, &kt);
2200 *expDate = ktimeRelDate_ToLong(&kt);
2202 dateString = concatParams(itemPtr);
2206 if (ktime_DateToLong(dateString, expDate) == 0) {
2207 *expType = BC_ABS_EXPDATE;
2208 code = ktime_DateToLong(dateString, expDate);
2211 } else if (ParseRelDate(dateString, &kt) == 0) {
2212 *expType = BC_REL_EXPDATE;
2213 *expDate = ktimeRelDate_ToLong(&kt);
2225 /* database lookup command and routines */
2228 * Currently a single option, volumename to search for. Reports
2229 * all dumps containing the specified volume
2232 bc_dblookupCmd(struct cmd_syndesc *as, void *arock)
2234 struct cmd_item *ciptr;
2237 ciptr = as->parms[0].items;
2238 if (ciptr == 0) /* no argument specified */
2241 code = DBLookupByVolume(ciptr->data);
2247 /* for ubik version */
2249 bc_dbVerifyCmd(struct cmd_syndesc *as, void *arock)
2255 struct hostent *hostPtr;
2259 extern struct udbHandleS udbHandle;
2261 detail = (as->parms[0].items ? 1 : 0); /* print more details */
2264 ubik_BUDB_DbVerify(udbHandle.uh_client, 0, &status, &orphans,
2268 afs_com_err(whoami, code, "; Unable to verify database");
2272 /* verification call succeeded */
2275 printf("Database OK\n");
2277 afs_com_err(whoami, status, "; Database is NOT_OK");
2280 printf("Orphan blocks %d\n", orphans);
2283 printf("Unable to lookup host id\n");
2285 hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2287 printf("Database checker was %d.%d.%d.%d\n",
2288 ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2289 ((host & 0xFF00) >> 8), (host & 0xFF));
2291 printf("Database checker was %s\n", hostPtr->h_name);
2294 return ((status ? -1 : 0));
2298 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2301 deleteDump(afs_uint32 dumpid, /* The dumpid to delete */
2302 afs_int32 port, /* port==-1 means don't go to butc */
2305 afs_int32 code = 0, tcode;
2306 struct budb_dumpEntry dumpEntry;
2307 struct rx_connection *tconn = 0;
2308 afs_int32 i, taskflag, xbsadump;
2309 statusP statusPtr = 0;
2310 budb_dumpsList dumps;
2313 /* If the port is set, we will try to send a delete request to the butc */
2315 tcode = bc_UpdateHosts();
2317 afs_com_err(whoami, tcode, "; Can't retrieve tape hosts");
2321 /* Find the dump in the backup database */
2322 tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2324 afs_com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2328 xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2330 /* If dump is to an XBSA server, connect to butc and send it
2331 * the dump to delete. Butc will contact the XBSA server.
2332 * The dump will not be an appended dump because XBSA butc
2333 * does not support the append option.
2335 if (xbsadump && dumpEntry.nVolumes) {
2336 tcode = ConnectButc(bc_globalConfig, port, &tconn);
2340 tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2342 if (tcode == RXGEN_OPCODE)
2343 tcode = BC_VERSIONFAIL;
2344 afs_com_err(whoami, tcode,
2345 "; Unable to delete dumpID %u via butc", dumpid);
2349 statusPtr = createStatusNode();
2351 statusPtr->taskId = taskId;
2352 statusPtr->port = port;
2353 statusPtr->jobNumber = bc_jobNumber();
2354 statusPtr->flags |= (SILENT | NOREMOVE); /* No msg & keep statusPtr */
2355 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2356 sprintf(statusPtr->taskName, "DeleteDump");
2359 /* Wait for task to finish */
2360 taskflag = waitForTask(taskId);
2361 if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2362 afs_com_err(whoami, BUTX_DELETEOBJFAIL,
2363 "; Unable to delete dumpID %u via butc", dumpid);
2364 ERROR(BUTX_DELETEOBJFAIL); /* the task failed */
2371 deleteStatusNode(statusPtr); /* Clean up statusPtr - because NOREMOVE */
2373 rx_DestroyConnection(tconn); /* Destroy the connection */
2375 /* Remove the dump from the backup database */
2376 if (!code || force) {
2377 dumps.budb_dumpsList_len = 0;
2378 dumps.budb_dumpsList_val = 0;
2380 tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2382 afs_com_err(whoami, tcode,
2383 "; Unable to delete dumpID %u from database", dumpid);
2384 dumps.budb_dumpsList_len = 0;
2389 /* Display the dumps that were deleted - includes appended dumps */
2390 for (i = 0; i < dumps.budb_dumpsList_len; i++)
2391 printf(" %u%s\n", dumps.budb_dumpsList_val[i],
2392 (i > 0) ? " Appended Dump" : NULL);
2393 if (dumps.budb_dumpsList_val)
2394 free(dumps.budb_dumpsList_val);
2401 * Delete a specified dump from the database
2403 * dump id - single required arg as param 0.
2406 bc_deleteDumpCmd(struct cmd_syndesc *as, void *arock)
2410 afs_int32 rcode = 0;
2411 afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2412 struct cmd_item *ti;
2413 afs_int32 fromTime = 0, toTime = 0, havetime = 0;
2415 budb_dumpsList dumps, flags;
2417 afs_int32 port = -1, force;
2419 /* Must specify at least one of -dumpid, -from, or -to */
2420 if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2421 && !as->parms[4].items) {
2422 afs_com_err(whoami, 0, "Must specify at least one field");
2426 /* Must have -to option with -from option */
2427 if (as->parms[1].items && !as->parms[2].items) {
2428 afs_com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2432 /* Get the time to delete from */
2433 if (as->parms[1].items) { /* -from */
2434 timeString = concatParams(as->parms[1].items);
2439 * Now parse this string for the time to start.
2441 code = ktime_DateToLong(timeString, &fromTime);
2444 afs_com_err(whoami, 0, "Can't parse 'from' date and time");
2445 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2451 port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0); /* -port */
2452 if (as->parms[5].items) /* -dbonly */
2455 force = (as->parms[6].items ? 1 : 0);
2457 havegroupid = (as->parms[4].items ? 1 : 0);
2459 groupId = atoi(as->parms[4].items->data);
2461 if (as->parms[7].items || as->parms[8].items) {
2462 /* -noexecute (hidden) or -dryrun used */
2468 /* Get the time to delete to */
2469 if (as->parms[2].items) { /* -to */
2470 timeString = concatParams(as->parms[2].items);
2475 * Now parse this string for the time to start. Simce
2476 * times are at minute granularity, add 59 seconds.
2478 code = ktime_DateToLong(timeString, &toTime);
2481 afs_com_err(whoami, 0, "Can't parse 'to' date and time");
2482 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2489 if (fromTime > toTime) {
2490 afs_com_err(whoami, 0,
2491 "'-from' date/time cannot be later than '-to' date/time");
2495 /* Remove speicific dump ids - if any */
2496 printf("The following dumps %s deleted:\n",
2497 (noexecute ? "would have been" : "were"));
2498 for (ti = as->parms[0].items; ti != 0; ti = ti->next) { /* -dumpid */
2499 dumpid = atoi(ti->data);
2501 code = deleteDump(dumpid, port, force);
2503 printf(" %u\n", dumpid);
2508 * Now remove dumps between to and from dates.
2510 if (havegroupid || havetime) {
2511 dumps.budb_dumpsList_len = 0;
2512 dumps.budb_dumpsList_val = 0;
2513 flags.budb_dumpsList_len = 0;
2514 flags.budb_dumpsList_val = 0;
2517 sflags |= BUDB_OP_GROUPID;
2519 sflags |= BUDB_OP_DATES;
2522 bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2524 afs_com_err(whoami, code,
2525 "; Error while deleting dumps from %u to %u", fromTime,
2530 for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2531 if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2535 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2537 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2538 /* Ignore code and continue */
2540 printf(" %u%s%s\n", dumps.budb_dumpsList_val[i],
2542 budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2543 " Appended Dump" : "",
2545 budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2546 " Database Dump" : "");
2551 if (dumps.budb_dumpsList_val)
2552 free(dumps.budb_dumpsList_val);
2553 dumps.budb_dumpsList_len = 0;
2554 dumps.budb_dumpsList_val = 0;
2555 if (flags.budb_dumpsList_val)
2556 free(flags.budb_dumpsList_val);
2557 flags.budb_dumpsList_len = 0;
2558 flags.budb_dumpsList_val = 0;
2565 bc_saveDbCmd(struct cmd_syndesc *as, void *arock)
2567 struct rx_connection *tconn;
2568 afs_int32 portOffset = 0;
2575 code = bc_UpdateHosts();
2577 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2581 if (as->parms[0].items) {
2582 portOffset = getPortOffset(as->parms[0].items->data);
2587 /* Get the time to delete to */
2588 if (as->parms[1].items) {
2589 timeString = concatParams(as->parms[1].items);
2594 * Now parse this string for the time. Since
2595 * times are at minute granularity, add 59 seconds.
2597 code = ktime_DateToLong(timeString, &toTime);
2600 afs_com_err(whoami, 0, "Can't parse '-archive' date and time");
2601 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2608 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2612 code = TC_SaveDb(tconn, toTime, &taskId);
2614 afs_com_err(whoami, code, "; Failed to save database");
2618 /* create status monitor block */
2619 statusPtr = createStatusNode();
2621 statusPtr->taskId = taskId;
2622 statusPtr->port = portOffset;
2623 statusPtr->jobNumber = bc_jobNumber();
2624 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2625 sprintf(statusPtr->taskName, "SaveDb");
2629 rx_DestroyConnection(tconn);
2634 bc_restoreDbCmd(struct cmd_syndesc *as, void *arock)
2636 struct rx_connection *tconn;
2637 afs_int32 portOffset = 0;
2642 code = bc_UpdateHosts();
2644 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2648 if (as->parms[0].items) {
2649 portOffset = getPortOffset(as->parms[0].items->data);
2654 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2658 code = TC_RestoreDb(tconn, &taskId);
2660 afs_com_err(whoami, code, "; Failed to restore database");
2664 /* create status monitor block */
2665 statusPtr = createStatusNode();
2667 statusPtr->taskId = taskId;
2668 statusPtr->port = portOffset;
2669 statusPtr->jobNumber = bc_jobNumber();
2670 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2671 sprintf(statusPtr->taskName, "RestoreDb");
2675 rx_DestroyConnection(tconn);
2679 /* ----------------------------------
2680 * supporting routines for database examination
2681 * ----------------------------------
2684 /* structures and defines for DBLookupByVolume */
2686 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2688 /* dumpedVol - saves interesting information so that we can print it out
2693 struct dumpedVol *next;
2695 afs_int32 initialDumpID;
2696 char tapeName[BU_MAXTAPELEN];
2699 afs_int32 createTime;
2700 afs_int32 incTime; /* actually the clone time */
2703 /* -----------------------------------------
2704 * routines for examining the database
2705 * -----------------------------------------
2709 * Lookup the volumename in the backup database and print the results
2711 * volumeName - volume to lookup
2715 DBLookupByVolume(char *volumeName)
2717 struct budb_dumpEntry dumpEntry;
2718 struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2719 afs_int32 numEntries;
2720 afs_int32 tapedumpid;
2721 afs_int32 last, next;
2723 struct dumpedVol *dvptr = 0;
2724 struct dumpedVol *tempPtr = 0;
2727 char vname[BU_MAXNAMELEN];
2730 for (pass = 0; pass < 2; pass++) {
2732 /* On second pass, search for backup volume */
2734 if (!BackupName(volumeName)) {
2735 strcpy(vname, volumeName);
2736 strcat(vname, ".backup");
2744 while (next != -1) { /*w */
2746 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2747 DBL_MAX_VOLUMES, &numEntries);
2751 /* add the volumes to the list */
2752 for (i = 0; i < numEntries; i++) { /*f */
2753 struct dumpedVol *insPtr, **prevPtr;
2755 tempPtr = calloc(1, sizeof(struct dumpedVol));
2759 tempPtr->incTime = volumeEntry[i].clone;
2760 tempPtr->dumpID = volumeEntry[i].dump;
2761 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2764 /* check if we need to null terminate it - just for safety */
2765 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2766 tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2768 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2774 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2775 tempPtr->parent = dumpEntry.parent;
2776 tempPtr->level = dumpEntry.level;
2777 tempPtr->createTime = dumpEntry.created;
2779 /* add volume to list in reverse chronological order */
2783 while ((insPtr != 0)
2784 && (insPtr->createTime > tempPtr->createTime)
2786 prevPtr = &insPtr->next;
2787 insPtr = insPtr->next;
2790 /* now at the right place - insert the block */
2791 tempPtr->next = *prevPtr;
2801 ("DumpID lvl parentID creation date clone date tape name\n");
2802 for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2803 /* For the user, the tape name is its name and initial dump id */
2805 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2808 /* beware the static items in compactDateString */
2809 compactDateString(&tempPtr->createTime, ds, 50);
2810 printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2811 tempPtr->parent, ds);
2812 compactDateString(&tempPtr->incTime, ds, 50);
2813 printf(" %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2819 for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2820 dvptr = dvptr->next;
2825 afs_com_err(whoami, code, NULL);
2829 /* structures for dumpInfo */
2832 struct volumeLink *nextVolume;
2833 struct budb_volumeEntry volumeEntry;
2837 struct tapeLink *nextTape;
2838 struct budb_tapeEntry tapeEntry;
2839 struct volumeLink *firstVolume;
2844 * print information about a dump and all its tapes and volumes.
2848 dumpInfo(afs_int32 dumpid, afs_int32 detailFlag)
2850 struct budb_dumpEntry dumpEntry;
2851 struct tapeLink *head = 0;
2852 struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2853 struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2856 afs_int32 last, next, dbTime;
2857 afs_int32 tapedumpid;
2865 extern struct udbHandleS udbHandle;
2868 lastTapeLinkPtr = 0;
2870 lastVolumeLinkPtr = 0;
2872 /* first get information about the dump */
2874 code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2878 /* For the user, the tape name is its name and initial dump id */
2879 tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2881 /* Is this a database dump id or not */
2882 if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2887 /* print out the information about the dump */
2891 printDumpEntry(&dumpEntry);
2893 time_t t = dumpEntry.created;
2895 printf("Dump: id %u, created: %s\n", dumpEntry.id,
2898 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2899 dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2903 if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2904 && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2905 printf("Backup Service: TSM: Server: %s\n",
2906 dumpEntry.tapes.tapeServer);
2909 /* now get the list of tapes */
2910 for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) { /*f */
2911 tapeLinkPtr = calloc(1, sizeof(struct tapeLink));
2913 afs_com_err(whoami, BC_NOMEM, NULL);
2917 code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2924 /* add this tape to previous chain */
2925 if (lastTapeLinkPtr) {
2926 lastTapeLinkPtr->nextTape = tapeLinkPtr;
2927 lastTapeLinkPtr = tapeLinkPtr;
2932 lastTapeLinkPtr = head;
2936 while (next != -1) { /*wn */
2937 vl.budb_volumeList_len = 0;
2938 vl.budb_volumeList_val = 0;
2941 /* now get all the volumes in this dump. */
2942 code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name, /* tape name */
2943 dumpid, /* dumpid (not initial dumpid) */
2946 &next, /* nextindex */
2947 &dbTime, /* update time */
2951 if (code == BUDB_ENDOFLIST) { /* 0 volumes on tape */
2958 for (i = 0; i < vl.budb_volumeList_len; i++) {
2959 link = &tapeLinkPtr->firstVolume;
2961 volumeLinkPtr = calloc(1, sizeof(struct volumeLink));
2962 if (!volumeLinkPtr) {
2963 afs_com_err(whoami, BC_NOMEM, NULL);
2967 memcpy(&volumeLinkPtr->volumeEntry,
2968 &vl.budb_volumeList_val[i],
2969 sizeof(struct budb_volumeEntry));
2971 /* now insert it onto the right place */
2973 && (volumeLinkPtr->volumeEntry.position >
2974 (*link)->volumeEntry.position)) {
2975 link = &((*link)->nextVolume);
2978 /* now link it in */
2979 volumeLinkPtr->nextVolume = *link;
2980 *link = volumeLinkPtr;
2983 if (vl.budb_volumeList_val)
2984 free(vl.budb_volumeList_val);
2988 for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
2992 printTapeEntry(&tapeLinkPtr->tapeEntry);
2994 printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
2996 printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
2997 compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
2998 printf("created %16s", ds);
2999 if (tapeLinkPtr->tapeEntry.expires != 0) {
3000 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3001 printf(", expires %16s", ds);
3006 /* print out all the volumes */
3008 /* print header for volume listing - db dumps have no volumes */
3009 if ((detailFlag == 0) && !dbDump)
3010 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3013 for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
3014 volumeLinkPtr = volumeLinkPtr->nextVolume) {
3016 printf("\nVolume\n");
3018 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3020 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3021 printf("%4d %s %10u %16s\n",
3022 volumeLinkPtr->volumeEntry.position, ds,
3023 volumeLinkPtr->volumeEntry.nBytes,
3024 volumeLinkPtr->volumeEntry.name);
3031 afs_com_err("dumpInfo", code, "; Can't get dump information");
3033 /* free all allocated structures */
3035 while (tapeLinkPtr) {
3036 volumeLinkPtr = tapeLinkPtr->firstVolume;
3037 while (volumeLinkPtr) {
3038 lastVolumeLinkPtr = volumeLinkPtr;
3039 volumeLinkPtr = volumeLinkPtr->nextVolume;
3040 free(lastVolumeLinkPtr);
3043 lastTapeLinkPtr = tapeLinkPtr;
3044 tapeLinkPtr = tapeLinkPtr->nextTape;
3045 free(lastTapeLinkPtr);
3051 compareDump(struct budb_dumpEntry *ptr1, struct budb_dumpEntry *ptr2)
3053 if (ptr1->created < ptr2->created)
3055 else if (ptr1->created > ptr2->created)
3061 printRecentDumps(int ndumps)
3064 afs_int32 nextindex, index = 0;
3067 struct budb_dumpEntry *dumpPtr;
3071 extern struct udbHandleS udbHandle;
3073 do { /* while (nextindex != -1) */
3074 /* initialize the dump list */
3075 dl.budb_dumpList_len = 0;
3076 dl.budb_dumpList_val = 0;
3078 /* outline algorithm */
3079 code = ubik_BUDB_GetDumps(udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "", /* no name */
3083 &nextindex, &dbTime, &dl);
3085 if (code == BUDB_ENDOFLIST)
3087 afs_com_err("dumpInfo", code, "; Can't get dump information");
3091 /* No need to sort, it's already sorted */
3093 if (dl.budb_dumpList_len && (index == 0))
3094 printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3095 "parentid", "lv", "created", "nt", "nvols");
3097 dumpPtr = dl.budb_dumpList_val;
3098 for (i = 1; i <= dl.budb_dumpList_len; i++) {
3099 compactDateString(&dumpPtr->created, ds, 50),
3100 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3101 dumpPtr->parent, dumpPtr->level, ds,
3102 dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3103 dumpPtr->nVolumes, dumpPtr->name);
3104 if (dumpPtr->initialDumpID) /* an appended dump */
3105 printf(" (%u)", dumpPtr->initialDumpID);
3106 else if (dumpPtr->appendedDumpID) /* has appended dumps */
3107 printf(" (%u)", dumpPtr->id);
3113 if (dl.budb_dumpList_val)
3114 free(dl.budb_dumpList_val);
3116 } while (nextindex != -1);
3122 * list the dumps and contens of the dumps.
3128 bc_dumpInfoCmd(struct cmd_syndesc *as, void *arock)
3131 afs_int32 detailFlag;
3135 if (as->parms[0].items) {
3136 if (as->parms[1].items) {
3137 afs_com_err(whoami, 0,
3138 "These options are exclusive - select only one");
3141 ndumps = atoi(as->parms[0].items->data);
3143 afs_com_err(whoami, 0, "Must provide a positive number");
3147 code = printRecentDumps(ndumps);
3148 } else if (as->parms[1].items) {
3149 detailFlag = (as->parms[2].items ? 1 : 0); /* 1 = detailed listing */
3150 dumpid = atoi(as->parms[1].items->data);
3151 code = dumpInfo(dumpid, detailFlag);
3153 code = printRecentDumps(10);