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>
15 #if defined(AFS_LINUX24_ENV)
16 #define _REGEX_RE_COMP
18 #include <sys/types.h>
19 #if defined(AFS_LINUX24_ENV)
26 #include <sys/socket.h>
27 #include <netinet/in.h>
31 #include <afs/com_err.h>
34 #include <afs/bubasics.h> /* PA */
35 #include <afs/volser.h>
36 #include <afs/voldefs.h> /* PA */
37 #include <afs/vldbint.h> /* PA */
38 #include <afs/ktime.h> /* PA */
43 #include <afs/tcdata.h>
45 #include <afs/vsutils_prototypes.h>
47 #include "error_macros.h"
48 #include "bucoord_prototypes.h"
51 extern struct bc_config *bc_globalConfig;
52 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
53 extern struct ubik_client *cstruct;
55 extern struct ktc_token ttoken;
58 extern afs_int32 lastTaskCode;
60 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
62 static int EvalVolumeSet1(struct bc_config *aconfig, struct bc_volumeSet *avs,
63 struct bc_volumeDump **avols,
64 struct ubik_client *uclient);
66 static int EvalVolumeSet2(struct bc_config *aconfig, struct bc_volumeSet *avs,
67 struct bc_volumeDump **avols,
68 struct ubik_client *uclient);
69 static int DBLookupByVolume(char *volumeName);
72 bc_EvalVolumeSet(struct bc_config *aconfig,
73 struct bc_volumeSet *avs,
74 struct bc_volumeDump **avols,
75 struct ubik_client *uclient)
76 { /*bc_EvalVolumeSet */
78 static afs_int32 use = 2;
80 if (use == 2) { /* Use EvalVolumeSet2() */
81 code = EvalVolumeSet2(aconfig, avs, avols, uclient);
82 if (code == RXGEN_OPCODE)
85 if (use == 1) { /* Use EvalVolumeSet1() */
86 code = EvalVolumeSet1(aconfig, avs, avols, uclient);
89 } /*bc_EvalVolumeSet */
91 struct partitionsort {
93 struct bc_volumeDump *vdlist;
94 struct bc_volumeDump *lastvdlist;
95 struct bc_volumeDump *dupvdlist;
96 struct bc_volumeEntry *vole;
97 struct partitionsort *next;
101 struct partitionsort *partitions;
102 struct serversort *next;
106 getSPEntries(afs_uint32 server, afs_int32 partition,
107 struct serversort **serverlist,
108 struct serversort **ss,
109 struct partitionsort **ps)
111 if (!(*ss) || ((*ss)->ipaddr != server)) {
113 for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
114 if ((*ss)->ipaddr == server)
118 /* No server entry added. Add one */
120 *ss = (struct serversort *)malloc(sizeof(struct serversort));
122 afs_com_err(whoami, BC_NOMEM, "");
126 memset(*ss, 0, sizeof(struct serversort));
127 (*ss)->ipaddr = server;
128 (*ss)->next = *serverlist;
133 if (!(*ps) || ((*ps)->part != partition)) {
134 for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
135 if ((*ps)->part == partition)
139 /* No partition entry added. Add one */
141 *ps = (struct partitionsort *)malloc(sizeof(struct partitionsort));
143 afs_com_err(whoami, BC_NOMEM, "");
149 memset(*ps, 0, sizeof(struct partitionsort));
150 (*ps)->part = partition;
151 (*ps)->next = (*ss)->partitions;
152 (*ss)->partitions = *ps;
158 randSPEntries(struct serversort *serverlist,
159 struct bc_volumeDump **avols)
161 struct serversort *ss, **pss;
162 struct partitionsort *ps, **pps;
164 afs_int32 scount, pcount;
168 /* Seed random number generator */
169 r = time(0) + getpid();
172 /* Count number of servers, remove one at a time */
173 for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
174 for (; scount; scount--) {
175 /* Pick a random server in list and remove it */
176 r = (rand() >> 4) % scount;
177 for (pss = &serverlist, ss = serverlist; r;
178 pss = &ss->next, ss = ss->next, r--);
181 /* Count number of partitions, remove one at a time */
182 for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
183 for (; pcount; pcount--) {
184 /* Pick a random parition in list and remove it */
185 r = (rand() >> 4) % pcount;
186 for (pps = &ss->partitions, ps = ss->partitions; r;
187 pps = &ps->next, ps = ps->next, r--);
190 ps->lastvdlist->next = *avols;
200 EvalVolumeSet2(struct bc_config *aconfig,
201 struct bc_volumeSet *avs,
202 struct bc_volumeDump **avols,
203 struct ubik_client *uclient)
204 { /*EvalVolumeSet2 */
205 struct bc_volumeEntry *tve;
206 struct bc_volumeDump *tavols;
207 struct VldbListByAttributes attributes;
208 afs_int32 si, nsi; /* startIndex and nextStartIndex */
209 afs_int32 nentries, e, ei, et, add, l;
210 nbulkentries bulkentries;
211 struct nvldbentry *entries = 0;
212 struct bc_volumeDump *tvd;
213 afs_int32 code = 0, tcode;
215 struct serversort *servers = 0, *ss = 0;
216 struct partitionsort *ps = 0;
218 *avols = (struct bc_volumeDump *)0;
219 bulkentries.nbulkentries_len = 0;
220 bulkentries.nbulkentries_val = 0;
222 /* For each of the volume set entries - collect the volumes that match it */
223 for (tve = avs->ventries; tve; tve = tve->next) {
224 /* Put together a call to the vlserver for this vlentry. The
225 * performance gain is from letting the vlserver expand the
226 * volumeset and not this routine.
229 if (tve->server.sin_addr.s_addr) { /* The server */
230 attributes.Mask |= VLLIST_SERVER;
231 attributes.server = tve->server.sin_addr.s_addr;
233 if (tve->partition != -1) { /* The partition */
234 attributes.Mask |= VLLIST_PARTITION;
235 attributes.partition = tve->partition;
238 /* Now make the call to the vlserver */
239 for (si = 0; si != -1; si = nsi) {
241 bulkentries.nbulkentries_len = 0;
242 bulkentries.nbulkentries_val = 0;
245 ubik_Call(VL_ListAttributesN2, uclient, 0, &attributes,
246 tve->name, si, &nentries, &bulkentries, &nsi);
250 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
251 * it is not complete. The only way to tell if it is not complete
252 * is if et == 0 (which we check for below). Also, if the call didn't
253 * match any entries, then we don't know what version the vlserver
254 * is. In both cases, we return RXGEN_OPCODE and the calling routine
255 * will switch to the EvalVolumeSet1() call.
258 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
260 /* Step through each entry and add it to the list of volumes */
261 entries = bulkentries.nbulkentries_val;
262 for (e = 0; e < nentries; e++) {
263 ei = entries[e].matchindex & 0xffff;
264 et = (entries[e].matchindex >> 16) & 0xffff;
279 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
282 /* Find server and partiton structure to hang the entry off of */
284 getSPEntries(entries[e].serverNumber[ei],
285 entries[e].serverPartition[ei], &servers,
288 afs_com_err(whoami, tcode, "");
292 /* Detect if this entry should be added (not a duplicate).
293 * Use ps->dupvdlist and ps->vole to only search volumes from
294 * previous volume set entries.
297 if (tve != avs->ventries) {
298 l = strlen(entries[e].name);
299 if (ps->vole != tve) {
301 ps->dupvdlist = ps->vdlist;
303 for (tavols = ps->dupvdlist; add && tavols;
304 tavols = tavols->next) {
305 if (strncmp(tavols->name, entries[e].name, l) == 0) {
306 if ((strcmp(&entries[e].name[l], ".backup") == 0)
307 || (strcmp(&entries[e].name[l], ".readonly")
309 || (strcmp(&entries[e].name[l], "") == 0))
316 /* Allocate a volume dump structure and its name */
317 tvd = (struct bc_volumeDump *)
318 malloc(sizeof(struct bc_volumeDump));
320 afs_com_err(whoami, BC_NOMEM, "");
323 memset(tvd, 0, sizeof(*tvd));
325 tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
327 afs_com_err(whoami, BC_NOMEM, "");
332 /* Fill it in and thread onto avols list */
333 strcpy(tvd->name, entries[e].name);
335 strcat(tvd->name, ".backup");
336 else if (et == ROVOL)
337 strcat(tvd->name, ".readonly");
338 tvd->vid = entries[e].volumeId[et];
341 tvd->partition = entries[e].serverPartition[ei];
342 tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
343 tvd->server.sin_port = 0; /* default FS port */
344 tvd->server.sin_family = AF_INET;
345 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
346 tvd->server.sin_len = sizeof(struct sockaddr_in);
349 /* String tvd off of partition struct */
350 tvd->next = ps->vdlist;
353 ps->lastvdlist = tvd;
359 /* Free memory allocated during VL call */
360 if (bulkentries.nbulkentries_val) {
361 free((char *)bulkentries.nbulkentries_val);
362 bulkentries.nbulkentries_val = 0;
368 /* Randomly link the volumedump entries together */
369 randSPEntries(servers, avols);
370 fprintf(stderr, "Total number of volumes : %u\n", count);
373 if (bulkentries.nbulkentries_val) {
374 free((char *)bulkentries.nbulkentries_val);
377 } /*EvalVolumeSet2 */
379 /*-----------------------------------------------------------------------------
383 * Takes the entries in a volumeset and expands them into a list of
384 * volumes. Every VLDB volume entry is looked at and compared to the
387 * When matching a VLDB volume entry to a volumeset entry,
388 * 1. If the RW volume entry matches, that RW volume is used.
389 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
390 * 3. Finally, if the RO volume entry matches, the RO volume is used.
391 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
392 * "user.troy" and "user.troy.backup". The rules will use
393 * the RW volume "user.troy".
395 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
396 * that volume is used and matches against any remaining volumeset entries
398 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
399 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
400 * would have matched its RW volume "user.troy", but the first
401 * match is used and the second match isn't even done.
404 * aconfig : Global configuration info.
406 * avols : Ptr to linked list of entries describing volumes to dump.
407 * uclient : Ptr to Ubik client structure.
410 * 0 on successful volume set evaluation,
411 * Lower-level codes otherwise.
414 * Expand only the single volume set provided (avs); don't go down its chain.
418 *-----------------------------------------------------------------------------
421 EvalVolumeSet1(struct bc_config *aconfig,
422 struct bc_volumeSet *avs,
423 struct bc_volumeDump **avols,
424 struct ubik_client *uclient)
425 { /*EvalVolumeSet1 */
426 afs_int32 code; /*Result of various calls */
428 struct bc_volumeDump *tvd; /*Ptr to new dump instance */
429 struct bc_volumeEntry *tve, *ctve; /*Ptr to new volume entry instance */
430 char patt[256]; /*Composite regex; also, target string */
431 int volType = 0; /*Type of volume that worked */
432 afs_int32 index; /*Current VLDB entry index */
433 afs_int32 count; /*Needed by VL_ListEntry() */
434 afs_int32 next_index; /*Next index to list */
435 struct vldbentry entry; /*VLDB entry */
436 int srvpartpair; /*Loop counter: server/partition pair */
440 struct serversort *servers = 0, *ss = 0;
441 struct partitionsort *ps = 0;
443 *avols = (struct bc_volumeDump *)0;
444 ctve = (struct bc_volumeEntry *)0; /* no compiled entry */
446 /* For each vldb entry.
447 * Variable next_index is set to the index of the next VLDB entry
448 * in the enumeration.
450 for (index = 0; 1; index = next_index) { /*w */
451 memset(&entry, 0, sizeof(entry));
452 code = ubik_Call(VL_ListEntry, /*Routine to invoke */
453 uclient, /*Ubik client structure */
455 index, /*Current index */
456 &count, /*Ptr to working variable */
457 &next_index, /*Ptr to next index value to list */
458 &entry); /*Ptr to entry to fill */
462 break; /* If the next index is invalid, bail out now. */
464 /* For each entry in the volume set */
465 found = 0; /* No match in volume set yet */
466 for (tve = avs->ventries; tve; tve = tve->next) { /*ve */
467 /* for each server in the vldb entry */
468 for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) { /*s */
469 /* On the same server */
470 if (tve->server.sin_addr.s_addr
471 && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
472 entry.serverNumber[srvpartpair],
480 /* On the same partition */
481 if ((tve->partition != -1)
482 && (tve->partition != entry.serverPartition[srvpartpair]))
485 /* If the volume entry is not compiled, then compile it */
487 sprintf(patt, "^%s$", tve->name);
488 errm = (char *)re_comp(patt);
490 afs_com_err(whoami, 0,
491 "Can't compile regular expression '%s': %s",
498 /* If the RW name matches the volume set entry, take
499 * it and exit. First choice is to use the RW volume.
501 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
502 if (entry.flags & RW_EXISTS) {
503 sprintf(patt, "%s", entry.name);
504 code = re_exec(patt);
507 foundentry = srvpartpair;
513 /* If the BK name matches the volume set entry, take
514 * it and exit. Second choice is to use the BK volume.
516 if (entry.flags & BACK_EXISTS) {
517 sprintf(patt, "%s.backup", entry.name);
518 code = re_exec(patt);
521 foundentry = srvpartpair;
528 /* If the RO name matches the volume set entry, remember
529 * it, but continue searching. Further entries may be
530 * RW or backup entries that will match.
532 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
533 && (entry.flags & RO_EXISTS)) {
534 sprintf(patt, "%s.readonly", entry.name);
535 code = re_exec(patt);
538 foundentry = srvpartpair;
544 afs_com_err(whoami, 0, "Internal error in regex package");
547 /* If found a match, then create a new volume dump entry */
549 /* Find server and partition structure to hang the entry off of */
551 getSPEntries(entry.serverNumber[foundentry],
552 entry.serverPartition[foundentry], &servers,
555 afs_com_err(whoami, code, "");
560 tvd = (struct bc_volumeDump *)
561 malloc(sizeof(struct bc_volumeDump));
563 afs_com_err(whoami, BC_NOMEM, "");
566 memset(tvd, 0, sizeof(*tvd));
568 tvd->name = (char *)malloc(strlen(entry.name) + 10);
570 afs_com_err(whoami, BC_NOMEM, "");
575 strcpy(tvd->name, entry.name);
576 if (volType == BACKVOL)
577 strcat(tvd->name, ".backup");
578 else if (volType == ROVOL)
579 strcat(tvd->name, ".readonly");
580 tvd->vid = entry.volumeId[volType];
582 tvd->volType = volType;
583 tvd->partition = entry.serverPartition[foundentry];
584 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
585 tvd->server.sin_port = 0; /* default FS port */
586 tvd->server.sin_family = AF_INET;
588 /* String tvd off of partition struct */
589 tvd->next = ps->vdlist;
592 ps->lastvdlist = tvd;
599 /* Randomly link the volumedump entries together */
600 randSPEntries(servers, avols);
602 fprintf(stderr, "Total number of volumes : %u\n", total);
604 } /*EvalVolumeSet1 */
607 * print out a date in compact format, 16 chars, format is
610 * date_long - ptr to a long containing the time
612 * ptr to a string containing a representation of the date
615 compactDateString(afs_uint32 *date_long, char *string, afs_int32 size)
622 if (*date_long == NEVERDATE) {
623 sprintf(string, "NEVER");
625 time_t t = *date_long;
626 ltime = localtime(&t);
627 /* prints date in U.S. format of mm/dd/yyyy */
628 strftime(string, size, "%m/%d/%Y %H:%M", ltime);
634 bc_SafeATOI(char *anum)
638 for (; *anum; anum++) {
639 if ((*anum < '0') || (*anum > '9'))
641 total = (10 * total) + (afs_int32) (*anum - '0');
647 * Take a string and parse it for a number (could be float) followed
648 * by a character representing the units (K,M,G,T). Default is 'K'.
649 * Return the size in KBytes.
652 bc_FloatATOI(char *anum)
656 afs_int32 fraction = 0; /* > 0 if past the decimal */
658 for (; *anum; anum++) {
659 if ((*anum == 't') || (*anum == 'T')) {
660 total *= 1024 * 1024 * 1024;
663 if ((*anum == 'g') || (*anum == 'G')) {
664 total *= 1024 * 1024;
667 if ((*anum == 'm') || (*anum == 'M')) {
671 if ((*anum == 'k') || (*anum == 'K')) {
678 if ((*anum < '0') || (*anum > '9'))
682 total = (10. * total) + (float)(*anum - '0');
684 total += ((float)(*anum - '0')) / (float)fraction;
689 total += 0.5; /* Round up */
690 if (total > 0x7fffffff) /* Don't go over 2G */
692 rtotal = (afs_int32) total;
696 /* make a copy of a string so that it can be freed later */
698 bc_CopyString(char *astring)
704 return (NULL); /* propagate null strings easily */
705 tlen = strlen(astring);
706 tp = (char *)malloc(tlen + 1); /* don't forget the terminating null */
708 afs_com_err(whoami, BC_NOMEM, "");
717 * Concatenates the parameters of an option and returns the string.
722 concatParams(struct cmd_item *itemPtr)
724 struct cmd_item *tempPtr;
725 afs_int32 length = 0;
728 /* compute the length of string required */
729 for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
730 length += strlen(tempPtr->data);
731 length++; /* space or null terminator */
734 if (length == 0) { /* no string (0 length) */
735 afs_com_err(whoami, 0, "Can't have zero length date and time string");
739 string = (char *)malloc(length); /* allocate the string */
741 afs_com_err(whoami, BC_NOMEM, "");
746 tempPtr = itemPtr; /* now assemble the string */
748 strcat(string, tempPtr->data);
749 tempPtr = tempPtr->next;
754 return (string); /* return the string */
758 * print out an interface status node as received from butc
762 printIfStatus(struct tciStatusS *statusPtr)
764 printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
765 if (statusPtr->nKBytes)
766 printf("%ld Kbytes transferred", (long unsigned int) statusPtr->nKBytes);
767 if (strlen(statusPtr->volumeName) != 0) {
768 if (statusPtr->nKBytes)
770 printf("volume %s", statusPtr->volumeName);
775 if (statusPtr->flags & ABORT_REQUEST)
776 printf(" [abort request rcvd]");
778 if (statusPtr->flags & ABORT_DONE)
779 printf(" [abort complete]");
781 if (statusPtr->flags & OPR_WAIT)
782 printf(" [operator wait]");
784 if (statusPtr->flags & CALL_WAIT)
785 printf(" [callout in progress]");
787 if (statusPtr->flags & DRIVE_WAIT)
788 printf(" [drive wait]");
790 if (statusPtr->flags & TASK_DONE)
796 getPortOffset(char *port)
798 afs_int32 portOffset;
800 portOffset = bc_SafeATOI(port);
802 if (portOffset < 0) {
803 afs_com_err(whoami, 0, "Can't decode port offset '%s'", port);
805 } else if (portOffset > BC_MAXPORTOFFSET) {
806 afs_com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
813 /* bc_GetTapeStatusCmd
814 * display status of all tasks on a particular tape coordinator
817 bc_GetTapeStatusCmd(struct cmd_syndesc *as, void *arock)
820 struct rx_connection *tconn;
821 afs_int32 portOffset = 0;
826 struct tciStatusS status;
828 code = bc_UpdateHosts();
830 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
834 if (as->parms[0].items) {
835 portOffset = getPortOffset(as->parms[0].items->data);
840 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
844 flags = TSK_STAT_FIRST;
848 while ((flags & TSK_STAT_END) == 0) {
849 code = TC_ScanStatus(tconn, &taskId, &status, &flags);
851 if (code == TC_NOTASKS)
853 afs_com_err(whoami, code, "; Can't get status from butc");
856 if ((flags & TSK_STAT_NOTFOUND))
857 break; /* Can't find the task id */
858 flags &= ~TSK_STAT_FIRST; /* turn off flag */
860 printIfStatus(&status);
865 printf("Tape coordinator is idle\n");
867 if (flags & TSK_STAT_ADSM)
868 printf("TSM Tape coordinator\n");
869 else if (flags & TSK_STAT_XBSA)
870 printf("XBSA Tape coordinator\n");
875 extern struct Lock dispatchLock;
878 * wait for all jobs to terminate
881 bc_WaitForNoJobs(void)
884 int usefulJobRunning = 1;
886 extern dlqlinkT statusHead;
888 afs_com_err(whoami, 0, "waiting for job termination");
890 while (usefulJobRunning) {
891 usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
892 if (dispatchLock.excl_locked)
893 usefulJobRunning = 1;
894 for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
895 if (bc_dumpTasks[i].flags & BC_DI_INUSE)
896 usefulJobRunning = 1;
899 /* Wait 5 seconds and check again */
900 if (usefulJobRunning)
903 return (lastTaskCode);
907 * print status on running jobs
909 * ignored - a null "as" prints only jobs.
912 bc_JobsCmd(struct cmd_syndesc *as, void *arock)
921 extern dlqlinkT statusHead;
923 dlqInit(&atJobsHead);
926 ptr = (&statusHead)->dlq_next;
927 while (ptr != &statusHead) {
928 statusPtr = (statusP) ptr;
931 if (statusPtr->scheduledDump) {
932 dlqUnlink((dlqlinkP) statusPtr);
933 dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
935 printf("Job %d:", statusPtr->jobNumber);
937 printf(" %s", statusPtr->taskName);
939 if (statusPtr->dbDumpId)
940 printf(": DumpID %u", statusPtr->dbDumpId);
941 if (statusPtr->nKBytes)
942 printf(", %ld Kbytes", afs_cast_int32(statusPtr->nKBytes));
943 if (strlen(statusPtr->volumeName) != 0)
944 printf(", volume %s", statusPtr->volumeName);
946 if (statusPtr->flags & CONTACT_LOST)
947 printf(" [butc contact lost]");
949 if (statusPtr->flags & ABORT_REQUEST)
950 printf(" [abort request]");
952 if (statusPtr->flags & ABORT_SENT)
953 printf(" [abort sent]");
955 if (statusPtr->flags & OPR_WAIT)
956 printf(" [operator wait]");
958 if (statusPtr->flags & CALL_WAIT)
959 printf(" [callout in progress]");
961 if (statusPtr->flags & DRIVE_WAIT)
962 printf(" [drive wait]");
968 * Now print the scheduled dumps.
970 if (!dlqEmpty(&statusHead) && as)
971 printf("\n"); /* blank line between running and scheduled dumps */
974 while (!dlqEmpty(&atJobsHead)) {
975 ptr = (&atJobsHead)->dlq_next;
976 youngest = (statusP) ptr;
979 while (ptr != &atJobsHead) { /* Find the dump that starts the earliest */
980 statusPtr = (statusP) ptr;
981 if (statusPtr->scheduledDump < youngest->scheduledDump)
982 youngest = statusPtr;
986 /* Print token expiration time */
987 if ((ttoken.endTime > prevTime)
988 && (ttoken.endTime <= youngest->scheduledDump) && as
989 && (ttoken.endTime != NEVERDATE)) {
990 if (ttoken.endTime > time(0)) {
991 compactDateString(&ttoken.endTime, ds, 50);
992 printf(" %16s: TOKEN EXPIRATION\n", ds);
994 printf(" TOKEN HAS EXPIRED\n");
997 prevTime = youngest->scheduledDump;
1000 compactDateString(&youngest->scheduledDump, ds, 50);
1001 printf("Job %d:", youngest->jobNumber);
1002 printf(" %16s: %s", ds, youngest->cmdLine);
1005 /* return to original list */
1006 dlqUnlink((dlqlinkP) youngest);
1007 dlqLinkb(&statusHead, (dlqlinkP) youngest);
1010 /* Print token expiration time if havn't already */
1011 if ((ttoken.endTime == NEVERDATE) && as)
1012 printf(" : TOKEN NEVER EXPIRES\n");
1013 else if ((ttoken.endTime > prevTime) && as) {
1014 if (ttoken.endTime > time(0)) {
1015 compactDateString(&ttoken.endTime, ds, 50);
1016 printf(" %16s: TOKEN EXPIRATION\n", ds);
1018 printf(" : TOKEN HAS EXPIRED\n");
1027 bc_KillCmd(struct cmd_syndesc *as, void *arock)
1031 struct bc_dumpTask *td;
1038 extern dlqlinkT statusHead;
1040 tp = as->parms[0].items->data;
1041 if (strchr(tp, '.') == 0) {
1042 slot = bc_SafeATOI(tp);
1044 afs_com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1049 ptr = (&statusHead)->dlq_next;
1050 while (ptr != &statusHead) {
1051 statusPtr = (statusP) ptr;
1052 if (statusPtr->jobNumber == slot) {
1053 statusPtr->flags |= ABORT_REQUEST;
1057 ptr = ptr->dlq_next;
1061 fprintf(stderr, "Job %d not found\n", slot);
1066 for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1067 if (td->flags & BC_DI_INUSE) {
1069 strcpy(tbuffer, td->volSetName);
1070 strcat(tbuffer, ".");
1071 strcat(tbuffer, tailCompPtr(td->dumpName));
1072 if (strcmp(tbuffer, tp) == 0)
1076 if (i >= BC_MAXSIMDUMPS) {
1077 afs_com_err(whoami, 0, "Can't find job %s", tp);
1082 statusPtr = findStatus(td->dumpID);
1084 if (statusPtr == 0) {
1085 afs_com_err(whoami, 0, "Can't locate status - internal error");
1089 statusPtr->flags |= ABORT_REQUEST;
1096 /* restore a volume or volumes */
1098 bc_VolRestoreCmd(struct cmd_syndesc *as, void *arock)
1101 * parm 0 is the new server to restore to
1102 * parm 1 is the new partition to restore to
1103 * parm 2 is volume(s) to restore
1104 * parm 3 is the new extension, if any, for the volume name.
1105 * parm 4 gives the new volume # to restore this volume as (removed).
1106 * parm 4 date is a string representing the date
1108 * We handle four types of restores. If old is set, then we restore the
1109 * volume with the same name and ID. If old is not set, we allocate
1110 * a new volume ID for the restored volume. If a new extension is specified,
1111 * we add that extension to the volume name of the restored volume.
1113 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1114 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1115 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1116 struct bc_volumeDump *tvol; /* temp for same */
1117 struct sockaddr_in destServ; /* machine to which to restore volumes */
1118 afs_int32 destPartition; /* partition to which to restore volumes */
1120 struct cmd_item *ti;
1124 afs_int32 dumpID = 0;
1125 char *newExt, *timeString;
1127 afs_int32 *ports = NULL;
1128 afs_int32 portCount = 0;
1131 code = bc_UpdateHosts();
1133 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1137 /* specified other destination host */
1138 if (as->parms[0].items) {
1139 tp = as->parms[0].items->data;
1140 if (bc_ParseHost(tp, &destServ)) {
1141 afs_com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1146 /* specified other destination partition */
1147 if (as->parms[1].items) {
1148 tp = as->parms[1].items->data;
1149 if (bc_GetPartitionID(tp, &destPartition)) {
1150 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1155 for (ti = as->parms[2].items; ti; ti = ti->next) {
1156 /* build list of volume items */
1157 tvol = (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1159 afs_com_err(whoami, BC_NOMEM, "");
1162 memset(tvol, 0, sizeof(struct bc_volumeDump));
1164 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1166 afs_com_err(whoami, BC_NOMEM, "");
1169 strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1170 tvol->entry = &tvolumeEntry;
1173 lastVol->next = tvol; /* thread onto end of list */
1175 volsToRestore = tvol;
1179 if (as->parms[4].items) {
1180 timeString = concatParams(as->parms[4].items);
1184 code = ktime_DateToLong(timeString, &fromDate);
1187 afs_com_err(whoami, 0, "Can't parse restore date and time");
1188 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1192 fromDate = 0x7fffffff; /* latest one */
1195 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1198 /* Read all the port offsets into the ports array. The first element in the
1199 * array is for full restore and the rest are for incremental restores
1201 if (as->parms[5].items) {
1202 for (ti = as->parms[5].items; ti; ti = ti->next)
1204 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1206 afs_com_err(whoami, BC_NOMEM, "");
1210 for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1211 ports[i] = getPortOffset(ti->data);
1217 dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1219 if (as->parms[7].items)
1221 dumpID = atoi(as->parms[7].items->data);
1227 * Perform the call to start the restore.
1230 bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1231 &destServ, destPartition, fromDate, newExt, oldFlag,
1232 /*parentDump */ dumpID, /*dumpLevel */ 0,
1233 bc_Restorer, ports, portCount,
1234 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1236 afs_com_err(whoami, code, "; Failed to queue restore");
1241 /* restore a whole partition or server */
1243 /* bc_DiskRestoreCmd
1244 * restore a whole partition
1246 * first, reqd - machine (server) to restore
1247 * second, reqd - partition to restore
1252 bc_DiskRestoreCmd(struct cmd_syndesc *as, void *arock)
1254 struct bc_volumeSet tvolumeSet; /* temporary volume set for EvalVolumeSet call */
1255 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1256 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1257 struct sockaddr_in destServ; /* machine to which to restore volumes */
1258 afs_int32 destPartition; /* partition to which to restore volumes */
1264 afs_int32 *ports = NULL;
1265 afs_int32 portCount = 0;
1267 struct bc_volumeDump *prev, *tvol, *nextvol;
1268 struct cmd_item *ti;
1271 /* parm 0 is the server to restore
1272 * parm 1 is the partition to restore
1274 * parm 8 and above as in VolRestoreCmd:
1275 * parm 8 is the new server to restore to
1276 * parm 9 is the new partition to restore to
1279 code = bc_UpdateVolumeSet();
1281 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1284 code = bc_UpdateHosts();
1286 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1290 /* create a volume set corresponding to the volume pattern we've been given */
1291 memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1292 memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1293 tvolumeSet.name = "TempVolumeSet";
1294 tvolumeSet.ventries = &tvolumeEntry;
1295 tvolumeEntry.serverName = as->parms[0].items->data;
1296 tvolumeEntry.partname = as->parms[1].items->data;
1298 if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1299 afs_com_err(whoami, 0, "Can't parse partition '%s'",
1300 tvolumeEntry.partname);
1304 if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1305 afs_com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1309 /* specified other destination host */
1310 if (as->parms[8].items) {
1311 tp = as->parms[8].items->data;
1312 if (bc_ParseHost(tp, &destServ)) {
1313 afs_com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1316 } else /* use destination host == original host */
1317 memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1319 /* specified other destination partition */
1320 if (as->parms[9].items) {
1321 tp = as->parms[9].items->data;
1322 if (bc_GetPartitionID(tp, &destPartition)) {
1323 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1326 } else /* use original partition */
1327 destPartition = tvolumeEntry.partition;
1329 tvolumeEntry.name = ".*"; /* match all volumes (this should be a parameter) */
1331 if (as->parms[2].items) {
1332 for (ti = as->parms[2].items; ti; ti = ti->next)
1334 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1336 afs_com_err(whoami, BC_NOMEM, "");
1340 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1341 ports[i] = getPortOffset(ti->data);
1347 newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1348 dontExecute = (as->parms[11].items ? 1 : 0); /* -n */
1351 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1354 bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1357 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1361 /* Since we want only RW volumes, remove any
1362 * BK or RO volumes from the list.
1364 for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1365 nextvol = tvol->next;
1366 if (BackupName(tvol->name)) {
1369 "Will not restore volume %s (its RW does not reside on the partition)\n",
1372 if (tvol == volsToRestore) {
1373 volsToRestore = nextvol;
1375 prev->next = nextvol;
1385 fromDate = 0x7fffffff; /* last one before this date */
1386 oldFlag = 1; /* do restore same volid (and name) */
1389 * Perform the call to start the dump.
1392 bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1393 &destServ, destPartition, fromDate, newExt, oldFlag,
1394 /*parentDump */ 0, /*dumpLevel */ 0,
1395 bc_Restorer, ports, portCount,
1396 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1398 afs_com_err(whoami, code, "; Failed to queue restore");
1403 /* bc_VolsetRestoreCmd
1404 * restore a volumeset or list of volumes.
1408 bc_VolsetRestoreCmd(struct cmd_syndesc *as, void *arock)
1415 afs_int32 *ports = NULL;
1416 afs_int32 portCount = 0;
1419 struct bc_volumeSet *volsetPtr; /* Ptr to list of generated volume info */
1420 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1421 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1422 struct sockaddr_in destServer; /* machine to which to restore volume */
1423 afs_int32 destPartition; /* partition to which to restore volumes */
1424 struct cmd_item *ti;
1427 code = bc_UpdateVolumeSet();
1429 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1432 code = bc_UpdateHosts();
1434 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1438 if (as->parms[0].items) {
1439 if (as->parms[1].items) {
1440 afs_com_err(whoami, 0, "Can't have both -name and -file options");
1444 volsetName = as->parms[0].items->data;
1445 volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1447 afs_com_err(whoami, 0,
1448 "Can't find volume set '%s' in backup database",
1453 /* Expand out the volume set into its component list of volumes. */
1455 bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1458 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1461 } else if (as->parms[1].items) {
1464 char server[50], partition[50], volume[50], rest[256];
1466 struct bc_volumeDump *tvol;
1468 fd = fopen(as->parms[1].items->data, "r");
1470 afs_com_err(whoami, errno, "; Cannot open file '%s'",
1471 as->parms[1].items->data);
1475 while (fgets(line, 255, fd)) {
1477 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1483 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1484 fprintf(stderr, " %s", line);
1488 if (bc_ParseHost(server, &destServer)) {
1489 afs_com_err(whoami, 0, "Failed to locate host '%s'", server);
1493 if (bc_GetPartitionID(partition, &destPartition)) {
1494 afs_com_err(whoami, 0,
1495 "Failed to parse destination partition '%s'",
1500 /* Allocate a volumeDump structure and link it in */
1502 (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1503 memset(tvol, 0, sizeof(struct bc_volumeDump));
1505 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1507 afs_com_err(whoami, BC_NOMEM, "");
1510 strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1511 memcpy(&tvol->server, &destServer, sizeof(destServer));
1512 tvol->partition = destPartition;
1515 lastVol->next = tvol; /* thread onto end of list */
1517 volsToRestore = tvol;
1522 afs_com_err(whoami, 0, "-name or -file option required");
1527 /* Get the port offset for the restore */
1528 if (as->parms[2].items) {
1529 for (ti = as->parms[2].items; ti; ti = ti->next)
1531 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1533 afs_com_err(whoami, BC_NOMEM, "");
1537 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1538 ports[i] = getPortOffset(ti->data);
1544 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1545 dontExecute = (as->parms[4].items ? 1 : 0);
1547 fromDate = 0x7fffffff; /* last one before this date */
1548 oldFlag = 1; /* do restore same volid (and name) */
1550 /* Perform the call to start the restore */
1551 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1552 /*destserver */ NULL, /*destpartition */ 0, fromDate,
1554 /*parentDump */ 0, /*dumpLevel */ 0,
1555 bc_Restorer, ports, portCount,
1556 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1558 afs_com_err(whoami, code, "; Failed to queue restore");
1563 /*-----------------------------------------------------------------------------
1567 * Perform a dump of the set of volumes provided.
1568 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1571 * as : Parsed command line information.
1572 * arock : Ptr to misc stuff; not used.
1576 * The result of bc_StartDump() otherwise
1583 *---------------------------------------------------------------------------
1588 bc_DumpCmd(struct cmd_syndesc *as, void *arock)
1590 char *dumpPath = NULL;
1591 char *vsName = NULL; /*Ptrs to various names */
1592 struct bc_volumeSet *tvs = NULL; /*Ptr to list of generated volume info */
1593 struct bc_dumpSchedule *tds;
1594 struct bc_dumpSchedule *baseds = NULL; /*Ptr to dump schedule node */
1595 struct bc_volumeDump *tve, *volsToDump; /*Ptr to individual vols to be dumped */
1596 struct budb_dumpEntry dumpEntry, de, fde; /* dump entry */
1599 afs_int32 parent; /* parent dump */
1600 afs_int32 level; /* this dump's level # */
1601 afs_int32 problemFindingDump; /* can't find parent(s) */
1603 afs_int32 *portp = NULL;
1604 afs_int32 portCount = 0;
1605 afs_int32 doAt, atTime; /* Time a timed-dump is to start at */
1608 int doAppend = 0; /* Append the dump to dump set */
1609 afs_int32 code; /* Return code */
1610 int loadfile; /* whether to load a file or not */
1614 extern struct bc_dumpTask bc_dumpTasks[];
1616 code = bc_UpdateDumpSchedule();
1618 afs_com_err(whoami, code, "; Can't retrieve dump schedule");
1621 code = bc_UpdateVolumeSet();
1623 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1626 code = bc_UpdateHosts();
1628 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1633 * Some parameters cannot be specified together
1634 * The "-file" option cannot exist with the "-volume", "-dump",
1635 * "-portoffset", or "-append" option
1637 if (as->parms[6].items) {
1639 if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1640 || as->parms[4].items) {
1641 afs_com_err(whoami, 0, "Invalid option specified with -file option");
1646 if (!as->parms[0].items || !as->parms[1].items) {
1647 afs_com_err(whoami, 0,
1648 "Must specify volume set name and dump level name");
1654 * Get the time we are to perform this dump
1656 if (as->parms[3].items) {
1659 timeString = concatParams(as->parms[3].items);
1664 * Now parse this string for the time to start.
1666 code = ktime_DateToLong(timeString, &atTime);
1669 afs_com_err(whoami, 0, "Can't parse dump start date and time");
1670 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1676 dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1679 * If this dump is not a load file, then check the parameters.
1681 if (!loadfile) { /*6 */
1682 vsName = as->parms[0].items->data; /* get volume set name */
1683 dumpPath = as->parms[1].items->data; /* get dump path */
1685 /* get the port number, if one was specified */
1686 if (as->parms[2].items) {
1688 portp = (afs_int32 *) malloc(sizeof(afs_int32));
1690 afs_com_err(whoami, BC_NOMEM, "");
1694 *portp = getPortOffset(as->parms[2].items->data);
1699 doAppend = (as->parms[4].items ? 1 : 0); /* -append */
1702 * Get a hold of the given volume set and dump set.
1704 tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1706 afs_com_err(whoami, 0,
1707 "Can't find volume set '%s' in backup database", vsName);
1710 baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1712 afs_com_err(whoami, 0,
1713 "Can't find dump schedule '%s' in backup database",
1722 * If given the "-at" option, then add this to the jobs list and return
1725 * Create a status node for this timed dump.
1726 * Fill in the time to dump and the cmd line for the dump leaving off
1727 * the -at option. If the -n option is there, it is scheduled with
1728 * the Timed dump as opposed to not scheduling the time dump at all.
1731 if (atTime < time(0)) {
1732 afs_com_err(whoami, 0,
1733 "Time of dump is earlier then current time - not added");
1735 statusPtr = createStatusNode();
1737 statusPtr->scheduledDump = atTime;
1739 /* Determine length of the dump command */
1741 length += 4; /* "dump" */
1743 length += 6; /* " -file" */
1744 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1746 /* length += 11; *//* " -volumeset" */
1747 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1749 /* length += 6; *//* " -dump" */
1750 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1752 if (as->parms[2].items) {
1753 /* length += 12; *//* " -portoffset" */
1754 length += 1 + strlen(as->parms[2].items->data); /* " <port>" */
1757 if (as->parms[4].items)
1758 length += 8; /* " -append" */
1762 length += 3; /* " -n" */
1763 length++; /* end-of-line */
1765 /* Allocate status block for this timed dump */
1766 sprintf(statusPtr->taskName, "Scheduled Dump");
1767 statusPtr->jobNumber = bc_jobNumber();
1768 statusPtr->scheduledDump = atTime;
1769 statusPtr->cmdLine = (char *)malloc(length);
1770 if (!statusPtr->cmdLine) {
1771 afs_com_err(whoami, BC_NOMEM, "");
1775 /* Now reconstruct the dump command */
1776 statusPtr->cmdLine[0] = 0;
1777 strcat(statusPtr->cmdLine, "dump");
1779 strcat(statusPtr->cmdLine, " -file");
1780 strcat(statusPtr->cmdLine, " ");
1781 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1783 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1784 strcat(statusPtr->cmdLine, " ");
1785 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1787 /* strcat(statusPtr->cmdLine, " -dump"); */
1788 strcat(statusPtr->cmdLine, " ");
1789 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1791 if (as->parms[2].items) {
1792 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1793 strcat(statusPtr->cmdLine, " ");
1794 strcat(statusPtr->cmdLine, as->parms[2].items->data);
1797 if (as->parms[4].items)
1798 strcat(statusPtr->cmdLine, " -append");
1801 strcat(statusPtr->cmdLine, " -n");
1803 printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1804 if ((atTime > ttoken.endTime) && (ttoken.endTime != NEVERDATE))
1805 afs_com_err(whoami, 0,
1806 "Warning: job %d starts after expiration of AFS token",
1807 statusPtr->jobNumber);
1816 * Read and execute the load file if specified. The work of reading is done
1817 * in the main routine prior the dispatch call. loadFile and dontExecute are
1818 * global variables so this can take place in main.
1821 loadFile = (char *)malloc(strlen(as->parms[6].items->data) + 1);
1823 afs_com_err(whoami, BC_NOMEM, "");
1826 strcpy(loadFile, as->parms[6].items->data);
1831 * We are doing a real dump (no load file or timed dump).
1833 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1836 /* For each dump-level above this one, see if the volumeset was dumped
1837 * at the level. We search all dump-levels since a higher dump-level
1838 * may have actually been done AFTER a lower dump level.
1840 parent = level = problemFindingDump = 0;
1841 for (tds = baseds->parent; tds; tds = tds->parent) {
1842 /* Find the most recent dump of the volume-set at this dump-level.
1843 * Raise problem flag if didn't find a dump and a parent not yet found.
1845 code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1848 problemFindingDump = 1; /* skipping a dump level */
1852 /* We found the most recent dump at this level. Now check
1853 * if we should use it by seeing if its full dump hierarchy
1854 * exists. If it doesn't, we don't want to base our incremental
1857 if (!parent || (dumpEntry.id > parent)) {
1858 /* Follow the parent dumps to see if they are all there */
1859 for (d = dumpEntry.parent; d; d = de.parent) {
1860 code = bcdb_FindDumpByID(d, &de);
1865 /* If we found the entire level, remember it. Otherwise raise flag.
1866 * If we had already found a dump, raise the problem flag.
1870 problemFindingDump = 1;
1871 parent = dumpEntry.id;
1872 level = dumpEntry.level + 1;
1873 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1875 /* Dump hierarchy not complete so can't base off the latest */
1876 problemFindingDump = 1;
1881 /* If the problemflag was raise, it means we are not doing the
1882 * dump at the level we requested it be done at.
1884 if (problemFindingDump) {
1885 afs_com_err(whoami, 0,
1886 "Warning: Doing level %d dump due to missing higher-level dumps",
1889 printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1891 } else if (parent) {
1892 printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1895 /* Expand out the volume set into its component list of volumes. */
1896 code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1898 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1902 printf("No volumes to dump\n");
1906 /* Determine what the clone time of the volume was when it was
1907 * last dumped (tve->date). This is the time from when an
1908 * incremental should be done (remains zero if a full dump).
1911 for (tve = volsToDump; tve; tve = tve->next) {
1912 code = bcdb_FindClone(parent, tve->name, &tve->date);
1916 /* Get the time the volume was last cloned and see if the volume has
1917 * changed since then. Only do this when the "-n" flag is specified
1918 * because butc will get the cloneDate at time of dump.
1922 volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1923 tve->vid, tve->volType, &tve->cloneDate);
1927 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1928 afs_com_err(whoami, 0,
1929 "Warning: Timestamp on volume %s unchanged from previous dump",
1937 printf("Would have dumped the following volumes:\n");
1939 printf("Preparing to dump the following volumes:\n");
1940 for (tve = volsToDump; tve; tve = tve->next) {
1941 printf("\t%s (%u)\n", tve->name, tve->vid);
1946 code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1947 /*destServer */ NULL, /*destPartition */ 0,
1949 /*newExt */ NULL, /*oldFlag */ 0,
1950 parent, level, bc_Dumper, portp, /*portCount */ 1,
1951 baseds, doAppend, dontExecute);
1953 afs_com_err(whoami, code, "; Failed to queue dump");
1960 * terminate the backup process. Insists that that all running backup
1961 * jobs be terminated before it will quit
1966 bc_QuitCmd(struct cmd_syndesc *as, void *arock)
1969 struct bc_dumpTask *td;
1970 extern dlqlinkT statusHead;
1974 /* Check the status list for outstanding jobs */
1976 for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
1977 ptr = ptr->dlq_next) {
1978 statusPtr = (statusP) ptr;
1979 if (!(statusPtr->flags & ABORT_REQUEST)) {
1981 afs_com_err(whoami, 0, "Job %d still running (and not aborted)",
1982 statusPtr->jobNumber);
1983 afs_com_err(whoami, 0,
1984 "You must at least 'kill' all running jobs before quitting");
1990 /* A job still being initialized (but no status structure or job number since it
1991 * has not been handed to a butc process yet)
1993 for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1994 if (td->flags & BC_DI_INUSE) {
1995 afs_com_err(whoami, 0, "A job is still running");
1996 afs_com_err(whoami, 0,
1997 "You must at least 'kill' all running jobs before quitting");
2003 /* close the all temp text files before quitting */
2004 for (i = 0; i < TB_NUM; i++)
2005 bc_closeTextFile(&bc_globalConfig->configText[i],
2006 &bc_globalConfig->tmpTextFileNames[i][0]);
2012 * Labels a tape i.e. request the tape coordinator to perform this
2016 bc_LabelTapeCmd(struct cmd_syndesc *as, void *arock)
2018 char *tapename = 0, *pname = 0;
2023 code = bc_UpdateHosts();
2025 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2029 if (as->parms[0].items) { /* -name */
2030 tapename = as->parms[0].items->data;
2031 if (strlen(tapename) >= TC_MAXTAPELEN) {
2032 afs_com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2037 if (as->parms[3].items) { /* -pname */
2039 afs_com_err(whoami, 0, "Can only specify -name or -pname");
2042 pname = as->parms[3].items->data;
2043 if (strlen(pname) >= TC_MAXTAPELEN) {
2044 afs_com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2049 if (as->parms[1].items) {
2050 size = bc_FloatATOI(as->parms[1].items->data);
2052 afs_com_err(whoami, 0, "Bad syntax for tape size '%s'",
2053 as->parms[1].items->data);
2059 if (as->parms[2].items) {
2060 port = getPortOffset(as->parms[2].items->data);
2065 code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2072 * read the label on a tape
2074 * optional port number
2077 bc_ReadLabelCmd(struct cmd_syndesc *as, void *arock)
2082 code = bc_UpdateHosts();
2084 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2088 if (as->parms[0].items) {
2089 port = getPortOffset(as->parms[0].items->data);
2094 code = bc_ReadLabel(bc_globalConfig, port);
2101 * read content information from dump tapes, and if user desires,
2102 * add it to the database
2105 bc_ScanDumpsCmd(struct cmd_syndesc *as, void *arock)
2108 afs_int32 dbAddFlag = 0;
2111 code = bc_UpdateHosts();
2113 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2117 /* check for flag */
2118 if (as->parms[0].items != 0) { /* add scan to database */
2122 /* check for port */
2123 if (as->parms[1].items) {
2124 port = getPortOffset(as->parms[1].items->data);
2129 code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2133 /* bc_ParseExpiration
2136 * dates are specified as absolute or relative, the syntax is:
2137 * absolute: at %d/%d/%d [%d:%d] where [..] is optional
2138 * relative: in [%dy][%dm][%dd] where at least one component
2143 bc_ParseExpiration(struct cmd_parmdesc *paramPtr, afs_int32 *expType,
2146 struct cmd_item *itemPtr;
2147 struct ktime_date kt;
2148 char *dateString = 0;
2151 *expType = BC_NO_EXPDATE;
2154 if (!paramPtr->items)
2155 ERROR(0); /* no expiration specified */
2157 /* some form of expiration date specified. First validate the prefix */
2158 itemPtr = paramPtr->items;
2160 if (strcmp(itemPtr->data, "at") == 0) {
2161 *expType = BC_ABS_EXPDATE;
2163 dateString = concatParams(itemPtr->next);
2167 code = ktime_DateToLong(dateString, expDate);
2170 } else if (strcmp(itemPtr->data, "in") == 0) {
2171 *expType = BC_REL_EXPDATE;
2173 dateString = concatParams(itemPtr->next);
2177 code = ParseRelDate(dateString, &kt);
2180 *expDate = ktimeRelDate_ToLong(&kt);
2182 dateString = concatParams(itemPtr);
2186 if (ktime_DateToLong(dateString, expDate) == 0) {
2187 *expType = BC_ABS_EXPDATE;
2188 code = ktime_DateToLong(dateString, expDate);
2191 } else if (ParseRelDate(dateString, &kt) == 0) {
2192 *expType = BC_REL_EXPDATE;
2193 *expDate = ktimeRelDate_ToLong(&kt);
2205 /* database lookup command and routines */
2208 * Currently a single option, volumename to search for. Reports
2209 * all dumps containing the specified volume
2212 bc_dblookupCmd(struct cmd_syndesc *as, void *arock)
2214 struct cmd_item *ciptr;
2217 ciptr = as->parms[0].items;
2218 if (ciptr == 0) /* no argument specified */
2221 code = DBLookupByVolume(ciptr->data);
2227 /* for ubik version */
2229 bc_dbVerifyCmd(struct cmd_syndesc *as, void *arock)
2235 struct hostent *hostPtr;
2239 extern struct udbHandleS udbHandle;
2241 detail = (as->parms[0].items ? 1 : 0); /* print more details */
2244 ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0, &status, &orphans,
2248 afs_com_err(whoami, code, "; Unable to verify database");
2252 /* verification call succeeded */
2255 printf("Database OK\n");
2257 afs_com_err(whoami, status, "; Database is NOT_OK");
2260 printf("Orphan blocks %d\n", orphans);
2263 printf("Unable to lookup host id\n");
2265 hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2267 printf("Database checker was %d.%d.%d.%d\n",
2268 ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2269 ((host & 0xFF00) >> 8), (host & 0xFF));
2271 printf("Database checker was %s\n", hostPtr->h_name);
2274 return ((status ? -1 : 0));
2278 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2281 deleteDump(afs_uint32 dumpid, /* The dumpid to delete */
2282 afs_int32 port, /* port==-1 means don't go to butc */
2285 afs_int32 code = 0, tcode;
2286 struct budb_dumpEntry dumpEntry;
2287 struct rx_connection *tconn = 0;
2288 afs_int32 i, taskflag, xbsadump;
2289 statusP statusPtr = 0;
2290 budb_dumpsList dumps;
2293 /* If the port is set, we will try to send a delete request to the butc */
2295 tcode = bc_UpdateHosts();
2297 afs_com_err(whoami, tcode, "; Can't retrieve tape hosts");
2301 /* Find the dump in the backup database */
2302 tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2304 afs_com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2308 xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2310 /* If dump is to an XBSA server, connect to butc and send it
2311 * the dump to delete. Butc will contact the XBSA server.
2312 * The dump will not be an appended dump because XBSA butc
2313 * does not support the append option.
2315 if (xbsadump && dumpEntry.nVolumes) {
2316 tcode = ConnectButc(bc_globalConfig, port, &tconn);
2320 tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2322 if (tcode == RXGEN_OPCODE)
2323 tcode = BC_VERSIONFAIL;
2324 afs_com_err(whoami, tcode,
2325 "; Unable to delete dumpID %u via butc", dumpid);
2329 statusPtr = createStatusNode();
2331 statusPtr->taskId = taskId;
2332 statusPtr->port = port;
2333 statusPtr->jobNumber = bc_jobNumber();
2334 statusPtr->flags |= (SILENT | NOREMOVE); /* No msg & keep statusPtr */
2335 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2336 sprintf(statusPtr->taskName, "DeleteDump");
2339 /* Wait for task to finish */
2340 taskflag = waitForTask(taskId);
2341 if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2342 afs_com_err(whoami, BUTX_DELETEOBJFAIL,
2343 "; Unable to delete dumpID %u via butc", dumpid);
2344 ERROR(BUTX_DELETEOBJFAIL); /* the task failed */
2351 deleteStatusNode(statusPtr); /* Clean up statusPtr - because NOREMOVE */
2353 rx_DestroyConnection(tconn); /* Destroy the connection */
2355 /* Remove the dump from the backup database */
2356 if (!code || force) {
2357 dumps.budb_dumpsList_len = 0;
2358 dumps.budb_dumpsList_val = 0;
2360 tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2362 afs_com_err(whoami, tcode,
2363 "; Unable to delete dumpID %u from database", dumpid);
2364 dumps.budb_dumpsList_len = 0;
2369 /* Display the dumps that were deleted - includes appended dumps */
2370 for (i = 0; i < dumps.budb_dumpsList_len; i++)
2371 printf(" %u%s\n", dumps.budb_dumpsList_val[i],
2372 (i > 0) ? " Appended Dump" : "");
2373 if (dumps.budb_dumpsList_val)
2374 free(dumps.budb_dumpsList_val);
2381 * Delete a specified dump from the database
2383 * dump id - single required arg as param 0.
2386 bc_deleteDumpCmd(struct cmd_syndesc *as, void *arock)
2390 afs_int32 rcode = 0;
2391 afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2392 struct cmd_item *ti;
2393 afs_uint32 fromTime = 0, toTime = 0, havetime = 0;
2395 budb_dumpsList dumps, flags;
2397 afs_int32 port = -1, dbonly = 0, force;
2399 /* Must specify at least one of -dumpid, -from, or -to */
2400 if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2401 && !as->parms[4].items) {
2402 afs_com_err(whoami, 0, "Must specify at least one field");
2406 /* Must have -to option with -from option */
2407 if (as->parms[1].items && !as->parms[2].items) {
2408 afs_com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2412 /* Get the time to delete from */
2413 if (as->parms[1].items) { /* -from */
2414 timeString = concatParams(as->parms[1].items);
2419 * Now parse this string for the time to start.
2421 code = ktime_DateToLong(timeString, &fromTime);
2424 afs_com_err(whoami, 0, "Can't parse 'from' date and time");
2425 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2431 port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0); /* -port */
2432 if (as->parms[5].items) /* -dbonly */
2435 force = (as->parms[6].items ? 1 : 0);
2437 havegroupid = (as->parms[4].items ? 1 : 0);
2439 groupId = atoi(as->parms[4].items->data);
2441 noexecute = (as->parms[7].items ? 1 : 0);
2443 /* Get the time to delete to */
2444 if (as->parms[2].items) { /* -to */
2445 timeString = concatParams(as->parms[2].items);
2450 * Now parse this string for the time to start. Simce
2451 * times are at minute granularity, add 59 seconds.
2453 code = ktime_DateToLong(timeString, &toTime);
2456 afs_com_err(whoami, 0, "Can't parse 'to' date and time");
2457 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2464 if (fromTime > toTime) {
2465 afs_com_err(whoami, 0,
2466 "'-from' date/time cannot be later than '-to' date/time");
2470 /* Remove speicific dump ids - if any */
2471 printf("The following dumps %s deleted:\n",
2472 (noexecute ? "would have been" : "were"));
2473 for (ti = as->parms[0].items; ti != 0; ti = ti->next) { /* -dumpid */
2474 dumpid = atoi(ti->data);
2476 code = deleteDump(dumpid, port, force);
2478 printf(" %u\n", dumpid);
2483 * Now remove dumps between to and from dates.
2485 if (havegroupid || havetime) {
2486 dumps.budb_dumpsList_len = 0;
2487 dumps.budb_dumpsList_val = 0;
2488 flags.budb_dumpsList_len = 0;
2489 flags.budb_dumpsList_val = 0;
2492 sflags |= BUDB_OP_GROUPID;
2494 sflags |= BUDB_OP_DATES;
2497 bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2499 afs_com_err(whoami, code,
2500 "; Error while deleting dumps from %u to %u", fromTime,
2505 for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2506 if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2510 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2512 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2513 /* Ignore code and continue */
2515 printf(" %u%s%s\n", dumps.budb_dumpsList_val[i],
2517 budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2518 " Appended Dump" : "",
2520 budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2521 " Database Dump" : "");
2526 if (dumps.budb_dumpsList_val)
2527 free(dumps.budb_dumpsList_val);
2528 dumps.budb_dumpsList_len = 0;
2529 dumps.budb_dumpsList_val = 0;
2530 if (flags.budb_dumpsList_val)
2531 free(flags.budb_dumpsList_val);
2532 flags.budb_dumpsList_len = 0;
2533 flags.budb_dumpsList_val = 0;
2540 bc_saveDbCmd(struct cmd_syndesc *as, void *arock)
2542 struct rx_connection *tconn;
2543 afs_int32 portOffset = 0;
2550 code = bc_UpdateHosts();
2552 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2556 if (as->parms[0].items) {
2557 portOffset = getPortOffset(as->parms[0].items->data);
2562 /* Get the time to delete to */
2563 if (as->parms[1].items) {
2564 timeString = concatParams(as->parms[1].items);
2569 * Now parse this string for the time. Since
2570 * times are at minute granularity, add 59 seconds.
2572 code = ktime_DateToLong(timeString, &toTime);
2575 afs_com_err(whoami, 0, "Can't parse '-archive' date and time");
2576 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2583 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2587 code = TC_SaveDb(tconn, toTime, &taskId);
2589 afs_com_err(whoami, code, "; Failed to save database");
2593 /* create status monitor block */
2594 statusPtr = createStatusNode();
2596 statusPtr->taskId = taskId;
2597 statusPtr->port = portOffset;
2598 statusPtr->jobNumber = bc_jobNumber();
2599 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2600 sprintf(statusPtr->taskName, "SaveDb");
2604 rx_DestroyConnection(tconn);
2609 bc_restoreDbCmd(struct cmd_syndesc *as, void *arock)
2611 struct rx_connection *tconn;
2612 afs_int32 portOffset = 0;
2617 code = bc_UpdateHosts();
2619 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2623 if (as->parms[0].items) {
2624 portOffset = getPortOffset(as->parms[0].items->data);
2629 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2633 code = TC_RestoreDb(tconn, &taskId);
2635 afs_com_err(whoami, code, "; Failed to restore database");
2639 /* create status monitor block */
2640 statusPtr = createStatusNode();
2642 statusPtr->taskId = taskId;
2643 statusPtr->port = portOffset;
2644 statusPtr->jobNumber = bc_jobNumber();
2645 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2646 sprintf(statusPtr->taskName, "RestoreDb");
2650 rx_DestroyConnection(tconn);
2654 /* ----------------------------------
2655 * supporting routines for database examination
2656 * ----------------------------------
2659 /* structures and defines for DBLookupByVolume */
2661 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2663 /* dumpedVol - saves interesting information so that we can print it out
2668 struct dumpedVol *next;
2670 afs_int32 initialDumpID;
2671 char tapeName[BU_MAXTAPELEN];
2674 afs_int32 createTime;
2675 afs_int32 incTime; /* actually the clone time */
2678 /* -----------------------------------------
2679 * routines for examining the database
2680 * -----------------------------------------
2684 * Lookup the volumename in the backup database and print the results
2686 * volumeName - volume to lookup
2690 DBLookupByVolume(char *volumeName)
2692 struct budb_dumpEntry dumpEntry;
2693 struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2694 afs_int32 numEntries;
2695 afs_int32 tapedumpid;
2696 afs_int32 last, next;
2698 struct dumpedVol *dvptr = 0;
2699 struct dumpedVol *tempPtr = 0;
2702 char vname[BU_MAXNAMELEN];
2705 for (pass = 0; pass < 2; pass++) {
2707 /* On second pass, search for backup volume */
2709 if (!BackupName(volumeName)) {
2710 strcpy(vname, volumeName);
2711 strcat(vname, ".backup");
2719 while (next != -1) { /*w */
2721 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2722 DBL_MAX_VOLUMES, &numEntries);
2726 /* add the volumes to the list */
2727 for (i = 0; i < numEntries; i++) { /*f */
2728 struct dumpedVol *insPtr, **prevPtr;
2731 (struct dumpedVol *)malloc(sizeof(struct dumpedVol));
2735 memset(tempPtr, 0, sizeof(*tempPtr));
2736 tempPtr->incTime = volumeEntry[i].clone;
2737 tempPtr->dumpID = volumeEntry[i].dump;
2738 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2741 /* check if we need to null terminate it - just for safety */
2742 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2743 tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2745 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2751 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2752 tempPtr->parent = dumpEntry.parent;
2753 tempPtr->level = dumpEntry.level;
2754 tempPtr->createTime = dumpEntry.created;
2756 /* add volume to list in reverse chronological order */
2760 while ((insPtr != 0)
2761 && (insPtr->createTime > tempPtr->createTime)
2763 prevPtr = &insPtr->next;
2764 insPtr = insPtr->next;
2767 /* now at the right place - insert the block */
2768 tempPtr->next = *prevPtr;
2778 ("DumpID lvl parentID creation date clone date tape name\n");
2779 for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2780 /* For the user, the tape name is its name and initial dump id */
2782 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2785 /* beware the static items in compactDateString */
2786 compactDateString(&tempPtr->createTime, ds, 50);
2787 printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2788 tempPtr->parent, ds);
2789 compactDateString(&tempPtr->incTime, ds, 50);
2790 printf(" %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2796 for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2797 dvptr = dvptr->next;
2802 afs_com_err(whoami, code, "");
2806 /* structures for dumpInfo */
2809 struct volumeLink *nextVolume;
2810 struct budb_volumeEntry volumeEntry;
2814 struct tapeLink *nextTape;
2815 struct budb_tapeEntry tapeEntry;
2816 struct volumeLink *firstVolume;
2821 * print information about a dump and all its tapes and volumes.
2825 dumpInfo(afs_int32 dumpid, afs_int32 detailFlag)
2827 struct budb_dumpEntry dumpEntry;
2828 struct tapeLink *head = 0;
2829 struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2830 struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2833 afs_int32 last, next, dbTime;
2834 afs_int32 tapedumpid;
2842 extern struct udbHandleS udbHandle;
2845 lastTapeLinkPtr = 0;
2847 lastVolumeLinkPtr = 0;
2849 /* first get information about the dump */
2851 code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2855 /* For the user, the tape name is its name and initial dump id */
2856 tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2858 /* Is this a database dump id or not */
2859 if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2864 /* print out the information about the dump */
2868 printDumpEntry(&dumpEntry);
2870 time_t t = dumpEntry.created;
2872 printf("Dump: id %u, created: %s\n", dumpEntry.id,
2875 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2876 dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2880 if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2881 && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2882 printf("Backup Service: TSM: Server: %s\n",
2883 dumpEntry.tapes.tapeServer);
2886 /* now get the list of tapes */
2887 for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) { /*f */
2888 tapeLinkPtr = (struct tapeLink *)malloc(sizeof(struct tapeLink));
2890 afs_com_err(whoami, BC_NOMEM, "");
2894 memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2895 code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2902 /* add this tape to previous chain */
2903 if (lastTapeLinkPtr) {
2904 lastTapeLinkPtr->nextTape = tapeLinkPtr;
2905 lastTapeLinkPtr = tapeLinkPtr;
2910 lastTapeLinkPtr = head;
2914 while (next != -1) { /*wn */
2915 vl.budb_volumeList_len = 0;
2916 vl.budb_volumeList_val = 0;
2919 /* now get all the volumes in this dump. */
2920 code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name, /* tape name */
2921 dumpid, /* dumpid (not initial dumpid) */
2924 &next, /* nextindex */
2925 &dbTime, /* update time */
2929 if (code == BUDB_ENDOFLIST) { /* 0 volumes on tape */
2936 for (i = 0; i < vl.budb_volumeList_len; i++) {
2937 link = &tapeLinkPtr->firstVolume;
2940 (struct volumeLink *)malloc(sizeof(struct volumeLink));
2941 if (!volumeLinkPtr) {
2942 afs_com_err(whoami, BC_NOMEM, "");
2945 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
2947 memcpy(&volumeLinkPtr->volumeEntry,
2948 &vl.budb_volumeList_val[i],
2949 sizeof(struct budb_volumeEntry));
2951 /* now insert it onto the right place */
2953 && (volumeLinkPtr->volumeEntry.position >
2954 (*link)->volumeEntry.position)) {
2955 link = &((*link)->nextVolume);
2958 /* now link it in */
2959 volumeLinkPtr->nextVolume = *link;
2960 *link = volumeLinkPtr;
2963 if (vl.budb_volumeList_val)
2964 free(vl.budb_volumeList_val);
2968 for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
2972 printTapeEntry(&tapeLinkPtr->tapeEntry);
2974 printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
2976 printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
2977 compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
2978 printf("created %16s", ds);
2979 if (tapeLinkPtr->tapeEntry.expires != 0) {
2980 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
2981 printf(", expires %16s", ds);
2986 /* print out all the volumes */
2988 /* print header for volume listing - db dumps have no volumes */
2989 if ((detailFlag == 0) && !dbDump)
2990 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
2993 for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
2994 volumeLinkPtr = volumeLinkPtr->nextVolume) {
2996 printf("\nVolume\n");
2998 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3000 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3001 printf("%4d %s %10u %16s\n",
3002 volumeLinkPtr->volumeEntry.position, ds,
3003 volumeLinkPtr->volumeEntry.nBytes,
3004 volumeLinkPtr->volumeEntry.name);
3011 afs_com_err("dumpInfo", code, "; Can't get dump information");
3013 /* free all allocated structures */
3015 while (tapeLinkPtr) {
3016 volumeLinkPtr = tapeLinkPtr->firstVolume;
3017 while (volumeLinkPtr) {
3018 lastVolumeLinkPtr = volumeLinkPtr;
3019 volumeLinkPtr = volumeLinkPtr->nextVolume;
3020 free(lastVolumeLinkPtr);
3023 lastTapeLinkPtr = tapeLinkPtr;
3024 tapeLinkPtr = tapeLinkPtr->nextTape;
3025 free(lastTapeLinkPtr);
3031 compareDump(struct budb_dumpEntry *ptr1, struct budb_dumpEntry *ptr2)
3033 if (ptr1->created < ptr2->created)
3035 else if (ptr1->created > ptr2->created)
3041 printRecentDumps(int ndumps)
3044 afs_int32 nextindex, index = 0;
3047 struct budb_dumpEntry *dumpPtr;
3051 extern struct udbHandleS udbHandle;
3053 do { /* while (nextindex != -1) */
3054 /* initialize the dump list */
3055 dl.budb_dumpList_len = 0;
3056 dl.budb_dumpList_val = 0;
3058 /* outline algorithm */
3059 code = ubik_Call(BUDB_GetDumps, udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "", /* no name */
3063 &nextindex, &dbTime, &dl);
3065 if (code == BUDB_ENDOFLIST)
3067 afs_com_err("dumpInfo", code, "; Can't get dump information");
3071 /* No need to sort, it's already sorted */
3073 if (dl.budb_dumpList_len && (index == 0))
3074 printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3075 "parentid", "lv", "created", "nt", "nvols");
3077 dumpPtr = dl.budb_dumpList_val;
3078 for (i = 1; i <= dl.budb_dumpList_len; i++) {
3079 compactDateString(&dumpPtr->created, ds, 50),
3080 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3081 dumpPtr->parent, dumpPtr->level, ds,
3082 dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3083 dumpPtr->nVolumes, dumpPtr->name);
3084 if (dumpPtr->initialDumpID) /* an appended dump */
3085 printf(" (%u)", dumpPtr->initialDumpID);
3086 else if (dumpPtr->appendedDumpID) /* has appended dumps */
3087 printf(" (%u)", dumpPtr->id);
3093 if (dl.budb_dumpList_val)
3094 free(dl.budb_dumpList_val);
3096 } while (nextindex != -1);
3102 * list the dumps and contens of the dumps.
3108 bc_dumpInfoCmd(struct cmd_syndesc *as, void *arock)
3111 afs_int32 detailFlag;
3115 if (as->parms[0].items) {
3116 if (as->parms[1].items) {
3117 afs_com_err(whoami, 0,
3118 "These options are exclusive - select only one");
3121 ndumps = atoi(as->parms[0].items->data);
3123 afs_com_err(whoami, 0, "Must provide a positive number");
3127 code = printRecentDumps(ndumps);
3128 } else if (as->parms[1].items) {
3129 detailFlag = (as->parms[2].items ? 1 : 0); /* 1 = detailed listing */
3130 dumpid = atoi(as->parms[1].items->data);
3131 code = dumpInfo(dumpid, detailFlag);
3133 code = printRecentDumps(10);