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>
17 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
28 #include <afs/com_err.h>
31 #include <afs/bubasics.h> /* PA */
32 #include <afs/volser.h>
33 #include <afs/voldefs.h> /* PA */
34 #include <afs/vldbint.h> /* PA */
35 #include <afs/ktime.h> /* PA */
41 #include <afs/tcdata.h>
44 #include "error_macros.h"
47 extern struct bc_config *bc_globalConfig;
48 extern char *ktime_GetDateUsage();
49 extern struct bc_dumpSchedule *bc_FindDumpSchedule();
50 extern struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *cfg,
52 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
53 extern struct ubik_client *cstruct;
54 extern int bc_Dumper(); /* function to do dumps */
55 extern int bc_Restorer(); /* function to do restores */
56 extern void bc_HandleMisc();
57 extern afs_int32 ScanDumpHdr();
58 extern afs_int32 ScanTapeVolume();
60 extern int VL_ListEntry();
61 extern int VL_ListAttributesN2();
62 extern struct ktc_token ttoken;
63 extern char *tailCompPtr();
64 extern statusP createStatusNode();
67 extern afs_int32 lastTaskCode;
70 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
72 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
76 bc_EvalVolumeSet(aconfig, avs, avols, uclient)
77 struct bc_config *aconfig;
78 register struct bc_volumeSet *avs;
79 struct bc_volumeDump **avols;
80 struct ubik_client *uclient;
81 { /*bc_EvalVolumeSet */
84 static afs_int32 use = 2;
86 if (use == 2) { /* Use EvalVolumeSet2() */
87 code = EvalVolumeSet2(aconfig, avs, avols, uclient);
88 if (code == RXGEN_OPCODE)
91 if (use == 1) { /* Use EvalVolumeSet1() */
92 code = EvalVolumeSet1(aconfig, avs, avols, uclient);
95 } /*bc_EvalVolumeSet */
97 struct partitionsort {
99 struct bc_volumeDump *vdlist;
100 struct bc_volumeDump *lastvdlist;
101 struct bc_volumeDump *dupvdlist;
102 struct bc_volumeEntry *vole;
103 struct partitionsort *next;
107 struct partitionsort *partitions;
108 struct serversort *next;
112 getSPEntries(server, partition, serverlist, ss, ps)
115 struct serversort **serverlist, **ss;
116 struct partitionsort **ps;
118 if (!(*ss) || ((*ss)->ipaddr != server)) {
120 for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
121 if ((*ss)->ipaddr == server)
125 /* No server entry added. Add one */
127 *ss = (struct serversort *)malloc(sizeof(struct serversort));
129 com_err(whoami, BC_NOMEM, "");
133 memset(*ss, 0, sizeof(struct serversort));
134 (*ss)->ipaddr = server;
135 (*ss)->next = *serverlist;
140 if (!(*ps) || ((*ps)->part != partition)) {
141 for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
142 if ((*ps)->part == partition)
146 /* No partition entry added. Add one */
148 *ps = (struct partitionsort *)malloc(sizeof(struct partitionsort));
150 com_err(whoami, BC_NOMEM, "");
156 memset(*ps, 0, sizeof(struct partitionsort));
157 (*ps)->part = partition;
158 (*ps)->next = (*ss)->partitions;
159 (*ss)->partitions = *ps;
165 randSPEntries(serverlist, avols)
166 struct serversort *serverlist;
167 struct bc_volumeDump **avols;
169 struct serversort *ss, **pss, *tss;
170 struct partitionsort *ps, **pps;
172 afs_int32 scount, pcount;
176 /* Seed random number generator */
177 r = time(0) + getpid();
180 /* Count number of servers, remove one at a time */
181 for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
182 for (; scount; scount--) {
183 /* Pick a random server in list and remove it */
184 r = (rand() >> 4) % scount;
185 for (pss = &serverlist, ss = serverlist; r;
186 pss = &ss->next, ss = ss->next, r--);
189 /* Count number of partitions, remove one at a time */
190 for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
191 for (; pcount; pcount--) {
192 /* Pick a random parition in list and remove it */
193 r = (rand() >> 4) % pcount;
194 for (pps = &ss->partitions, ps = ss->partitions; r;
195 pps = &ps->next, ps = ps->next, r--);
198 ps->lastvdlist->next = *avols;
207 EvalVolumeSet2(aconfig, avs, avols, uclient)
208 struct bc_config *aconfig;
209 struct bc_volumeSet *avs;
210 struct bc_volumeDump **avols;
211 struct ubik_client *uclient;
212 { /*EvalVolumeSet2 */
213 struct bc_volumeEntry *tve;
214 struct bc_volumeDump *tavols;
215 struct VldbListByAttributes attributes;
216 afs_int32 si, nsi; /* startIndex and nextStartIndex */
217 afs_int32 nentries, e, ei, et, add, l;
218 nbulkentries bulkentries;
219 struct nvldbentry *entries = 0;
220 struct bc_volumeDump *tvd;
221 afs_int32 code = 0, tcode;
223 struct serversort *servers = 0, *lastserver = 0, *ss = 0, *nss;
224 struct partitionsort *ps = 0, *nps;
226 *avols = (struct bc_volumeDump *)0;
227 bulkentries.nbulkentries_len = 0;
228 bulkentries.nbulkentries_val = 0;
230 /* For each of the volume set entries - collect the volumes that match it */
231 for (tve = avs->ventries; tve; tve = tve->next) {
232 /* Put together a call to the vlserver for this vlentry. The
233 * performance gain is from letting the vlserver expand the
234 * volumeset and not this routine.
237 if (tve->server.sin_addr.s_addr) { /* The server */
238 attributes.Mask |= VLLIST_SERVER;
239 attributes.server = tve->server.sin_addr.s_addr;
241 if (tve->partition != -1) { /* The partition */
242 attributes.Mask |= VLLIST_PARTITION;
243 attributes.partition = tve->partition;
246 /* Now make the call to the vlserver */
247 for (si = 0; si != -1; si = nsi) {
249 bulkentries.nbulkentries_len = 0;
250 bulkentries.nbulkentries_val = 0;
253 ubik_Call(VL_ListAttributesN2, uclient, 0, &attributes,
254 tve->name, si, &nentries, &bulkentries, &nsi);
258 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
259 * it is not complete. The only way to tell if it is not complete
260 * is if et == 0 (which we check for below). Also, if the call didn't
261 * match any entries, then we don't know what version the vlserver
262 * is. In both cases, we return RXGEN_OPCODE and the calling routine
263 * will switch to the EvalVolumeSet1() call.
266 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
268 /* Step through each entry and add it to the list of volumes */
269 entries = bulkentries.nbulkentries_val;
270 for (e = 0; e < nentries; e++) {
271 ei = entries[e].matchindex & 0xffff;
272 et = (entries[e].matchindex >> 16) & 0xffff;
287 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
290 /* Find server and partiton structure to hang the entry off of */
292 getSPEntries(entries[e].serverNumber[ei],
293 entries[e].serverPartition[ei], &servers,
296 com_err(whoami, tcode, "");
300 /* Detect if this entry should be added (not a duplicate).
301 * Use ps->dupvdlist and ps->vole to only search volumes from
302 * previous volume set entries.
305 if (tve != avs->ventries) {
306 l = strlen(entries[e].name);
307 if (ps->vole != tve) {
309 ps->dupvdlist = ps->vdlist;
311 for (tavols = ps->dupvdlist; add && tavols;
312 tavols = tavols->next) {
313 if (strncmp(tavols->name, entries[e].name, l) == 0) {
314 if ((strcmp(&entries[e].name[l], ".backup") == 0)
315 || (strcmp(&entries[e].name[l], ".readonly")
317 || (strcmp(&entries[e].name[l], "") == 0))
324 /* Allocate a volume dump structure and its name */
325 tvd = (struct bc_volumeDump *)
326 malloc(sizeof(struct bc_volumeDump));
328 com_err(whoami, BC_NOMEM, "");
331 memset(tvd, 0, sizeof(*tvd));
333 tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
335 com_err(whoami, BC_NOMEM, "");
340 /* Fill it in and thread onto avols list */
341 strcpy(tvd->name, entries[e].name);
343 strcat(tvd->name, ".backup");
344 else if (et == ROVOL)
345 strcat(tvd->name, ".readonly");
346 tvd->vid = entries[e].volumeId[et];
349 tvd->partition = entries[e].serverPartition[ei];
350 tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
351 tvd->server.sin_port = 0; /* default FS port */
352 tvd->server.sin_family = AF_INET;
353 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
354 tvd->server.sin_len = sizeof(struct sockaddr_in);
357 /* String tvd off of partition struct */
358 tvd->next = ps->vdlist;
361 ps->lastvdlist = tvd;
367 /* Free memory allocated during VL call */
368 if (bulkentries.nbulkentries_val) {
369 free((char *)bulkentries.nbulkentries_val);
370 bulkentries.nbulkentries_val = 0;
376 /* Randomly link the volumedump entries together */
377 randSPEntries(servers, avols);
378 fprintf(stderr, "Total number of volumes : %u\n", count);
381 if (bulkentries.nbulkentries_val) {
382 free((char *)bulkentries.nbulkentries_val);
385 } /*EvalVolumeSet2 */
387 /*-----------------------------------------------------------------------------
391 * Takes the entries in a volumeset and expands them into a list of
392 * volumes. Every VLDB volume entry is looked at and compared to the
395 * When matching a VLDB volume entry to a volumeset entry,
396 * 1. If the RW volume entry matches, that RW volume is used.
397 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
398 * 3. Finally, if the RO volume entry matches, the RO volume is used.
399 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
400 * "user.troy" and "user.troy.backup". The rules will use
401 * the RW volume "user.troy".
403 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
404 * that volume is used and matches against any remaining volumeset entries
406 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
407 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
408 * would have matched its RW volume "user.troy", but the first
409 * match is used and the second match isn't even done.
412 * aconfig : Global configuration info.
414 * avols : Ptr to linked list of entries describing volumes to dump.
415 * uclient : Ptr to Ubik client structure.
418 * 0 on successful volume set evaluation,
419 * Lower-level codes otherwise.
422 * Expand only the single volume set provided (avs); don't go down its chain.
426 *-----------------------------------------------------------------------------
429 EvalVolumeSet1(aconfig, avs, avols, uclient)
430 struct bc_config *aconfig;
431 struct bc_volumeSet *avs;
432 struct bc_volumeDump **avols;
433 struct ubik_client *uclient;
434 { /*EvalVolumeSet1 */
435 afs_int32 code; /*Result of various calls */
437 struct bc_volumeDump *tvd; /*Ptr to new dump instance */
438 struct bc_volumeEntry *tve, *ctve; /*Ptr to new volume entry instance */
439 char patt[256]; /*Composite regex; also, target string */
440 int volType; /*Type of volume that worked */
441 afs_int32 index; /*Current VLDB entry index */
442 afs_int32 count; /*Needed by VL_ListEntry() */
443 afs_int32 next_index; /*Next index to list */
444 struct vldbentry entry; /*VLDB entry */
445 int srvpartpair; /*Loop counter: server/partition pair */
447 int found, foundentry;
448 struct serversort *servers = 0, *ss = 0;
449 struct partitionsort *ps = 0;
451 *avols = (struct bc_volumeDump *)0;
452 ctve = (struct bc_volumeEntry *)0; /* no compiled entry */
454 /* For each vldb entry.
455 * Variable next_index is set to the index of the next VLDB entry
456 * in the enumeration.
458 for (index = 0; 1; index = next_index) { /*w */
459 memset(&entry, 0, sizeof(entry));
460 code = ubik_Call(VL_ListEntry, /*Routine to invoke */
461 uclient, /*Ubik client structure */
463 index, /*Current index */
464 &count, /*Ptr to working variable */
465 &next_index, /*Ptr to next index value to list */
466 &entry); /*Ptr to entry to fill */
470 break; /* If the next index is invalid, bail out now. */
472 /* For each entry in the volume set */
473 found = 0; /* No match in volume set yet */
474 for (tve = avs->ventries; tve; tve = tve->next) { /*ve */
475 /* for each server in the vldb entry */
476 for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) { /*s */
477 /* On the same server */
478 if (tve->server.sin_addr.s_addr
479 && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
480 entry.serverNumber[srvpartpair],
488 /* On the same partition */
489 if ((tve->partition != -1)
490 && (tve->partition != entry.serverPartition[srvpartpair]))
493 /* If the volume entry is not compiled, then compile it */
495 sprintf(patt, "^%s$", tve->name);
496 errm = (char *)re_comp(patt);
499 "Can't compile regular expression '%s': %s",
506 /* If the RW name matches the volume set entry, take
507 * it and exit. First choice is to use the RW volume.
509 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
510 if (entry.flags & RW_EXISTS) {
511 sprintf(patt, "%s", entry.name);
512 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 code = re_exec(patt);
529 foundentry = srvpartpair;
536 /* If the RO name matches the volume set entry, remember
537 * it, but continue searching. Further entries may be
538 * RW or backup entries that will match.
540 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
541 && (entry.flags & RO_EXISTS)) {
542 sprintf(patt, "%s.readonly", entry.name);
543 code = re_exec(patt);
546 foundentry = srvpartpair;
552 com_err(whoami, 0, "Internal error in regex package");
555 /* If found a match, then create a new volume dump entry */
557 /* Find server and partition structure to hang the entry off of */
559 getSPEntries(entry.serverNumber[foundentry],
560 entry.serverPartition[foundentry], &servers,
563 com_err(whoami, code, "");
568 tvd = (struct bc_volumeDump *)
569 malloc(sizeof(struct bc_volumeDump));
571 com_err(whoami, BC_NOMEM, "");
574 memset(tvd, 0, sizeof(*tvd));
576 tvd->name = (char *)malloc(strlen(entry.name) + 10);
578 com_err(whoami, BC_NOMEM, "");
583 strcpy(tvd->name, entry.name);
584 if (volType == BACKVOL)
585 strcat(tvd->name, ".backup");
586 else if (volType == ROVOL)
587 strcat(tvd->name, ".readonly");
588 tvd->vid = entry.volumeId[volType];
590 tvd->volType = volType;
591 tvd->partition = entry.serverPartition[foundentry];
592 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
593 tvd->server.sin_port = 0; /* default FS port */
594 tvd->server.sin_family = AF_INET;
596 /* String tvd off of partition struct */
597 tvd->next = ps->vdlist;
600 ps->lastvdlist = tvd;
607 /* Randomly link the volumedump entries together */
608 randSPEntries(servers, avols);
610 fprintf(stderr, "Total number of volumes : %u\n", total);
612 } /*EvalVolumeSet1 */
615 * print out a date in compact format, 16 chars, format is
618 * date_long - ptr to a long containing the time
620 * ptr to a string containing a representation of the date
623 compactDateString(date_long, string, size)
624 afs_int32 *date_long, size;
632 if (*date_long == NEVERDATE) {
633 sprintf(string, "NEVER");
635 ltime = localtime(date_long);
636 /* prints date in U.S. format of mm/dd/yyyy */
637 strftime(string, size, "%m/%d/%Y %H:%M", ltime);
648 for (; *anum; anum++) {
649 if ((*anum < '0') || (*anum > '9'))
651 total = (10 * total) + (afs_int32) (*anum - '0');
657 * Take a string and parse it for a number (could be float) followed
658 * by a character representing the units (K,M,G,T). Default is 'K'.
659 * Return the size in KBytes.
667 afs_int32 fraction = 0; /* > 0 if past the decimal */
669 for (; *anum; anum++) {
670 if ((*anum == 't') || (*anum == 'T')) {
671 total *= 1024 * 1024 * 1024;
674 if ((*anum == 'g') || (*anum == 'G')) {
675 total *= 1024 * 1024;
678 if ((*anum == 'm') || (*anum == 'M')) {
682 if ((*anum == 'k') || (*anum == 'K')) {
689 if ((*anum < '0') || (*anum > '9'))
693 total = (10. * total) + (float)(*anum - '0');
695 total += ((float)(*anum - '0')) / (float)fraction;
700 total += 0.5; /* Round up */
701 if (total > 0x7fffffff) /* Don't go over 2G */
703 rtotal = (afs_int32) total;
707 /* make a copy of a string so that it can be freed later */
709 bc_CopyString(astring)
716 return (NULL); /* propagate null strings easily */
717 tlen = strlen(astring);
718 tp = (char *)malloc(tlen + 1); /* don't forget the terminating null */
720 com_err(whoami, BC_NOMEM, "");
729 * Concatenates the parameters of an option and returns the string.
734 concatParams(itemPtr)
735 struct cmd_item *itemPtr;
737 struct cmd_item *tempPtr;
738 afs_int32 length = 0;
741 /* compute the length of string required */
742 for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
743 length += strlen(tempPtr->data);
744 length++; /* space or null terminator */
747 if (length == 0) { /* no string (0 length) */
748 com_err(whoami, 0, "Can't have zero length date and time string");
752 string = (char *)malloc(length); /* allocate the string */
754 com_err(whoami, BC_NOMEM, "");
759 tempPtr = itemPtr; /* now assemble the string */
761 strcat(string, tempPtr->data);
762 tempPtr = tempPtr->next;
767 return (string); /* return the string */
771 * print out an interface status node as received from butc
774 printIfStatus(statusPtr)
775 struct tciStatusS *statusPtr;
777 printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
778 if (statusPtr->nKBytes)
779 printf("%ld Kbytes transferred", statusPtr->nKBytes);
780 if (strlen(statusPtr->volumeName) != 0) {
781 if (statusPtr->nKBytes)
783 printf("volume %s", statusPtr->volumeName);
788 if (statusPtr->flags & ABORT_REQUEST)
789 printf(" [abort request rcvd]");
791 if (statusPtr->flags & ABORT_DONE)
792 printf(" [abort complete]");
794 if (statusPtr->flags & OPR_WAIT)
795 printf(" [operator wait]");
797 if (statusPtr->flags & CALL_WAIT)
798 printf(" [callout in progress]");
800 if (statusPtr->flags & DRIVE_WAIT)
801 printf(" [drive wait]");
803 if (statusPtr->flags & TASK_DONE)
812 afs_int32 portOffset;
814 portOffset = bc_SafeATOI(port);
816 if (portOffset < 0) {
817 com_err(whoami, 0, "Can't decode port offset '%s'", port);
819 } else if (portOffset > BC_MAXPORTOFFSET) {
820 com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
827 /* bc_GetTapeStatusCmd
828 * display status of all tasks on a particular tape coordinator
831 bc_GetTapeStatusCmd(as, arock)
832 struct cmd_syndesc *as;
836 afs_int32 index, dumpID;
837 struct rx_connection *tconn;
838 afs_int32 portOffset = 0;
843 struct tciStatusS status;
845 code = bc_UpdateHosts();
847 com_err(whoami, code, "; Can't retrieve tape hosts");
851 if (as->parms[0].items) {
852 portOffset = getPortOffset(as->parms[0].items->data);
857 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
861 flags = TSK_STAT_FIRST;
865 while ((flags & TSK_STAT_END) == 0) {
866 code = TC_ScanStatus(tconn, &taskId, &status, &flags);
868 if (code == TC_NOTASKS)
870 com_err(whoami, code, "; Can't get status from butc");
873 if ((flags & TSK_STAT_NOTFOUND))
874 break; /* Can't find the task id */
875 flags &= ~TSK_STAT_FIRST; /* turn off flag */
877 printIfStatus(&status);
882 printf("Tape coordinator is idle\n");
884 if (flags & TSK_STAT_ADSM)
885 printf("TSM Tape coordinator\n");
886 else if (flags & TSK_STAT_XBSA)
887 printf("XBSA Tape coordinator\n");
892 extern struct Lock dispatchLock;
895 * wait for all jobs to terminate
902 int usefulJobRunning = 1;
904 extern dlqlinkT statusHead;
908 com_err(whoami, 0, "waiting for job termination");
910 while (usefulJobRunning) {
911 usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
912 if (dispatchLock.excl_locked)
913 usefulJobRunning = 1;
914 for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
915 if (bc_dumpTasks[i].flags & BC_DI_INUSE)
916 usefulJobRunning = 1;
919 /* Wait 5 seconds and check again */
920 if (usefulJobRunning)
923 return (lastTaskCode);
927 * print status on running jobs
929 * ignored - a null "as" prints only jobs.
932 bc_JobsCmd(as, arock)
933 struct cmd_syndesc *as;
938 struct bc_dumpTask *td;
945 extern dlqlinkT statusHead;
947 dlqInit(&atJobsHead);
950 ptr = (&statusHead)->dlq_next;
951 while (ptr != &statusHead) {
952 statusPtr = (statusP) ptr;
955 if (statusPtr->scheduledDump) {
956 dlqUnlink((dlqlinkP) statusPtr);
957 dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
959 printf("Job %d:", statusPtr->jobNumber);
961 printf(" %s", statusPtr->taskName);
963 if (statusPtr->dbDumpId)
964 printf(": DumpID %u", statusPtr->dbDumpId);
965 if (statusPtr->nKBytes)
966 printf(", %ld Kbytes", statusPtr->nKBytes);
967 if (strlen(statusPtr->volumeName) != 0)
968 printf(", volume %s", statusPtr->volumeName);
970 if (statusPtr->flags & CONTACT_LOST)
971 printf(" [butc contact lost]");
973 if (statusPtr->flags & ABORT_REQUEST)
974 printf(" [abort request]");
976 if (statusPtr->flags & ABORT_SENT)
977 printf(" [abort sent]");
979 if (statusPtr->flags & OPR_WAIT)
980 printf(" [operator wait]");
982 if (statusPtr->flags & CALL_WAIT)
983 printf(" [callout in progress]");
985 if (statusPtr->flags & DRIVE_WAIT)
986 printf(" [drive wait]");
992 * Now print the scheduled dumps.
994 if (!dlqEmpty(&statusHead) && as)
995 printf("\n"); /* blank line between running and scheduled dumps */
998 while (!dlqEmpty(&atJobsHead)) {
999 ptr = (&atJobsHead)->dlq_next;
1000 youngest = (statusP) ptr;
1002 ptr = ptr->dlq_next;
1003 while (ptr != &atJobsHead) { /* Find the dump that starts the earliest */
1004 statusPtr = (statusP) ptr;
1005 if (statusPtr->scheduledDump < youngest->scheduledDump)
1006 youngest = statusPtr;
1007 ptr = ptr->dlq_next;
1010 /* Print token expiration time */
1011 if ((ttoken.endTime > prevTime)
1012 && (ttoken.endTime <= youngest->scheduledDump) && as
1013 && (ttoken.endTime != NEVERDATE)) {
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");
1021 prevTime = youngest->scheduledDump;
1023 /* Print the info */
1024 compactDateString(&youngest->scheduledDump, ds, 50);
1025 printf("Job %d:", youngest->jobNumber);
1026 printf(" %16s: %s", ds, youngest->cmdLine);
1029 /* return to original list */
1030 dlqUnlink((dlqlinkP) youngest);
1031 dlqLinkb(&statusHead, (dlqlinkP) youngest);
1034 /* Print token expiration time if havn't already */
1035 if ((ttoken.endTime == NEVERDATE) && as)
1036 printf(" : TOKEN NEVER EXPIRES\n");
1037 else if ((ttoken.endTime > prevTime) && as) {
1038 if (ttoken.endTime > time(0)) {
1039 compactDateString(&ttoken.endTime, ds, 50);
1040 printf(" %16s: TOKEN EXPIRATION\n", ds);
1042 printf(" : TOKEN HAS EXPIRED\n");
1050 bc_KillCmd(as, arock)
1051 struct cmd_syndesc *as;
1056 struct bc_dumpTask *td;
1064 extern dlqlinkT statusHead;
1065 extern statusP findStatus();
1068 tp = as->parms[0].items->data;
1069 if (strchr(tp, '.') == 0) {
1070 slot = bc_SafeATOI(tp);
1072 com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1077 ptr = (&statusHead)->dlq_next;
1078 while (ptr != &statusHead) {
1079 statusPtr = (statusP) ptr;
1080 if (statusPtr->jobNumber == slot) {
1081 statusPtr->flags |= ABORT_REQUEST;
1085 ptr = ptr->dlq_next;
1089 fprintf(stderr, "Job %d not found\n", slot);
1094 for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1095 if (td->flags & BC_DI_INUSE) {
1097 strcpy(tbuffer, td->volSetName);
1098 strcat(tbuffer, ".");
1099 strcat(tbuffer, tailCompPtr(td->dumpName));
1100 if (strcmp(tbuffer, tp) == 0)
1104 if (i >= BC_MAXSIMDUMPS) {
1105 com_err(whoami, 0, "Can't find job %s", tp);
1110 statusPtr = findStatus(td->dumpID);
1112 if (statusPtr == 0) {
1113 com_err(whoami, 0, "Can't locate status - internal error");
1117 statusPtr->flags |= ABORT_REQUEST;
1124 /* restore a volume or volumes */
1125 bc_VolRestoreCmd(as, arock)
1126 struct cmd_syndesc *as;
1130 * parm 0 is the new server to restore to
1131 * parm 1 is the new partition to restore to
1132 * parm 2 is volume(s) to restore
1133 * parm 3 is the new extension, if any, for the volume name.
1134 * parm 4 gives the new volume # to restore this volume as (removed).
1135 * parm 4 date is a string representing the date
1137 * We handle four types of restores. If old is set, then we restore the
1138 * volume with the same name and ID. If old is not set, we allocate
1139 * a new volume ID for the restored volume. If a new extension is specified,
1140 * we add that extension to the volume name of the restored volume.
1142 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1143 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1144 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1145 struct bc_volumeDump *tvol; /* temp for same */
1146 struct sockaddr_in destServ; /* machine to which to restore volumes */
1147 afs_int32 destPartition; /* partition to which to restore volumes */
1149 struct cmd_item *ti;
1153 char *newExt, *timeString;
1154 afs_int32 i, portRemain;
1155 afs_int32 *ports = NULL;
1156 afs_int32 portCount = 0;
1159 code = bc_UpdateHosts();
1161 com_err(whoami, code, "; Can't retrieve tape hosts");
1165 /* specified other destination host */
1166 if (as->parms[0].items) {
1167 tp = as->parms[0].items->data;
1168 if (bc_ParseHost(tp, &destServ)) {
1169 com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1174 /* specified other destination partition */
1175 if (as->parms[1].items) {
1176 tp = as->parms[1].items->data;
1177 if (bc_GetPartitionID(tp, &destPartition)) {
1178 com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1183 for (ti = as->parms[2].items; ti; ti = ti->next) {
1184 /* build list of volume items */
1185 tvol = (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1187 com_err(whoami, BC_NOMEM, "");
1190 memset(tvol, 0, sizeof(struct bc_volumeDump));
1192 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1194 com_err(whoami, BC_NOMEM, "");
1197 strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1198 tvol->entry = &tvolumeEntry;
1201 lastVol->next = tvol; /* thread onto end of list */
1203 volsToRestore = tvol;
1207 if (as->parms[4].items) {
1208 timeString = concatParams(as->parms[4].items);
1212 code = ktime_DateToLong(timeString, &fromDate);
1215 com_err(whoami, 0, "Can't parse restore date and time");
1216 com_err(whoami, 0, "%s", ktime_GetDateUsage());
1220 fromDate = 0x7fffffff; /* latest one */
1223 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1226 /* Read all the port offsets into the ports array. The first element in the
1227 * array is for full restore and the rest are for incremental restores
1229 if (as->parms[5].items) {
1230 for (ti = as->parms[5].items; ti; ti = ti->next)
1232 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1234 com_err(whoami, BC_NOMEM, "");
1238 for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1239 ports[i] = getPortOffset(ti->data);
1245 dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1248 * Perform the call to start the restore.
1251 bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1252 &destServ, destPartition, fromDate, newExt, oldFlag,
1253 /*parentDump */ 0, /*dumpLevel */ 0,
1254 bc_Restorer, ports, portCount,
1255 /*dumpSched */ 0, /*append */ 0, dontExecute);
1257 com_err(whoami, code, "; Failed to queue restore");
1262 /* restore a whole partition or server */
1264 /* bc_DiskRestoreCmd
1265 * restore a whole partition
1267 * first, reqd - machine (server) to restore
1268 * second, reqd - partition to restore
1272 bc_DiskRestoreCmd(as, arock)
1273 struct cmd_syndesc *as;
1276 struct bc_volumeSet tvolumeSet; /* temporary volume set for EvalVolumeSet call */
1277 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1278 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1279 struct sockaddr_in destServ; /* machine to which to restore volumes */
1280 afs_int32 destPartition; /* partition to which to restore volumes */
1286 afs_int32 *ports = NULL;
1287 afs_int32 portCount = 0;
1289 struct bc_volumeDump *prev, *tvol, *nextvol;
1290 struct cmd_item *ti;
1293 /* parm 0 is the server to restore
1294 * parm 1 is the partition to restore
1296 * parm 8 and above as in VolRestoreCmd:
1297 * parm 8 is the new server to restore to
1298 * parm 9 is the new partition to restore to
1301 code = bc_UpdateVolumeSet();
1303 com_err(whoami, code, "; Can't retrieve volume sets");
1306 code = bc_UpdateHosts();
1308 com_err(whoami, code, "; Can't retrieve tape hosts");
1312 /* create a volume set corresponding to the volume pattern we've been given */
1313 memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1314 memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1315 tvolumeSet.name = "TempVolumeSet";
1316 tvolumeSet.ventries = &tvolumeEntry;
1317 tvolumeEntry.serverName = as->parms[0].items->data;
1318 tvolumeEntry.partname = as->parms[1].items->data;
1320 if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1321 com_err(whoami, 0, "Can't parse partition '%s'",
1322 tvolumeEntry.partname);
1326 if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1327 com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1331 /* specified other destination host */
1332 if (as->parms[8].items) {
1333 tp = as->parms[8].items->data;
1334 if (bc_ParseHost(tp, &destServ)) {
1335 com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1338 } else /* use destination host == original host */
1339 memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1341 /* specified other destination partition */
1342 if (as->parms[9].items) {
1343 tp = as->parms[9].items->data;
1344 if (bc_GetPartitionID(tp, &destPartition)) {
1345 com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1348 } else /* use original partition */
1349 destPartition = tvolumeEntry.partition;
1351 tvolumeEntry.name = ".*"; /* match all volumes (this should be a parameter) */
1353 if (as->parms[2].items) {
1354 for (ti = as->parms[2].items; ti; ti = ti->next)
1356 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1358 com_err(whoami, BC_NOMEM, "");
1362 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1363 ports[i] = getPortOffset(ti->data);
1369 newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1370 dontExecute = (as->parms[11].items ? 1 : 0); /* -n */
1373 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1376 bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1379 com_err(whoami, code, "; Failed to evaluate volume set");
1383 /* Since we want only RW volumes, remove any
1384 * BK or RO volumes from the list.
1386 for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1387 nextvol = tvol->next;
1388 if (BackupName(tvol->name)) {
1391 "Will not restore volume %s (its RW does not reside on the partition)\n",
1394 if (tvol == volsToRestore) {
1395 volsToRestore = nextvol;
1397 prev->next = nextvol;
1407 fromDate = 0x7fffffff; /* last one before this date */
1408 oldFlag = 1; /* do restore same volid (and name) */
1411 * Perform the call to start the dump.
1414 bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1415 &destServ, destPartition, fromDate, newExt, oldFlag,
1416 /*parentDump */ 0, /*dumpLevel */ 0,
1417 bc_Restorer, ports, portCount,
1418 /*dumpSched */ 0, /*append */ 0, dontExecute);
1420 com_err(whoami, code, "; Failed to queue restore");
1425 /* bc_VolsetRestoreCmd
1426 * restore a volumeset or list of volumes.
1429 bc_VolsetRestoreCmd(as, arock)
1430 struct cmd_syndesc *as;
1438 afs_int32 *ports = NULL;
1439 afs_int32 portCount = 0;
1441 afs_int32 portoffset = 0;
1443 struct bc_volumeSet *volsetPtr; /* Ptr to list of generated volume info */
1444 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1445 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1446 struct sockaddr_in destServer; /* machine to which to restore volume */
1447 afs_int32 destPartition; /* partition to which to restore volumes */
1448 struct cmd_item *ti;
1451 code = bc_UpdateVolumeSet();
1453 com_err(whoami, code, "; Can't retrieve volume sets");
1456 code = bc_UpdateHosts();
1458 com_err(whoami, code, "; Can't retrieve tape hosts");
1462 if (as->parms[0].items) {
1463 if (as->parms[1].items) {
1464 com_err(whoami, 0, "Can't have both -name and -file options");
1468 volsetName = as->parms[0].items->data;
1469 volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1472 "Can't find volume set '%s' in backup database",
1477 /* Expand out the volume set into its component list of volumes. */
1479 bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1482 com_err(whoami, code, "; Failed to evaluate volume set");
1485 } else if (as->parms[1].items) {
1488 char server[50], partition[50], volume[50], rest[256];
1490 struct bc_volumeDump *tvol;
1492 fd = fopen(as->parms[1].items->data, "r");
1494 com_err(whoami, errno, "; Cannot open file '%s'",
1495 as->parms[1].items->data);
1499 while (fgets(line, 255, fd)) {
1501 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1507 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1508 fprintf(stderr, " %s", line);
1512 if (bc_ParseHost(server, &destServer)) {
1513 com_err(whoami, 0, "Failed to locate host '%s'", server);
1517 if (bc_GetPartitionID(partition, &destPartition)) {
1519 "Failed to parse destination partition '%s'",
1524 /* Allocate a volumeDump structure and link it in */
1526 (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1527 memset(tvol, 0, sizeof(struct bc_volumeDump));
1529 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1531 com_err(whoami, BC_NOMEM, "");
1534 strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1535 memcpy(&tvol->server, &destServer, sizeof(destServer));
1536 tvol->partition = destPartition;
1539 lastVol->next = tvol; /* thread onto end of list */
1541 volsToRestore = tvol;
1546 com_err(whoami, 0, "-name or -file option required");
1551 /* Get the port offset for the restore */
1552 if (as->parms[2].items) {
1553 for (ti = as->parms[2].items; ti; ti = ti->next)
1555 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1557 com_err(whoami, BC_NOMEM, "");
1561 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1562 ports[i] = getPortOffset(ti->data);
1568 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1569 dontExecute = (as->parms[4].items ? 1 : 0);
1571 fromDate = 0x7fffffff; /* last one before this date */
1572 oldFlag = 1; /* do restore same volid (and name) */
1574 /* Perform the call to start the restore */
1575 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1576 /*destserver */ 0, /*destpartition */ 0, fromDate,
1578 /*parentDump */ 0, /*dumpLevel */ 0,
1579 bc_Restorer, ports, portCount,
1580 /*dumpSched */ 0, /*append */ 0, dontExecute);
1582 com_err(whoami, code, "; Failed to queue restore");
1587 /*-----------------------------------------------------------------------------
1591 * Perform a dump of the set of volumes provided.
1592 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1595 * as : Parsed command line information.
1596 * arock : Ptr to misc stuff; not used.
1600 * The result of bc_StartDump() otherwise
1607 *---------------------------------------------------------------------------
1612 bc_DumpCmd(as, arock)
1613 struct cmd_syndesc *as;
1616 static char rn[] = "bc_DumpCmd"; /*Routine name */
1617 char *dumpPath, *vsName; /*Ptrs to various names */
1618 struct bc_volumeSet *tvs; /*Ptr to list of generated volume info */
1619 struct bc_dumpSchedule *tds, *baseds; /*Ptr to dump schedule node */
1620 struct bc_volumeDump *tve, *volsToDump; /*Ptr to individual vols to be dumped */
1621 struct bc_volumeDump *ntve, *tves, *ptves, *rtves;
1622 struct budb_dumpEntry dumpEntry, de, fde; /* dump entry */
1625 afs_int32 parent; /* parent dump */
1626 afs_int32 level; /* this dump's level # */
1627 afs_int32 problemFindingDump; /* can't find parent(s) */
1629 afs_int32 *portp = NULL;
1630 afs_int32 portCount = 0;
1631 afs_int32 doAt, atTime; /* Time a timed-dump is to start at */
1634 int doAppend; /* Append the dump to dump set */
1635 afs_int32 code; /* Return code */
1636 int loadfile; /* whether to load a file or not */
1638 struct bc_dumpTask *dumpTaskPtr; /* for dump thread */
1639 afs_int32 dumpTaskSlot;
1642 int r, nservers, ns, serverfound;
1644 extern struct bc_dumpTask bc_dumpTasks[];
1645 extern afs_int32 bcdb_FindLastVolClone();
1646 extern afs_int32 volImageTime();
1648 code = bc_UpdateDumpSchedule();
1650 com_err(whoami, code, "; Can't retrieve dump schedule");
1653 code = bc_UpdateVolumeSet();
1655 com_err(whoami, code, "; Can't retrieve volume sets");
1658 code = bc_UpdateHosts();
1660 com_err(whoami, code, "; Can't retrieve tape hosts");
1665 * Some parameters cannot be specified together
1666 * The "-file" option cannot exist with the "-volume", "-dump",
1667 * "-portoffset", or "-append" option
1669 if (as->parms[6].items) {
1671 if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1672 || as->parms[4].items) {
1673 com_err(whoami, 0, "Invalid option specified with -file option");
1678 if (!as->parms[0].items || !as->parms[1].items) {
1680 "Must specify volume set name and dump level name");
1686 * Get the time we are to perform this dump
1688 if (as->parms[3].items) {
1691 timeString = concatParams(as->parms[3].items);
1696 * Now parse this string for the time to start.
1698 code = ktime_DateToLong(timeString, &atTime);
1701 com_err(whoami, 0, "Can't parse dump start date and time");
1702 com_err(whoami, 0, "%s", ktime_GetDateUsage());
1708 dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1711 * If this dump is not a load file, then check the parameters.
1713 if (!loadfile) { /*6 */
1714 vsName = as->parms[0].items->data; /* get volume set name */
1715 dumpPath = as->parms[1].items->data; /* get dump path */
1717 /* get the port number, if one was specified */
1718 if (as->parms[2].items) {
1720 portp = (afs_int32 *) malloc(sizeof(afs_int32));
1722 com_err(whoami, BC_NOMEM, "");
1726 *portp = getPortOffset(as->parms[2].items->data);
1731 doAppend = (as->parms[4].items ? 1 : 0); /* -append */
1734 * Get a hold of the given volume set and dump set.
1736 tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1739 "Can't find volume set '%s' in backup database", vsName);
1742 baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1745 "Can't find dump schedule '%s' in backup database",
1754 * If given the "-at" option, then add this to the jobs list and return
1757 * Create a status node for this timed dump.
1758 * Fill in the time to dump and the cmd line for the dump leaving off
1759 * the -at option. If the -n option is there, it is scheduled with
1760 * the Timed dump as opposed to not scheduling the time dump at all.
1763 if (atTime < time(0)) {
1765 "Time of dump is earlier then current time - not added");
1767 statusPtr = createStatusNode();
1769 statusPtr->scheduledDump = atTime;
1771 /* Determine length of the dump command */
1773 length += 4; /* "dump" */
1775 length += 6; /* " -file" */
1776 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1778 /* length += 11; *//* " -volumeset" */
1779 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1781 /* length += 6; *//* " -dump" */
1782 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1784 if (as->parms[2].items) {
1785 /* length += 12; *//* " -portoffset" */
1786 length += 1 + strlen(as->parms[2].items->data); /* " <port>" */
1789 if (as->parms[4].items)
1790 length += 8; /* " -append" */
1794 length += 3; /* " -n" */
1795 length++; /* end-of-line */
1797 /* Allocate status block for this timed dump */
1798 sprintf(statusPtr->taskName, "Scheduled Dump");
1799 statusPtr->jobNumber = bc_jobNumber();
1800 statusPtr->scheduledDump = atTime;
1801 statusPtr->cmdLine = (char *)malloc(length);
1802 if (!statusPtr->cmdLine) {
1803 com_err(whoami, BC_NOMEM, "");
1807 /* Now reconstruct the dump command */
1808 statusPtr->cmdLine[0] = 0;
1809 strcat(statusPtr->cmdLine, "dump");
1811 strcat(statusPtr->cmdLine, " -file");
1812 strcat(statusPtr->cmdLine, " ");
1813 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1815 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1816 strcat(statusPtr->cmdLine, " ");
1817 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1819 /* strcat(statusPtr->cmdLine, " -dump"); */
1820 strcat(statusPtr->cmdLine, " ");
1821 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1823 if (as->parms[2].items) {
1824 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1825 strcat(statusPtr->cmdLine, " ");
1826 strcat(statusPtr->cmdLine, as->parms[2].items->data);
1829 if (as->parms[4].items)
1830 strcat(statusPtr->cmdLine, " -append");
1833 strcat(statusPtr->cmdLine, " -n");
1835 printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1836 if ((atTime > ttoken.endTime) && (ttoken.endTime != NEVERDATE))
1838 "Warning: job %d starts after expiration of AFS token",
1839 statusPtr->jobNumber);
1848 * Read and execute the load file if specified. The work of reading is done
1849 * in the main routine prior the dispatch call. loadFile and dontExecute are
1850 * global variables so this can take place in main.
1853 loadFile = (char *)malloc(strlen(as->parms[6].items->data) + 1);
1855 com_err(whoami, BC_NOMEM, "");
1858 strcpy(loadFile, as->parms[6].items->data);
1863 * We are doing a real dump (no load file or timed dump).
1865 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1868 /* For each dump-level above this one, see if the volumeset was dumped
1869 * at the level. We search all dump-levels since a higher dump-level
1870 * may have actually been done AFTER a lower dump level.
1872 parent = level = problemFindingDump = 0;
1873 for (tds = baseds->parent; tds; tds = tds->parent) {
1874 /* Find the most recent dump of the volume-set at this dump-level.
1875 * Raise problem flag if didn't find a dump and a parent not yet found.
1877 code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1880 problemFindingDump = 1; /* skipping a dump level */
1884 /* We found the most recent dump at this level. Now check
1885 * if we should use it by seeing if its full dump hierarchy
1886 * exists. If it doesn't, we don't want to base our incremental
1889 if (!parent || (dumpEntry.id > parent)) {
1890 /* Follow the parent dumps to see if they are all there */
1891 for (d = dumpEntry.parent; d; d = de.parent) {
1892 code = bcdb_FindDumpByID(d, &de);
1897 /* If we found the entire level, remember it. Otherwise raise flag.
1898 * If we had already found a dump, raise the problem flag.
1902 problemFindingDump = 1;
1903 parent = dumpEntry.id;
1904 level = dumpEntry.level + 1;
1905 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1907 /* Dump hierarchy not complete so can't base off the latest */
1908 problemFindingDump = 1;
1913 /* If the problemflag was raise, it means we are not doing the
1914 * dump at the level we requested it be done at.
1916 if (problemFindingDump) {
1918 "Warning: Doing level %d dump due to missing higher-level dumps",
1921 printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1923 } else if (parent) {
1924 printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1927 /* Expand out the volume set into its component list of volumes. */
1928 code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1930 com_err(whoami, code, "; Failed to evaluate volume set");
1934 printf("No volumes to dump\n");
1938 /* Determine what the clone time of the volume was when it was
1939 * last dumped (tve->date). This is the time from when an
1940 * incremental should be done (remains zero if a full dump).
1943 for (tve = volsToDump; tve; tve = tve->next) {
1944 code = bcdb_FindClone(parent, tve->name, &tve->date);
1948 /* Get the time the volume was last cloned and see if the volume has
1949 * changed since then. Only do this when the "-n" flag is specified
1950 * because butc will get the cloneDate at time of dump.
1954 volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1955 tve->vid, tve->volType, &tve->cloneDate);
1959 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1961 "Warning: Timestamp on volume %s unchanged from previous dump",
1969 printf("Would have dumped the following volumes:\n");
1971 printf("Preparing to dump the following volumes:\n");
1972 for (tve = volsToDump; tve; tve = tve->next) {
1973 printf("\t%s (%u)\n", tve->name, tve->vid);
1978 code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1979 /*destServer */ 0, /*destPartition */ 0,
1981 /*newExt */ 0, /*oldFlag */ 0,
1982 parent, level, bc_Dumper, portp, /*portCount */ 1,
1983 baseds, doAppend, dontExecute);
1985 com_err(whoami, code, "; Failed to queue dump");
1992 * terminate the backup process. Insists that that all running backup
1993 * jobs be terminated before it will quit
1998 bc_QuitCmd(as, arock)
1999 struct cmd_syndesc *as;
2003 struct bc_dumpTask *td;
2004 extern dlqlinkT statusHead;
2008 /* Check the status list for outstanding jobs */
2010 for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
2011 ptr = ptr->dlq_next) {
2012 statusPtr = (statusP) ptr;
2013 if (!(statusPtr->flags & ABORT_REQUEST)) {
2015 com_err(whoami, 0, "Job %d still running (and not aborted)",
2016 statusPtr->jobNumber);
2018 "You must at least 'kill' all running jobs before quitting");
2024 /* A job still being initialized (but no status structure or job number since it
2025 * has not been handed to a butc process yet)
2027 for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
2028 if (td->flags & BC_DI_INUSE) {
2029 com_err(whoami, 0, "A job is still running");
2031 "You must at least 'kill' all running jobs before quitting");
2037 /* close the all temp text files before quitting */
2038 for (i = 0; i < TB_NUM; i++)
2039 bc_closeTextFile(&bc_globalConfig->configText[i],
2040 &bc_globalConfig->tmpTextFileNames[i][0]);
2046 * Labels a tape i.e. request the tape coordinator to perform this
2050 bc_LabelTapeCmd(as, arock)
2051 struct cmd_syndesc *as;
2054 char *tapename = 0, *pname = 0;
2059 code = bc_UpdateHosts();
2061 com_err(whoami, code, "; Can't retrieve tape hosts");
2065 if (as->parms[0].items) { /* -name */
2066 tapename = as->parms[0].items->data;
2067 if (strlen(tapename) >= TC_MAXTAPELEN) {
2068 com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2073 if (as->parms[3].items) { /* -pname */
2075 com_err(whoami, 0, "Can only specify -name or -pname");
2078 pname = as->parms[3].items->data;
2079 if (strlen(pname) >= TC_MAXTAPELEN) {
2080 com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2085 if (as->parms[1].items) {
2086 size = bc_FloatATOI(as->parms[1].items->data);
2088 com_err(whoami, 0, "Bad syntax for tape size '%s'",
2089 as->parms[1].items->data);
2095 if (as->parms[2].items) {
2096 port = getPortOffset(as->parms[2].items->data);
2101 code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2108 * read the label on a tape
2110 * optional port number
2113 bc_ReadLabelCmd(as, arock)
2114 struct cmd_syndesc *as;
2120 code = bc_UpdateHosts();
2122 com_err(whoami, code, "; Can't retrieve tape hosts");
2126 if (as->parms[0].items) {
2127 port = getPortOffset(as->parms[0].items->data);
2132 code = bc_ReadLabel(bc_globalConfig, port);
2139 * read content information from dump tapes, and if user desires,
2140 * add it to the database
2143 bc_ScanDumpsCmd(as, arock)
2144 struct cmd_syndesc *as;
2148 afs_int32 dbAddFlag = 0;
2151 code = bc_UpdateHosts();
2153 com_err(whoami, code, "; Can't retrieve tape hosts");
2157 /* check for flag */
2158 if (as->parms[0].items != 0) { /* add scan to database */
2162 /* check for port */
2163 if (as->parms[1].items) {
2164 port = getPortOffset(as->parms[1].items->data);
2169 code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2173 /* bc_ParseExpiration
2176 * dates are specified as absolute or relative, the syntax is:
2177 * absolute: at %d/%d/%d [%d:%d] where [..] is optional
2178 * relative: in [%dy][%dm][%dd] where at least one component
2183 bc_ParseExpiration(paramPtr, expType, expDate)
2184 struct cmd_parmdesc *paramPtr;
2188 struct cmd_item *itemPtr;
2189 struct ktime_date kt;
2190 char *dateString = 0;
2193 *expType = BC_NO_EXPDATE;
2196 if (!paramPtr->items)
2197 ERROR(0); /* no expiration specified */
2199 /* some form of expiration date specified. First validate the prefix */
2200 itemPtr = paramPtr->items;
2202 if (strcmp(itemPtr->data, "at") == 0) {
2203 *expType = BC_ABS_EXPDATE;
2205 dateString = concatParams(itemPtr->next);
2209 code = ktime_DateToLong(dateString, expDate);
2212 } else if (strcmp(itemPtr->data, "in") == 0) {
2213 *expType = BC_REL_EXPDATE;
2215 dateString = concatParams(itemPtr->next);
2219 code = ParseRelDate(dateString, &kt);
2222 *expDate = ktimeRelDate_ToLong(&kt);
2224 dateString = concatParams(itemPtr);
2228 if (ktime_DateToLong(dateString, expDate) == 0) {
2229 *expType = BC_ABS_EXPDATE;
2230 code = ktime_DateToLong(dateString, expDate);
2233 } else if (ParseRelDate(dateString, &kt) == 0) {
2234 *expType = BC_REL_EXPDATE;
2235 *expDate = ktimeRelDate_ToLong(&kt);
2247 /* database lookup command and routines */
2250 * Currently a single option, volumename to search for. Reports
2251 * all dumps containing the specified volume
2254 bc_dblookupCmd(as, arock)
2255 struct cmd_syndesc *as;
2258 struct cmd_item *ciptr;
2261 ciptr = as->parms[0].items;
2262 if (ciptr == 0) /* no argument specified */
2265 code = DBLookupByVolume(ciptr->data);
2271 /* for ubik version */
2273 bc_dbVerifyCmd(as, arock)
2274 struct cmd_syndesc *as;
2281 struct hostent *hostPtr;
2285 extern struct udbHandleS udbHandle;
2287 detail = (as->parms[0].items ? 1 : 0); /* print more details */
2290 ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0, &status, &orphans,
2294 com_err(whoami, code, "; Unable to verify database");
2298 /* verification call succeeded */
2301 printf("Database OK\n");
2303 com_err(whoami, status, "; Database is NOT_OK");
2306 printf("Orphan blocks %d\n", orphans);
2309 printf("Unable to lookup host id\n");
2311 hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2313 printf("Database checker was %d.%d.%d.%d\n",
2314 ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2315 ((host & 0xFF00) >> 8), (host & 0xFF));
2317 printf("Database checker was %s\n", hostPtr->h_name);
2320 return ((status ? -1 : 0));
2324 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2326 deleteDump(dumpid, port, force)
2327 afs_uint32 dumpid; /* The dumpid to delete */
2328 afs_int32 port; /* port==-1 means don't go to butc */
2331 afs_int32 code = 0, tcode;
2332 struct budb_dumpEntry dumpEntry;
2333 struct rx_connection *tconn = 0;
2334 afs_int32 i, taskflag, xbsadump;
2335 statusP statusPtr = 0;
2336 budb_dumpsList dumps;
2339 /* If the port is set, we will try to send a delete request to the butc */
2341 tcode = bc_UpdateHosts();
2343 com_err(whoami, tcode, "; Can't retrieve tape hosts");
2347 /* Find the dump in the backup database */
2348 tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2350 com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2354 xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2356 /* If dump is to an XBSA server, connect to butc and send it
2357 * the dump to delete. Butc will contact the XBSA server.
2358 * The dump will not be an appended dump because XBSA butc
2359 * does not support the append option.
2361 if (xbsadump && dumpEntry.nVolumes) {
2362 tcode = ConnectButc(bc_globalConfig, port, &tconn);
2366 tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2368 if (tcode == RXGEN_OPCODE)
2369 tcode = BC_VERSIONFAIL;
2370 com_err(whoami, tcode,
2371 "; Unable to delete dumpID %u via butc", dumpid);
2375 statusPtr = createStatusNode();
2377 statusPtr->taskId = taskId;
2378 statusPtr->port = port;
2379 statusPtr->jobNumber = bc_jobNumber();
2380 statusPtr->flags |= (SILENT | NOREMOVE); /* No msg & keep statusPtr */
2381 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2382 sprintf(statusPtr->taskName, "DeleteDump");
2385 /* Wait for task to finish */
2386 taskflag = waitForTask(taskId);
2387 if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2388 com_err(whoami, BUTX_DELETEOBJFAIL,
2389 "; Unable to delete dumpID %u via butc", dumpid);
2390 ERROR(BUTX_DELETEOBJFAIL); /* the task failed */
2397 deleteStatusNode(statusPtr); /* Clean up statusPtr - because NOREMOVE */
2399 rx_DestroyConnection(tconn); /* Destroy the connection */
2401 /* Remove the dump from the backup database */
2402 if (!code || force) {
2403 dumps.budb_dumpsList_len = 0;
2404 dumps.budb_dumpsList_val = 0;
2406 tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2408 com_err(whoami, tcode,
2409 "; Unable to delete dumpID %u from database", dumpid);
2410 dumps.budb_dumpsList_len = 0;
2415 /* Display the dumps that were deleted - includes appended dumps */
2416 for (i = 0; i < dumps.budb_dumpsList_len; i++)
2417 printf(" %u%s\n", dumps.budb_dumpsList_val[i],
2418 (i > 0) ? " Appended Dump" : "");
2419 if (dumps.budb_dumpsList_val)
2420 free(dumps.budb_dumpsList_val);
2427 * Delete a specified dump from the database
2429 * dump id - single required arg as param 0.
2432 bc_deleteDumpCmd(as, arock)
2433 struct cmd_syndesc *as;
2438 afs_int32 rcode = 0;
2439 afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2440 struct cmd_item *ti;
2441 afs_uint32 fromTime = 0, toTime = 0, havetime = 0;
2443 budb_dumpsList dumps, flags;
2445 afs_int32 port = -1, dbonly = 0, force;
2448 /* Must specify at least one of -dumpid, -from, or -to */
2449 if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2450 && !as->parms[4].items) {
2451 com_err(whoami, 0, "Must specify at least one field");
2455 /* Must have -to option with -from option */
2456 if (as->parms[1].items && !as->parms[2].items) {
2457 com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2461 /* Get the time to delete from */
2462 if (as->parms[1].items) { /* -from */
2463 timeString = concatParams(as->parms[1].items);
2468 * Now parse this string for the time to start.
2470 code = ktime_DateToLong(timeString, &fromTime);
2473 com_err(whoami, 0, "Can't parse 'from' date and time");
2474 com_err(whoami, 0, "%s", ktime_GetDateUsage());
2480 port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0); /* -port */
2481 if (as->parms[5].items) /* -dbonly */
2484 force = (as->parms[6].items ? 1 : 0);
2486 havegroupid = (as->parms[4].items ? 1 : 0);
2488 groupId = atoi(as->parms[4].items->data);
2490 noexecute = (as->parms[7].items ? 1 : 0);
2492 /* Get the time to delete to */
2493 if (as->parms[2].items) { /* -to */
2494 timeString = concatParams(as->parms[2].items);
2499 * Now parse this string for the time to start. Simce
2500 * times are at minute granularity, add 59 seconds.
2502 code = ktime_DateToLong(timeString, &toTime);
2505 com_err(whoami, 0, "Can't parse 'to' date and time");
2506 com_err(whoami, 0, "%s", ktime_GetDateUsage());
2513 if (fromTime > toTime) {
2515 "'-from' date/time cannot be later than '-to' date/time");
2519 /* Remove speicific dump ids - if any */
2520 printf("The following dumps %s deleted:\n",
2521 (noexecute ? "would have been" : "were"));
2522 for (ti = as->parms[0].items; ti != 0; ti = ti->next) { /* -dumpid */
2523 dumpid = atoi(ti->data);
2525 code = deleteDump(dumpid, port, force);
2527 printf(" %u\n", dumpid);
2532 * Now remove dumps between to and from dates.
2534 if (havegroupid || havetime) {
2535 dumps.budb_dumpsList_len = 0;
2536 dumps.budb_dumpsList_val = 0;
2537 flags.budb_dumpsList_len = 0;
2538 flags.budb_dumpsList_val = 0;
2541 sflags |= BUDB_OP_GROUPID;
2543 sflags |= BUDB_OP_DATES;
2546 bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2548 com_err(whoami, code,
2549 "; Error while deleting dumps from %u to %u", fromTime,
2554 for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2555 if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2559 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2561 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2562 /* Ignore code and continue */
2564 printf(" %u%s%s\n", dumps.budb_dumpsList_val[i],
2566 budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2567 " Appended Dump" : "",
2569 budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2570 " Database Dump" : "");
2575 if (dumps.budb_dumpsList_val)
2576 free(dumps.budb_dumpsList_val);
2577 dumps.budb_dumpsList_len = 0;
2578 dumps.budb_dumpsList_val = 0;
2579 if (flags.budb_dumpsList_val)
2580 free(flags.budb_dumpsList_val);
2581 flags.budb_dumpsList_len = 0;
2582 flags.budb_dumpsList_val = 0;
2588 bc_saveDbCmd(as, arock)
2589 struct cmd_syndesc *as;
2592 struct rx_connection *tconn;
2593 afs_int32 portOffset = 0;
2600 code = bc_UpdateHosts();
2602 com_err(whoami, code, "; Can't retrieve tape hosts");
2606 if (as->parms[0].items) {
2607 portOffset = getPortOffset(as->parms[0].items->data);
2612 /* Get the time to delete to */
2613 if (as->parms[1].items) {
2614 timeString = concatParams(as->parms[1].items);
2619 * Now parse this string for the time. Since
2620 * times are at minute granularity, add 59 seconds.
2622 code = ktime_DateToLong(timeString, &toTime);
2625 com_err(whoami, 0, "Can't parse '-archive' date and time");
2626 com_err(whoami, 0, "%s", ktime_GetDateUsage());
2633 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2637 code = TC_SaveDb(tconn, toTime, &taskId);
2639 com_err(whoami, code, "; Failed to save database");
2643 /* create status monitor block */
2644 statusPtr = createStatusNode();
2646 statusPtr->taskId = taskId;
2647 statusPtr->port = portOffset;
2648 statusPtr->jobNumber = bc_jobNumber();
2649 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2650 sprintf(statusPtr->taskName, "SaveDb");
2654 rx_DestroyConnection(tconn);
2658 bc_restoreDbCmd(as, arock)
2659 struct cmd_syndesc *as;
2662 struct rx_connection *tconn;
2663 afs_int32 portOffset = 0;
2668 code = bc_UpdateHosts();
2670 com_err(whoami, code, "; Can't retrieve tape hosts");
2674 if (as->parms[0].items) {
2675 portOffset = getPortOffset(as->parms[0].items->data);
2680 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2684 code = TC_RestoreDb(tconn, &taskId);
2686 com_err(whoami, code, "; Failed to restore database");
2690 /* create status monitor block */
2691 statusPtr = createStatusNode();
2693 statusPtr->taskId = taskId;
2694 statusPtr->port = portOffset;
2695 statusPtr->jobNumber = bc_jobNumber();
2696 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2697 sprintf(statusPtr->taskName, "RestoreDb");
2701 rx_DestroyConnection(tconn);
2705 /* ----------------------------------
2706 * supporting routines for database examination
2707 * ----------------------------------
2710 /* structures and defines for DBLookupByVolume */
2712 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2714 /* dumpedVol - saves interesting information so that we can print it out
2719 struct dumpedVol *next;
2721 afs_int32 initialDumpID;
2722 char tapeName[BU_MAXTAPELEN];
2725 afs_int32 createTime;
2726 afs_int32 incTime; /* actually the clone time */
2729 /* -----------------------------------------
2730 * routines for examining the database
2731 * -----------------------------------------
2735 * Lookup the volumename in the backup database and print the results
2737 * volumeName - volume to lookup
2740 DBLookupByVolume(volumeName)
2743 struct budb_dumpEntry dumpEntry;
2744 struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2745 afs_int32 numEntries;
2746 afs_int32 tapedumpid;
2747 afs_int32 last, next;
2749 struct dumpedVol *dvptr = 0;
2750 struct dumpedVol *tempPtr = 0;
2753 char vname[BU_MAXNAMELEN];
2756 for (pass = 0; pass < 2; pass++) {
2758 /* On second pass, search for backup volume */
2760 if (!BackupName(volumeName)) {
2761 strcpy(vname, volumeName);
2762 strcat(vname, ".backup");
2770 while (next != -1) { /*w */
2772 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2773 DBL_MAX_VOLUMES, &numEntries);
2777 /* add the volumes to the list */
2778 for (i = 0; i < numEntries; i++) { /*f */
2779 struct dumpedVol *insPtr, **prevPtr;
2782 (struct dumpedVol *)malloc(sizeof(struct dumpedVol));
2786 memset(tempPtr, 0, sizeof(*tempPtr));
2787 tempPtr->incTime = volumeEntry[i].clone;
2788 tempPtr->dumpID = volumeEntry[i].dump;
2789 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2792 /* check if we need to null terminate it - just for safety */
2793 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2794 tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2796 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2802 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2803 tempPtr->parent = dumpEntry.parent;
2804 tempPtr->level = dumpEntry.level;
2805 tempPtr->createTime = dumpEntry.created;
2807 /* add volume to list in reverse chronological order */
2811 while ((insPtr != 0)
2812 && (insPtr->createTime > tempPtr->createTime)
2814 prevPtr = &insPtr->next;
2815 insPtr = insPtr->next;
2818 /* now at the right place - insert the block */
2819 tempPtr->next = *prevPtr;
2829 ("DumpID lvl parentID creation date clone date tape name\n");
2830 for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2831 /* For the user, the tape name is its name and initial dump id */
2833 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2836 /* beware the static items in compactDateString */
2837 compactDateString(&tempPtr->createTime, ds, 50);
2838 printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2839 tempPtr->parent, ds);
2840 compactDateString(&tempPtr->incTime, ds, 50);
2841 printf(" %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2847 for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2848 dvptr = dvptr->next;
2853 com_err(whoami, code, "");
2857 /* structures for dumpInfo */
2860 struct volumeLink *nextVolume;
2861 struct budb_volumeEntry volumeEntry;
2865 struct tapeLink *nextTape;
2866 struct budb_tapeEntry tapeEntry;
2867 struct volumeLink *firstVolume;
2872 * print information about a dump and all its tapes and volumes.
2876 dumpInfo(dumpid, detailFlag)
2878 afs_int32 detailFlag;
2880 struct budb_dumpEntry dumpEntry;
2881 struct tapeLink *head = 0;
2882 struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2883 struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2886 afs_int32 last, next, dbTime;
2887 afs_int32 tapedumpid;
2896 extern struct udbHandleS udbHandle;
2899 lastTapeLinkPtr = 0;
2901 lastVolumeLinkPtr = 0;
2903 /* first get information about the dump */
2905 code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2909 /* For the user, the tape name is its name and initial dump id */
2910 tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2912 /* Is this a database dump id or not */
2913 if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2918 /* print out the information about the dump */
2922 printDumpEntry(&dumpEntry);
2925 printf("Dump: id %u, created: %s\n", dumpEntry.id,
2926 ctime(&dumpEntry.created));
2928 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2929 dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2930 ctime(&dumpEntry.created));
2933 if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2934 && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2935 printf("Backup Service: TSM: Server: %s\n",
2936 dumpEntry.tapes.tapeServer);
2939 /* now get the list of tapes */
2940 for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) { /*f */
2941 tapeLinkPtr = (struct tapeLink *)malloc(sizeof(struct tapeLink));
2943 com_err(whoami, BC_NOMEM, "");
2947 memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2948 code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2955 /* add this tape to previous chain */
2956 if (lastTapeLinkPtr) {
2957 lastTapeLinkPtr->nextTape = tapeLinkPtr;
2958 lastTapeLinkPtr = tapeLinkPtr;
2963 lastTapeLinkPtr = head;
2967 while (next != -1) { /*wn */
2968 vl.budb_volumeList_len = 0;
2969 vl.budb_volumeList_val = 0;
2972 /* now get all the volumes in this dump. */
2973 code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name, /* tape name */
2974 dumpid, /* dumpid (not initial dumpid) */
2977 &next, /* nextindex */
2978 &dbTime, /* update time */
2982 if (code == BUDB_ENDOFLIST) { /* 0 volumes on tape */
2989 for (i = 0; i < vl.budb_volumeList_len; i++) {
2990 link = &tapeLinkPtr->firstVolume;
2993 (struct volumeLink *)malloc(sizeof(struct volumeLink));
2994 if (!volumeLinkPtr) {
2995 com_err(whoami, BC_NOMEM, "");
2998 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
3000 memcpy(&volumeLinkPtr->volumeEntry,
3001 &vl.budb_volumeList_val[i],
3002 sizeof(struct budb_volumeEntry));
3004 /* now insert it onto the right place */
3006 && (volumeLinkPtr->volumeEntry.position >
3007 (*link)->volumeEntry.position)) {
3008 link = &((*link)->nextVolume);
3011 /* now link it in */
3012 volumeLinkPtr->nextVolume = *link;
3013 *link = volumeLinkPtr;
3016 if (vl.budb_volumeList_val)
3017 free(vl.budb_volumeList_val);
3021 for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
3025 printTapeEntry(&tapeLinkPtr->tapeEntry);
3027 printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
3029 printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
3030 compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
3031 printf("created %16s", ds);
3032 if (tapeLinkPtr->tapeEntry.expires != 0) {
3033 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3034 printf(", expires %16s", ds);
3039 /* print out all the volumes */
3041 /* print header for volume listing - db dumps have no volumes */
3042 if ((detailFlag == 0) && !dbDump)
3043 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3046 for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
3047 volumeLinkPtr = volumeLinkPtr->nextVolume) {
3049 printf("\nVolume\n");
3051 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3053 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3054 printf("%4d %s %10u %16s\n",
3055 volumeLinkPtr->volumeEntry.position, ds,
3056 volumeLinkPtr->volumeEntry.nBytes,
3057 volumeLinkPtr->volumeEntry.name);
3064 com_err("dumpInfo", code, "; Can't get dump information");
3066 /* free all allocated structures */
3068 while (tapeLinkPtr) {
3069 volumeLinkPtr = tapeLinkPtr->firstVolume;
3070 while (volumeLinkPtr) {
3071 lastVolumeLinkPtr = volumeLinkPtr;
3072 volumeLinkPtr = volumeLinkPtr->nextVolume;
3073 free(lastVolumeLinkPtr);
3076 lastTapeLinkPtr = tapeLinkPtr;
3077 tapeLinkPtr = tapeLinkPtr->nextTape;
3078 free(lastTapeLinkPtr);
3083 compareDump(ptr1, ptr2)
3084 struct budb_dumpEntry *ptr1, *ptr2;
3086 if (ptr1->created < ptr2->created)
3088 else if (ptr1->created > ptr2->created)
3094 printRecentDumps(ndumps)
3098 afs_int32 nextindex, index = 0;
3101 struct budb_dumpEntry *dumpPtr;
3105 extern struct udbHandleS udbHandle;
3106 extern compareDump();
3108 do { /* while (nextindex != -1) */
3109 /* initialize the dump list */
3110 dl.budb_dumpList_len = 0;
3111 dl.budb_dumpList_val = 0;
3113 /* outline algorithm */
3114 code = ubik_Call(BUDB_GetDumps, udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "", /* no name */
3118 &nextindex, &dbTime, &dl);
3120 if (code == BUDB_ENDOFLIST)
3122 com_err("dumpInfo", code, "; Can't get dump information");
3126 /* No need to sort, it's already sorted */
3128 if (dl.budb_dumpList_len && (index == 0))
3129 printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3130 "parentid", "lv", "created", "nt", "nvols");
3132 dumpPtr = dl.budb_dumpList_val;
3133 for (i = 1; i <= dl.budb_dumpList_len; i++) {
3134 compactDateString(&dumpPtr->created, ds, 50),
3135 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3136 dumpPtr->parent, dumpPtr->level, ds,
3137 dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3138 dumpPtr->nVolumes, dumpPtr->name);
3139 if (dumpPtr->initialDumpID) /* an appended dump */
3140 printf(" (%u)", dumpPtr->initialDumpID);
3141 else if (dumpPtr->appendedDumpID) /* has appended dumps */
3142 printf(" (%u)", dumpPtr->id);
3148 if (dl.budb_dumpList_val)
3149 free(dl.budb_dumpList_val);
3151 } while (nextindex != -1);
3157 * list the dumps and contens of the dumps.
3162 bc_dumpInfoCmd(as, arock)
3163 struct cmd_syndesc *as;
3167 afs_int32 detailFlag;
3171 afs_int32 dumpInfo();
3173 if (as->parms[0].items) {
3174 if (as->parms[1].items) {
3176 "These options are exclusive - select only one");
3179 ndumps = atoi(as->parms[0].items->data);
3181 com_err(whoami, 0, "Must provide a positive number");
3185 code = printRecentDumps(ndumps);
3186 } else if (as->parms[1].items) {
3187 detailFlag = (as->parms[2].items ? 1 : 0); /* 1 = detailed listing */
3188 dumpid = atoi(as->parms[1].items->data);
3189 code = dumpInfo(dumpid, detailFlag);
3191 code = printRecentDumps(10);