2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #if defined(AFS_LINUX24_ENV)
17 #define _REGEX_RE_COMP
19 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
29 #include <afs/com_err.h>
30 #include <afs/afsutil.h>
32 #include <afs/budb_prototypes.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>
46 #ifdef HAVE_POSIX_REGEX /* use POSIX regexp library */
50 #include "error_macros.h"
51 #include "bucoord_internal.h"
52 #include "bucoord_prototypes.h"
54 extern struct bc_config *bc_globalConfig;
55 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
56 extern struct ubik_client *cstruct;
60 extern afs_int32 lastTaskCode;
62 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
64 static int EvalVolumeSet1(struct bc_config *aconfig, struct bc_volumeSet *avs,
65 struct bc_volumeDump **avols,
66 struct ubik_client *uclient);
68 static int EvalVolumeSet2(struct bc_config *aconfig, struct bc_volumeSet *avs,
69 struct bc_volumeDump **avols,
70 struct ubik_client *uclient);
71 static int DBLookupByVolume(char *volumeName);
74 bc_EvalVolumeSet(struct bc_config *aconfig,
75 struct bc_volumeSet *avs,
76 struct bc_volumeDump **avols,
77 struct ubik_client *uclient)
78 { /*bc_EvalVolumeSet */
80 static afs_int32 use = 2;
82 if (use == 2) { /* Use EvalVolumeSet2() */
83 code = EvalVolumeSet2(aconfig, avs, avols, uclient);
84 if (code == RXGEN_OPCODE)
87 if (use == 1) { /* Use EvalVolumeSet1() */
88 code = EvalVolumeSet1(aconfig, avs, avols, uclient);
91 } /*bc_EvalVolumeSet */
93 struct partitionsort {
95 struct bc_volumeDump *vdlist;
96 struct bc_volumeDump *lastvdlist;
97 struct bc_volumeDump *dupvdlist;
98 struct bc_volumeEntry *vole;
99 struct partitionsort *next;
103 struct partitionsort *partitions;
104 struct serversort *next;
108 getSPEntries(afs_uint32 server, afs_int32 partition,
109 struct serversort **serverlist,
110 struct serversort **ss,
111 struct partitionsort **ps)
113 if (!(*ss) || ((*ss)->ipaddr != server)) {
115 for ((*ss) = *serverlist; (*ss); *ss = (*ss)->next) {
116 if ((*ss)->ipaddr == server)
120 /* No server entry added. Add one */
122 *ss = (struct serversort *)malloc(sizeof(struct serversort));
124 afs_com_err(whoami, BC_NOMEM, NULL);
128 memset(*ss, 0, sizeof(struct serversort));
129 (*ss)->ipaddr = server;
130 (*ss)->next = *serverlist;
135 if (!(*ps) || ((*ps)->part != partition)) {
136 for (*ps = (*ss)->partitions; *ps; *ps = (*ps)->next) {
137 if ((*ps)->part == partition)
141 /* No partition entry added. Add one */
143 *ps = (struct partitionsort *)malloc(sizeof(struct partitionsort));
145 afs_com_err(whoami, BC_NOMEM, NULL);
151 memset(*ps, 0, sizeof(struct partitionsort));
152 (*ps)->part = partition;
153 (*ps)->next = (*ss)->partitions;
154 (*ss)->partitions = *ps;
160 randSPEntries(struct serversort *serverlist,
161 struct bc_volumeDump **avols)
163 struct serversort *ss, **pss;
164 struct partitionsort *ps, **pps;
166 afs_int32 scount, pcount;
170 /* Seed random number generator */
171 r = time(0) + getpid();
174 /* Count number of servers, remove one at a time */
175 for (scount = 0, ss = serverlist; ss; ss = ss->next, scount++);
176 for (; scount; scount--) {
177 /* Pick a random server in list and remove it */
178 r = (rand() >> 4) % scount;
179 for (pss = &serverlist, ss = serverlist; r;
180 pss = &ss->next, ss = ss->next, r--);
183 /* Count number of partitions, remove one at a time */
184 for (pcount = 0, ps = ss->partitions; ps; ps = ps->next, pcount++);
185 for (; pcount; pcount--) {
186 /* Pick a random parition in list and remove it */
187 r = (rand() >> 4) % pcount;
188 for (pps = &ss->partitions, ps = ss->partitions; r;
189 pps = &ps->next, ps = ps->next, r--);
192 ps->lastvdlist->next = *avols;
202 EvalVolumeSet2(struct bc_config *aconfig,
203 struct bc_volumeSet *avs,
204 struct bc_volumeDump **avols,
205 struct ubik_client *uclient)
206 { /*EvalVolumeSet2 */
207 struct bc_volumeEntry *tve;
208 struct bc_volumeDump *tavols;
209 struct VldbListByAttributes attributes;
210 afs_int32 si, nsi; /* startIndex and nextStartIndex */
211 afs_int32 nentries, e, ei, et, add, l;
212 nbulkentries bulkentries;
213 struct nvldbentry *entries = 0;
214 struct bc_volumeDump *tvd;
215 afs_int32 code = 0, tcode;
217 struct serversort *servers = 0, *ss = 0;
218 struct partitionsort *ps = 0;
220 *avols = (struct bc_volumeDump *)0;
221 bulkentries.nbulkentries_len = 0;
222 bulkentries.nbulkentries_val = 0;
224 /* For each of the volume set entries - collect the volumes that match it */
225 for (tve = avs->ventries; tve; tve = tve->next) {
226 /* Put together a call to the vlserver for this vlentry. The
227 * performance gain is from letting the vlserver expand the
228 * volumeset and not this routine.
231 if (tve->server.sin_addr.s_addr) { /* The server */
232 attributes.Mask |= VLLIST_SERVER;
233 attributes.server = tve->server.sin_addr.s_addr;
235 if (tve->partition != -1) { /* The partition */
236 attributes.Mask |= VLLIST_PARTITION;
237 attributes.partition = tve->partition;
240 /* Now make the call to the vlserver */
241 for (si = 0; si != -1; si = nsi) {
243 bulkentries.nbulkentries_len = 0;
244 bulkentries.nbulkentries_val = 0;
247 ubik_Call(VL_ListAttributesN2, uclient, 0, &attributes,
248 tve->name, si, &nentries, &bulkentries, &nsi);
252 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
253 * it is not complete. The only way to tell if it is not complete
254 * is if et == 0 (which we check for below). Also, if the call didn't
255 * match any entries, then we don't know what version the vlserver
256 * is. In both cases, we return RXGEN_OPCODE and the calling routine
257 * will switch to the EvalVolumeSet1() call.
260 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
264 if (nentries > bulkentries.nbulkentries_len)
265 nentries = bulkentries.nbulkentries_len;
267 /* Step through each entry and add it to the list of volumes */
268 entries = bulkentries.nbulkentries_val;
269 for (e = 0; e < nentries; e++) {
270 ei = entries[e].matchindex & 0xffff;
271 et = (entries[e].matchindex >> 16) & 0xffff;
286 ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
289 /* Find server and partiton structure to hang the entry off of */
291 getSPEntries(entries[e].serverNumber[ei],
292 entries[e].serverPartition[ei], &servers,
295 afs_com_err(whoami, tcode, NULL);
299 /* Detect if this entry should be added (not a duplicate).
300 * Use ps->dupvdlist and ps->vole to only search volumes from
301 * previous volume set entries.
304 if (tve != avs->ventries) {
305 l = strlen(entries[e].name);
306 if (ps->vole != tve) {
308 ps->dupvdlist = ps->vdlist;
310 for (tavols = ps->dupvdlist; add && tavols;
311 tavols = tavols->next) {
312 if (strncmp(tavols->name, entries[e].name, l) == 0) {
313 if ((strcmp(&entries[e].name[l], ".backup") == 0)
314 || (strcmp(&entries[e].name[l], ".readonly")
316 || (strcmp(&entries[e].name[l], "") == 0))
323 /* Allocate a volume dump structure and its name */
324 tvd = (struct bc_volumeDump *)
325 malloc(sizeof(struct bc_volumeDump));
327 afs_com_err(whoami, BC_NOMEM, NULL);
330 memset(tvd, 0, sizeof(*tvd));
332 tvd->name = (char *)malloc(strlen(entries[e].name) + 10);
334 afs_com_err(whoami, BC_NOMEM, NULL);
339 /* Fill it in and thread onto avols list */
340 strcpy(tvd->name, entries[e].name);
342 strcat(tvd->name, ".backup");
343 else if (et == ROVOL)
344 strcat(tvd->name, ".readonly");
345 tvd->vid = entries[e].volumeId[et];
348 tvd->partition = entries[e].serverPartition[ei];
349 tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
350 tvd->server.sin_port = 0; /* default FS port */
351 tvd->server.sin_family = AF_INET;
352 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
353 tvd->server.sin_len = sizeof(struct sockaddr_in);
356 /* String tvd off of partition struct */
357 tvd->next = ps->vdlist;
360 ps->lastvdlist = tvd;
366 /* Free memory allocated during VL call */
367 if (bulkentries.nbulkentries_val) {
368 free((char *)bulkentries.nbulkentries_val);
369 bulkentries.nbulkentries_val = 0;
375 /* Randomly link the volumedump entries together */
376 randSPEntries(servers, avols);
377 fprintf(stderr, "Total number of volumes : %u\n", count);
380 if (bulkentries.nbulkentries_val) {
381 free((char *)bulkentries.nbulkentries_val);
384 } /*EvalVolumeSet2 */
386 /*-----------------------------------------------------------------------------
390 * Takes the entries in a volumeset and expands them into a list of
391 * volumes. Every VLDB volume entry is looked at and compared to the
394 * When matching a VLDB volume entry to a volumeset entry,
395 * 1. If the RW volume entry matches, that RW volume is used.
396 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
397 * 3. Finally, if the RO volume entry matches, the RO volume is used.
398 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
399 * "user.troy" and "user.troy.backup". The rules will use
400 * the RW volume "user.troy".
402 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
403 * that volume is used and matches against any remaining volumeset entries
405 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
406 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
407 * would have matched its RW volume "user.troy", but the first
408 * match is used and the second match isn't even done.
411 * aconfig : Global configuration info.
413 * avols : Ptr to linked list of entries describing volumes to dump.
414 * uclient : Ptr to Ubik client structure.
417 * 0 on successful volume set evaluation,
418 * Lower-level codes otherwise.
421 * Expand only the single volume set provided (avs); don't go down its chain.
425 *-----------------------------------------------------------------------------
428 EvalVolumeSet1(struct bc_config *aconfig,
429 struct bc_volumeSet *avs,
430 struct bc_volumeDump **avols,
431 struct ubik_client *uclient)
432 { /*EvalVolumeSet1 */
433 afs_int32 code; /*Result of various calls */
434 struct bc_volumeDump *tvd; /*Ptr to new dump instance */
435 struct bc_volumeEntry *tve, *ctve; /*Ptr to new volume entry instance */
436 char patt[256]; /*Composite regex; also, target string */
437 int volType = 0; /*Type of volume that worked */
438 afs_int32 index; /*Current VLDB entry index */
439 afs_int32 count; /*Needed by VL_ListEntry() */
440 afs_int32 next_index; /*Next index to list */
441 struct vldbentry entry; /*VLDB entry */
442 int srvpartpair; /*Loop counter: server/partition pair */
446 struct serversort *servers = 0, *ss = 0;
447 struct partitionsort *ps = 0;
448 #ifdef HAVE_POSIX_REGEX
450 int need_regfree = 0;
455 *avols = (struct bc_volumeDump *)0;
456 ctve = (struct bc_volumeEntry *)0; /* no compiled entry */
458 /* For each vldb entry.
459 * Variable next_index is set to the index of the next VLDB entry
460 * in the enumeration.
462 for (index = 0; 1; index = next_index) { /*w */
463 memset(&entry, 0, sizeof(entry));
464 code = ubik_Call(VL_ListEntry, /*Routine to invoke */
465 uclient, /*Ubik client structure */
467 index, /*Current index */
468 &count, /*Ptr to working variable */
469 &next_index, /*Ptr to next index value to list */
470 &entry); /*Ptr to entry to fill */
474 break; /* If the next index is invalid, bail out now. */
476 /* For each entry in the volume set */
477 found = 0; /* No match in volume set yet */
478 for (tve = avs->ventries; tve; tve = tve->next) { /*ve */
479 /* for each server in the vldb entry */
480 for (srvpartpair = 0; srvpartpair < entry.nServers; srvpartpair++) { /*s */
481 /* On the same server */
482 if (tve->server.sin_addr.s_addr
483 && !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
484 entry.serverNumber[srvpartpair],
492 /* On the same partition */
493 if ((tve->partition != -1)
494 && (tve->partition != entry.serverPartition[srvpartpair]))
497 /* If the volume entry is not compiled, then compile it */
499 sprintf(patt, "^%s$", tve->name);
500 #ifdef HAVE_POSIX_REGEX
501 if (regcomp(&re, patt, REG_NOSUB) != 0) {
502 afs_com_err(whoami, 0, "Can't compile regular expression '%s'", patt);
507 errm = (char *)re_comp(patt);
509 afs_com_err(whoami, 0,
510 "Can't compile regular expression '%s': %s",
518 /* If the RW name matches the volume set entry, take
519 * it and exit. First choice is to use the RW volume.
521 if (entry.serverFlags[srvpartpair] & ITSRWVOL) {
522 if (entry.flags & RW_EXISTS) {
523 sprintf(patt, "%s", entry.name);
524 #ifdef HAVE_POSIX_REGEX
525 code = regexec(&re, patt, 0, NULL, 0);
528 code = re_exec(patt);
532 foundentry = srvpartpair;
538 /* If the BK name matches the volume set entry, take
539 * it and exit. Second choice is to use the BK volume.
541 if (entry.flags & BACK_EXISTS) {
542 sprintf(patt, "%s.backup", entry.name);
543 #ifdef HAVE_POSIX_REGEX
544 code = regexec(&re, patt, 0, NULL, 0);
547 code = re_exec(patt);
551 foundentry = srvpartpair;
558 /* If the RO name matches the volume set entry, remember
559 * it, but continue searching. Further entries may be
560 * RW or backup entries that will match.
562 else if (!found && (entry.serverFlags[srvpartpair] & ITSROVOL)
563 && (entry.flags & RO_EXISTS)) {
564 sprintf(patt, "%s.readonly", entry.name);
565 #ifdef HAVE_POSIX_REGEX
566 code = regexec(&re, patt, 0, NULL, 0);
569 code = re_exec(patt);
573 foundentry = srvpartpair;
579 afs_com_err(whoami, 0, "Internal error in regex package");
582 /* If found a match, then create a new volume dump entry */
584 /* Find server and partition structure to hang the entry off of */
586 getSPEntries(entry.serverNumber[foundentry],
587 entry.serverPartition[foundentry], &servers,
590 afs_com_err(whoami, code, NULL);
595 tvd = (struct bc_volumeDump *)
596 malloc(sizeof(struct bc_volumeDump));
598 afs_com_err(whoami, BC_NOMEM, NULL);
601 memset(tvd, 0, sizeof(*tvd));
603 tvd->name = (char *)malloc(strlen(entry.name) + 10);
605 afs_com_err(whoami, BC_NOMEM, NULL);
610 strcpy(tvd->name, entry.name);
611 if (volType == BACKVOL)
612 strcat(tvd->name, ".backup");
613 else if (volType == ROVOL)
614 strcat(tvd->name, ".readonly");
615 tvd->vid = entry.volumeId[volType];
617 tvd->volType = volType;
618 tvd->partition = entry.serverPartition[foundentry];
619 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
620 tvd->server.sin_port = 0; /* default FS port */
621 tvd->server.sin_family = AF_INET;
623 /* String tvd off of partition struct */
624 tvd->next = ps->vdlist;
627 ps->lastvdlist = tvd;
633 #ifdef HAVE_POSIX_REGEX
638 /* Randomly link the volumedump entries together */
639 randSPEntries(servers, avols);
641 fprintf(stderr, "Total number of volumes : %u\n", total);
643 } /*EvalVolumeSet1 */
646 compactTimeString(time_t *date, char *string, afs_int32 size)
653 if (*date == NEVERDATE) {
654 sprintf(string, "NEVER");
656 ltime = localtime(date);
657 strftime(string, size, "%m/%d/%Y %H:%M", ltime);
663 * print out a date in compact format, 16 chars, format is
666 * date_long - ptr to a long containing the time
668 * ptr to a string containing a representation of the date
671 compactDateString(afs_uint32 *date_long, char *string, afs_int32 size)
673 time_t t = *date_long;
674 return compactTimeString(&t, string, size);
679 bc_SafeATOI(char *anum)
683 for (; *anum; anum++) {
684 if ((*anum < '0') || (*anum > '9'))
686 total = (10 * total) + (afs_int32) (*anum - '0');
692 * Take a string and parse it for a number (could be float) followed
693 * by a character representing the units (K,M,G,T). Default is 'K'.
694 * Return the size in KBytes.
697 bc_FloatATOI(char *anum)
701 afs_int32 fraction = 0; /* > 0 if past the decimal */
703 for (; *anum; anum++) {
704 if ((*anum == 't') || (*anum == 'T')) {
705 total *= 1024 * 1024 * 1024;
708 if ((*anum == 'g') || (*anum == 'G')) {
709 total *= 1024 * 1024;
712 if ((*anum == 'm') || (*anum == 'M')) {
716 if ((*anum == 'k') || (*anum == 'K')) {
723 if ((*anum < '0') || (*anum > '9'))
727 total = (10. * total) + (float)(*anum - '0');
729 total += ((float)(*anum - '0')) / (float)fraction;
734 total += 0.5; /* Round up */
735 if (total > 0x7fffffff) /* Don't go over 2G */
737 rtotal = (afs_int32) total;
741 /* make a copy of a string so that it can be freed later */
743 bc_CopyString(char *astring)
749 return (NULL); /* propagate null strings easily */
750 tlen = strlen(astring);
751 tp = (char *)malloc(tlen + 1); /* don't forget the terminating null */
753 afs_com_err(whoami, BC_NOMEM, NULL);
762 * Concatenates the parameters of an option and returns the string.
767 concatParams(struct cmd_item *itemPtr)
769 struct cmd_item *tempPtr;
770 afs_int32 length = 0;
773 /* compute the length of string required */
774 for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next) {
775 length += strlen(tempPtr->data);
776 length++; /* space or null terminator */
779 if (length == 0) { /* no string (0 length) */
780 afs_com_err(whoami, 0, "Can't have zero length date and time string");
784 string = (char *)malloc(length); /* allocate the string */
786 afs_com_err(whoami, BC_NOMEM, NULL);
791 tempPtr = itemPtr; /* now assemble the string */
793 strcat(string, tempPtr->data);
794 tempPtr = tempPtr->next;
799 return (string); /* return the string */
803 * print out an interface status node as received from butc
807 printIfStatus(struct tciStatusS *statusPtr)
809 printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
810 if (statusPtr->nKBytes)
811 printf("%ld Kbytes transferred", (long unsigned int) statusPtr->nKBytes);
812 if (strlen(statusPtr->volumeName) != 0) {
813 if (statusPtr->nKBytes)
815 printf("volume %s", statusPtr->volumeName);
820 if (statusPtr->flags & ABORT_REQUEST)
821 printf(" [abort request rcvd]");
823 if (statusPtr->flags & ABORT_DONE)
824 printf(" [abort complete]");
826 if (statusPtr->flags & OPR_WAIT)
827 printf(" [operator wait]");
829 if (statusPtr->flags & CALL_WAIT)
830 printf(" [callout in progress]");
832 if (statusPtr->flags & DRIVE_WAIT)
833 printf(" [drive wait]");
835 if (statusPtr->flags & TASK_DONE)
841 getPortOffset(char *port)
843 afs_int32 portOffset;
845 portOffset = bc_SafeATOI(port);
847 if (portOffset < 0) {
848 afs_com_err(whoami, 0, "Can't decode port offset '%s'", port);
850 } else if (portOffset > BC_MAXPORTOFFSET) {
851 afs_com_err(whoami, 0, "%u exceeds max port offset %u", portOffset,
858 /* bc_GetTapeStatusCmd
859 * display status of all tasks on a particular tape coordinator
862 bc_GetTapeStatusCmd(struct cmd_syndesc *as, void *arock)
865 struct rx_connection *tconn;
866 afs_int32 portOffset = 0;
871 struct tciStatusS status;
873 code = bc_UpdateHosts();
875 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
879 if (as->parms[0].items) {
880 portOffset = getPortOffset(as->parms[0].items->data);
885 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
889 flags = TSK_STAT_FIRST;
893 while ((flags & TSK_STAT_END) == 0) {
894 code = TC_ScanStatus(tconn, &taskId, &status, &flags);
896 if (code == TC_NOTASKS)
898 afs_com_err(whoami, code, "; Can't get status from butc");
901 if ((flags & TSK_STAT_NOTFOUND))
902 break; /* Can't find the task id */
903 flags &= ~TSK_STAT_FIRST; /* turn off flag */
905 printIfStatus(&status);
910 printf("Tape coordinator is idle\n");
912 if (flags & TSK_STAT_ADSM)
913 printf("TSM Tape coordinator\n");
914 else if (flags & TSK_STAT_XBSA)
915 printf("XBSA Tape coordinator\n");
920 extern struct Lock dispatchLock;
923 * wait for all jobs to terminate
926 bc_WaitForNoJobs(void)
929 int usefulJobRunning = 1;
931 extern dlqlinkT statusHead;
933 afs_com_err(whoami, 0, "waiting for job termination");
935 while (usefulJobRunning) {
936 usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
937 if (dispatchLock.excl_locked)
938 usefulJobRunning = 1;
939 for (i = 0; (!usefulJobRunning && (i < BC_MAXSIMDUMPS)); i++) {
940 if (bc_dumpTasks[i].flags & BC_DI_INUSE)
941 usefulJobRunning = 1;
944 /* Wait 5 seconds and check again */
945 if (usefulJobRunning)
948 return (lastTaskCode);
952 * print status on running jobs
954 * ignored - a null "as" prints only jobs.
957 bc_JobsCmd(struct cmd_syndesc *as, void *arock)
966 extern dlqlinkT statusHead;
968 dlqInit(&atJobsHead);
971 ptr = (&statusHead)->dlq_next;
972 while (ptr != &statusHead) {
973 statusPtr = (statusP) ptr;
976 if (statusPtr->scheduledDump) {
977 dlqUnlink((dlqlinkP) statusPtr);
978 dlqLinkb(&atJobsHead, (dlqlinkP) statusPtr);
980 printf("Job %d:", statusPtr->jobNumber);
982 printf(" %s", statusPtr->taskName);
984 if (statusPtr->dbDumpId)
985 printf(": DumpID %u", statusPtr->dbDumpId);
986 if (statusPtr->nKBytes)
987 printf(", %ld Kbytes", afs_printable_int32_ld(statusPtr->nKBytes));
988 if (strlen(statusPtr->volumeName) != 0)
989 printf(", volume %s", statusPtr->volumeName);
991 if (statusPtr->flags & CONTACT_LOST)
992 printf(" [butc contact lost]");
994 if (statusPtr->flags & ABORT_REQUEST)
995 printf(" [abort request]");
997 if (statusPtr->flags & ABORT_SENT)
998 printf(" [abort sent]");
1000 if (statusPtr->flags & OPR_WAIT)
1001 printf(" [operator wait]");
1003 if (statusPtr->flags & CALL_WAIT)
1004 printf(" [callout in progress]");
1006 if (statusPtr->flags & DRIVE_WAIT)
1007 printf(" [drive wait]");
1013 * Now print the scheduled dumps.
1015 if (!dlqEmpty(&statusHead) && as)
1016 printf("\n"); /* blank line between running and scheduled dumps */
1019 while (!dlqEmpty(&atJobsHead)) {
1020 ptr = (&atJobsHead)->dlq_next;
1021 youngest = (statusP) ptr;
1023 ptr = ptr->dlq_next;
1024 while (ptr != &atJobsHead) { /* Find the dump that starts the earliest */
1025 statusPtr = (statusP) ptr;
1026 if (statusPtr->scheduledDump < youngest->scheduledDump)
1027 youngest = statusPtr;
1028 ptr = ptr->dlq_next;
1031 /* Print token expiration time */
1032 if ((tokenExpires > prevTime)
1033 && (tokenExpires <= youngest->scheduledDump) && as
1034 && (tokenExpires != NEVERDATE)) {
1035 if (tokenExpires > time(0)) {
1036 compactTimeString(&tokenExpires, ds, 50);
1037 printf(" %16s: TOKEN EXPIRATION\n", ds);
1039 printf(" TOKEN HAS EXPIRED\n");
1042 prevTime = youngest->scheduledDump;
1044 /* Print the info */
1045 compactDateString(&youngest->scheduledDump, ds, 50);
1046 printf("Job %d:", youngest->jobNumber);
1047 printf(" %16s: %s", ds, youngest->cmdLine);
1050 /* return to original list */
1051 dlqUnlink((dlqlinkP) youngest);
1052 dlqLinkb(&statusHead, (dlqlinkP) youngest);
1055 /* Print token expiration time if havn't already */
1056 if ((tokenExpires == NEVERDATE) && as)
1057 printf(" : TOKEN NEVER EXPIRES\n");
1058 else if ((tokenExpires > prevTime) && as) {
1059 if (tokenExpires > time(0)) {
1060 compactTimeString(&tokenExpires, ds, 50);
1061 printf(" %16s: TOKEN EXPIRATION\n", ds);
1063 printf(" : TOKEN HAS EXPIRED\n");
1072 bc_KillCmd(struct cmd_syndesc *as, void *arock)
1076 struct bc_dumpTask *td;
1083 extern dlqlinkT statusHead;
1085 tp = as->parms[0].items->data;
1086 if (strchr(tp, '.') == 0) {
1087 slot = bc_SafeATOI(tp);
1089 afs_com_err(whoami, 0, "Bad syntax for number '%s'", tp);
1094 ptr = (&statusHead)->dlq_next;
1095 while (ptr != &statusHead) {
1096 statusPtr = (statusP) ptr;
1097 if (statusPtr->jobNumber == slot) {
1098 statusPtr->flags |= ABORT_REQUEST;
1102 ptr = ptr->dlq_next;
1106 fprintf(stderr, "Job %d not found\n", slot);
1111 for (i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
1112 if (td->flags & BC_DI_INUSE) {
1114 strcpy(tbuffer, td->volSetName);
1115 strcat(tbuffer, ".");
1116 strcat(tbuffer, tailCompPtr(td->dumpName));
1117 if (strcmp(tbuffer, tp) == 0)
1121 if (i >= BC_MAXSIMDUMPS) {
1122 afs_com_err(whoami, 0, "Can't find job %s", tp);
1127 statusPtr = findStatus(td->dumpID);
1129 if (statusPtr == 0) {
1130 afs_com_err(whoami, 0, "Can't locate status - internal error");
1134 statusPtr->flags |= ABORT_REQUEST;
1140 /* restore a volume or volumes */
1142 bc_VolRestoreCmd(struct cmd_syndesc *as, void *arock)
1145 * parm 0 is the new server to restore to
1146 * parm 1 is the new partition to restore to
1147 * parm 2 is volume(s) to restore
1148 * parm 3 is the new extension, if any, for the volume name.
1149 * parm 4 gives the new volume # to restore this volume as (removed).
1150 * parm 4 date is a string representing the date
1152 * We handle four types of restores. If old is set, then we restore the
1153 * volume with the same name and ID. If old is not set, we allocate
1154 * a new volume ID for the restored volume. If a new extension is specified,
1155 * we add that extension to the volume name of the restored volume.
1157 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1158 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1159 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1160 struct bc_volumeDump *tvol; /* temp for same */
1161 struct sockaddr_in destServ; /* machine to which to restore volumes */
1162 afs_int32 destPartition; /* partition to which to restore volumes */
1164 struct cmd_item *ti;
1168 afs_int32 dumpID = 0;
1169 char *newExt, *timeString;
1171 afs_int32 *ports = NULL;
1172 afs_int32 portCount = 0;
1175 code = bc_UpdateHosts();
1177 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1181 /* specified other destination host */
1182 if (as->parms[0].items) {
1183 tp = as->parms[0].items->data;
1184 if (bc_ParseHost(tp, &destServ)) {
1185 afs_com_err(whoami, 0, "Failed to locate destination host '%s'", tp);
1190 /* specified other destination partition */
1191 if (as->parms[1].items) {
1192 tp = as->parms[1].items->data;
1193 if (bc_GetPartitionID(tp, &destPartition)) {
1194 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1199 for (ti = as->parms[2].items; ti; ti = ti->next) {
1200 /* build list of volume items */
1201 tvol = (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1203 afs_com_err(whoami, BC_NOMEM, NULL);
1206 memset(tvol, 0, sizeof(struct bc_volumeDump));
1208 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1210 afs_com_err(whoami, BC_NOMEM, NULL);
1213 strncpy(tvol->name, ti->data, VOLSER_OLDMAXVOLNAME);
1214 tvol->entry = &tvolumeEntry;
1217 lastVol->next = tvol; /* thread onto end of list */
1219 volsToRestore = tvol;
1223 if (as->parms[4].items) {
1224 timeString = concatParams(as->parms[4].items);
1228 code = ktime_DateToLong(timeString, &fromDate);
1231 afs_com_err(whoami, 0, "Can't parse restore date and time");
1232 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1236 fromDate = 0x7fffffff; /* latest one */
1239 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1242 /* Read all the port offsets into the ports array. The first element in the
1243 * array is for full restore and the rest are for incremental restores
1245 if (as->parms[5].items) {
1246 for (ti = as->parms[5].items; ti; ti = ti->next)
1248 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1250 afs_com_err(whoami, BC_NOMEM, NULL);
1254 for (ti = as->parms[5].items, i = 0; ti; ti = ti->next, i++) {
1255 ports[i] = getPortOffset(ti->data);
1261 dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1263 if (as->parms[7].items)
1265 dumpID = atoi(as->parms[7].items->data);
1271 * Perform the call to start the restore.
1274 bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore,
1275 &destServ, destPartition, fromDate, newExt, oldFlag,
1276 /*parentDump */ dumpID, /*dumpLevel */ 0,
1277 bc_Restorer, ports, portCount,
1278 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1280 afs_com_err(whoami, code, "; Failed to queue restore");
1285 /* restore a whole partition or server */
1287 /* bc_DiskRestoreCmd
1288 * restore a whole partition
1290 * first, reqd - machine (server) to restore
1291 * second, reqd - partition to restore
1296 bc_DiskRestoreCmd(struct cmd_syndesc *as, void *arock)
1298 struct bc_volumeSet tvolumeSet; /* temporary volume set for EvalVolumeSet call */
1299 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1300 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1301 struct sockaddr_in destServ; /* machine to which to restore volumes */
1302 afs_int32 destPartition; /* partition to which to restore volumes */
1308 afs_int32 *ports = NULL;
1309 afs_int32 portCount = 0;
1311 struct bc_volumeDump *prev, *tvol, *nextvol;
1312 struct cmd_item *ti;
1315 /* parm 0 is the server to restore
1316 * parm 1 is the partition to restore
1318 * parm 8 and above as in VolRestoreCmd:
1319 * parm 8 is the new server to restore to
1320 * parm 9 is the new partition to restore to
1323 code = bc_UpdateVolumeSet();
1325 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1328 code = bc_UpdateHosts();
1330 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1334 /* create a volume set corresponding to the volume pattern we've been given */
1335 memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1336 memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1337 tvolumeSet.name = "TempVolumeSet";
1338 tvolumeSet.ventries = &tvolumeEntry;
1339 tvolumeEntry.serverName = as->parms[0].items->data;
1340 tvolumeEntry.partname = as->parms[1].items->data;
1342 if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition)) {
1343 afs_com_err(whoami, 0, "Can't parse partition '%s'",
1344 tvolumeEntry.partname);
1348 if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server)) {
1349 afs_com_err(whoami, 0, "Can't locate host '%s'", tvolumeEntry.serverName);
1353 /* specified other destination host */
1354 if (as->parms[8].items) {
1355 tp = as->parms[8].items->data;
1356 if (bc_ParseHost(tp, &destServ)) {
1357 afs_com_err(whoami, 0, "Can't locate destination host '%s'", tp);
1360 } else /* use destination host == original host */
1361 memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1363 /* specified other destination partition */
1364 if (as->parms[9].items) {
1365 tp = as->parms[9].items->data;
1366 if (bc_GetPartitionID(tp, &destPartition)) {
1367 afs_com_err(whoami, 0, "Can't parse destination partition '%s'", tp);
1370 } else /* use original partition */
1371 destPartition = tvolumeEntry.partition;
1373 tvolumeEntry.name = ".*"; /* match all volumes (this should be a parameter) */
1375 if (as->parms[2].items) {
1376 for (ti = as->parms[2].items; ti; ti = ti->next)
1378 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1380 afs_com_err(whoami, BC_NOMEM, NULL);
1384 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1385 ports[i] = getPortOffset(ti->data);
1391 newExt = (as->parms[10].items ? as->parms[10].items->data : NULL);
1392 dontExecute = (as->parms[11].items ? 1 : 0); /* -n */
1395 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1398 bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore,
1401 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1405 /* Since we want only RW volumes, remove any
1406 * BK or RO volumes from the list.
1408 for (prev = 0, tvol = volsToRestore; tvol; tvol = nextvol) {
1409 nextvol = tvol->next;
1410 if (BackupName(tvol->name)) {
1413 "Will not restore volume %s (its RW does not reside on the partition)\n",
1416 if (tvol == volsToRestore) {
1417 volsToRestore = nextvol;
1419 prev->next = nextvol;
1429 fromDate = 0x7fffffff; /* last one before this date */
1430 oldFlag = 1; /* do restore same volid (and name) */
1433 * Perform the call to start the dump.
1436 bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1437 &destServ, destPartition, fromDate, newExt, oldFlag,
1438 /*parentDump */ 0, /*dumpLevel */ 0,
1439 bc_Restorer, ports, portCount,
1440 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1442 afs_com_err(whoami, code, "; Failed to queue restore");
1447 /* bc_VolsetRestoreCmd
1448 * restore a volumeset or list of volumes.
1452 bc_VolsetRestoreCmd(struct cmd_syndesc *as, void *arock)
1459 afs_int32 *ports = NULL;
1460 afs_int32 portCount = 0;
1463 struct bc_volumeSet *volsetPtr; /* Ptr to list of generated volume info */
1464 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1465 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1466 struct sockaddr_in destServer; /* machine to which to restore volume */
1467 afs_int32 destPartition; /* partition to which to restore volumes */
1468 struct cmd_item *ti;
1471 code = bc_UpdateVolumeSet();
1473 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1476 code = bc_UpdateHosts();
1478 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1482 if (as->parms[0].items) {
1483 if (as->parms[1].items) {
1484 afs_com_err(whoami, 0, "Can't have both -name and -file options");
1488 volsetName = as->parms[0].items->data;
1489 volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1491 afs_com_err(whoami, 0,
1492 "Can't find volume set '%s' in backup database",
1497 /* Expand out the volume set into its component list of volumes. */
1499 bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore,
1502 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1505 } else if (as->parms[1].items) {
1508 char server[50], partition[50], volume[50], rest[256];
1510 struct bc_volumeDump *tvol;
1512 fd = fopen(as->parms[1].items->data, "r");
1514 afs_com_err(whoami, errno, "; Cannot open file '%s'",
1515 as->parms[1].items->data);
1519 while (fgets(line, 255, fd)) {
1521 sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1527 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1528 fprintf(stderr, " %s", line);
1532 if (bc_ParseHost(server, &destServer)) {
1533 afs_com_err(whoami, 0, "Failed to locate host '%s'", server);
1537 if (bc_GetPartitionID(partition, &destPartition)) {
1538 afs_com_err(whoami, 0,
1539 "Failed to parse destination partition '%s'",
1544 /* Allocate a volumeDump structure and link it in */
1546 (struct bc_volumeDump *)malloc(sizeof(struct bc_volumeDump));
1547 memset(tvol, 0, sizeof(struct bc_volumeDump));
1549 tvol->name = (char *)malloc(VOLSER_MAXVOLNAME + 1);
1551 afs_com_err(whoami, BC_NOMEM, NULL);
1554 strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1555 memcpy(&tvol->server, &destServer, sizeof(destServer));
1556 tvol->partition = destPartition;
1559 lastVol->next = tvol; /* thread onto end of list */
1561 volsToRestore = tvol;
1566 afs_com_err(whoami, 0, "-name or -file option required");
1571 /* Get the port offset for the restore */
1572 if (as->parms[2].items) {
1573 for (ti = as->parms[2].items; ti; ti = ti->next)
1575 ports = (afs_int32 *) malloc(portCount * sizeof(afs_int32));
1577 afs_com_err(whoami, BC_NOMEM, NULL);
1581 for (ti = as->parms[2].items, i = 0; ti; ti = ti->next, i++) {
1582 ports[i] = getPortOffset(ti->data);
1588 newExt = (as->parms[3].items ? as->parms[3].items->data : NULL);
1589 dontExecute = (as->parms[4].items ? 1 : 0);
1591 fromDate = 0x7fffffff; /* last one before this date */
1592 oldFlag = 1; /* do restore same volid (and name) */
1594 /* Perform the call to start the restore */
1595 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1596 /*destserver */ NULL, /*destpartition */ 0, fromDate,
1598 /*parentDump */ 0, /*dumpLevel */ 0,
1599 bc_Restorer, ports, portCount,
1600 /*dumpSched */ NULL, /*append */ 0, dontExecute);
1602 afs_com_err(whoami, code, "; Failed to queue restore");
1607 /*-----------------------------------------------------------------------------
1611 * Perform a dump of the set of volumes provided.
1612 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1615 * as : Parsed command line information.
1616 * arock : Ptr to misc stuff; not used.
1620 * The result of bc_StartDump() otherwise
1627 *---------------------------------------------------------------------------
1632 bc_DumpCmd(struct cmd_syndesc *as, void *arock)
1634 char *dumpPath = NULL;
1635 char *vsName = NULL; /*Ptrs to various names */
1636 struct bc_volumeSet *tvs = NULL; /*Ptr to list of generated volume info */
1637 struct bc_dumpSchedule *tds;
1638 struct bc_dumpSchedule *baseds = NULL; /*Ptr to dump schedule node */
1639 struct bc_volumeDump *tve, *volsToDump; /*Ptr to individual vols to be dumped */
1640 struct budb_dumpEntry dumpEntry, de, fde; /* dump entry */
1643 afs_int32 parent; /* parent dump */
1644 afs_int32 level; /* this dump's level # */
1645 afs_int32 problemFindingDump; /* can't find parent(s) */
1647 afs_int32 *portp = NULL;
1648 afs_int32 doAt, atTime; /* Time a timed-dump is to start at */
1651 int doAppend = 0; /* Append the dump to dump set */
1652 afs_int32 code; /* Return code */
1653 int loadfile; /* whether to load a file or not */
1657 extern struct bc_dumpTask bc_dumpTasks[];
1659 code = bc_UpdateDumpSchedule();
1661 afs_com_err(whoami, code, "; Can't retrieve dump schedule");
1664 code = bc_UpdateVolumeSet();
1666 afs_com_err(whoami, code, "; Can't retrieve volume sets");
1669 code = bc_UpdateHosts();
1671 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
1676 * Some parameters cannot be specified together
1677 * The "-file" option cannot exist with the "-volume", "-dump",
1678 * "-portoffset", or "-append" option
1680 if (as->parms[6].items) {
1682 if (as->parms[0].items || as->parms[1].items || as->parms[2].items
1683 || as->parms[4].items) {
1684 afs_com_err(whoami, 0, "Invalid option specified with -file option");
1689 if (!as->parms[0].items || !as->parms[1].items) {
1690 afs_com_err(whoami, 0,
1691 "Must specify volume set name and dump level name");
1697 * Get the time we are to perform this dump
1699 if (as->parms[3].items) {
1702 timeString = concatParams(as->parms[3].items);
1707 * Now parse this string for the time to start.
1709 code = ktime_DateToLong(timeString, &atTime);
1712 afs_com_err(whoami, 0, "Can't parse dump start date and time");
1713 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
1719 dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1722 * If this dump is not a load file, then check the parameters.
1724 if (!loadfile) { /*6 */
1725 vsName = as->parms[0].items->data; /* get volume set name */
1726 dumpPath = as->parms[1].items->data; /* get dump path */
1728 /* get the port number, if one was specified */
1729 if (as->parms[2].items) {
1730 portp = (afs_int32 *) malloc(sizeof(afs_int32));
1732 afs_com_err(whoami, BC_NOMEM, NULL);
1736 *portp = getPortOffset(as->parms[2].items->data);
1741 doAppend = (as->parms[4].items ? 1 : 0); /* -append */
1744 * Get a hold of the given volume set and dump set.
1746 tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1748 afs_com_err(whoami, 0,
1749 "Can't find volume set '%s' in backup database", vsName);
1752 baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1754 afs_com_err(whoami, 0,
1755 "Can't find dump schedule '%s' in backup database",
1764 * If given the "-at" option, then add this to the jobs list and return
1767 * Create a status node for this timed dump.
1768 * Fill in the time to dump and the cmd line for the dump leaving off
1769 * the -at option. If the -n option is there, it is scheduled with
1770 * the Timed dump as opposed to not scheduling the time dump at all.
1773 if (atTime < time(0)) {
1774 afs_com_err(whoami, 0,
1775 "Time of dump is earlier then current time - not added");
1777 statusPtr = createStatusNode();
1779 statusPtr->scheduledDump = atTime;
1781 /* Determine length of the dump command */
1783 length += 4; /* "dump" */
1785 length += 6; /* " -file" */
1786 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1788 /* length += 11; *//* " -volumeset" */
1789 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1791 /* length += 6; *//* " -dump" */
1792 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1794 if (as->parms[2].items) {
1795 /* length += 12; *//* " -portoffset" */
1796 length += 1 + strlen(as->parms[2].items->data); /* " <port>" */
1799 if (as->parms[4].items)
1800 length += 8; /* " -append" */
1804 length += 3; /* " -n" */
1805 length++; /* end-of-line */
1807 /* Allocate status block for this timed dump */
1808 sprintf(statusPtr->taskName, "Scheduled Dump");
1809 statusPtr->jobNumber = bc_jobNumber();
1810 statusPtr->scheduledDump = atTime;
1811 statusPtr->cmdLine = (char *)malloc(length);
1812 if (!statusPtr->cmdLine) {
1813 afs_com_err(whoami, BC_NOMEM, NULL);
1817 /* Now reconstruct the dump command */
1818 statusPtr->cmdLine[0] = 0;
1819 strcat(statusPtr->cmdLine, "dump");
1821 strcat(statusPtr->cmdLine, " -file");
1822 strcat(statusPtr->cmdLine, " ");
1823 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1825 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1826 strcat(statusPtr->cmdLine, " ");
1827 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1829 /* strcat(statusPtr->cmdLine, " -dump"); */
1830 strcat(statusPtr->cmdLine, " ");
1831 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1833 if (as->parms[2].items) {
1834 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1835 strcat(statusPtr->cmdLine, " ");
1836 strcat(statusPtr->cmdLine, as->parms[2].items->data);
1839 if (as->parms[4].items)
1840 strcat(statusPtr->cmdLine, " -append");
1843 strcat(statusPtr->cmdLine, " -n");
1845 printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1846 if ((atTime > tokenExpires) && (tokenExpires != NEVERDATE))
1847 afs_com_err(whoami, 0,
1848 "Warning: job %d starts after expiration of AFS token",
1849 statusPtr->jobNumber);
1858 * Read and execute the load file if specified. The work of reading is done
1859 * in the main routine prior the dispatch call. loadFile and dontExecute are
1860 * global variables so this can take place in main.
1863 loadFile = (char *)malloc(strlen(as->parms[6].items->data) + 1);
1865 afs_com_err(whoami, BC_NOMEM, NULL);
1868 strcpy(loadFile, as->parms[6].items->data);
1873 * We are doing a real dump (no load file or timed dump).
1875 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName,
1878 /* For each dump-level above this one, see if the volumeset was dumped
1879 * at the level. We search all dump-levels since a higher dump-level
1880 * may have actually been done AFTER a lower dump level.
1882 parent = level = problemFindingDump = 0;
1883 for (tds = baseds->parent; tds; tds = tds->parent) {
1884 /* Find the most recent dump of the volume-set at this dump-level.
1885 * Raise problem flag if didn't find a dump and a parent not yet found.
1887 code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1890 problemFindingDump = 1; /* skipping a dump level */
1894 /* We found the most recent dump at this level. Now check
1895 * if we should use it by seeing if its full dump hierarchy
1896 * exists. If it doesn't, we don't want to base our incremental
1899 if (!parent || (dumpEntry.id > parent)) {
1900 /* Follow the parent dumps to see if they are all there */
1901 for (d = dumpEntry.parent; d; d = de.parent) {
1902 code = bcdb_FindDumpByID(d, &de);
1907 /* If we found the entire level, remember it. Otherwise raise flag.
1908 * If we had already found a dump, raise the problem flag.
1912 problemFindingDump = 1;
1913 parent = dumpEntry.id;
1914 level = dumpEntry.level + 1;
1915 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1917 /* Dump hierarchy not complete so can't base off the latest */
1918 problemFindingDump = 1;
1923 /* If the problemflag was raise, it means we are not doing the
1924 * dump at the level we requested it be done at.
1926 if (problemFindingDump) {
1927 afs_com_err(whoami, 0,
1928 "Warning: Doing level %d dump due to missing higher-level dumps",
1931 printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1933 } else if (parent) {
1934 printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1937 /* Expand out the volume set into its component list of volumes. */
1938 code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1940 afs_com_err(whoami, code, "; Failed to evaluate volume set");
1944 printf("No volumes to dump\n");
1948 /* Determine what the clone time of the volume was when it was
1949 * last dumped (tve->date). This is the time from when an
1950 * incremental should be done (remains zero if a full dump).
1953 for (tve = volsToDump; tve; tve = tve->next) {
1954 code = bcdb_FindClone(parent, tve->name, &tve->date);
1958 /* Get the time the volume was last cloned and see if the volume has
1959 * changed since then. Only do this when the "-n" flag is specified
1960 * because butc will get the cloneDate at time of dump.
1964 volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1965 tve->vid, tve->volType, &tve->cloneDate);
1969 if (tve->cloneDate && (tve->cloneDate == tve->date)) {
1970 afs_com_err(whoami, 0,
1971 "Warning: Timestamp on volume %s unchanged from previous dump",
1979 printf("Would have dumped the following volumes:\n");
1981 printf("Preparing to dump the following volumes:\n");
1982 for (tve = volsToDump; tve; tve = tve->next) {
1983 printf("\t%s (%u)\n", tve->name, tve->vid);
1988 code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1989 /*destServer */ NULL, /*destPartition */ 0,
1991 /*newExt */ NULL, /*oldFlag */ 0,
1992 parent, level, bc_Dumper, portp, /*portCount */ 1,
1993 baseds, doAppend, dontExecute);
1995 afs_com_err(whoami, code, "; Failed to queue dump");
2002 * terminate the backup process. Insists that that all running backup
2003 * jobs be terminated before it will quit
2008 bc_QuitCmd(struct cmd_syndesc *as, void *arock)
2011 struct bc_dumpTask *td;
2012 extern dlqlinkT statusHead;
2016 /* Check the status list for outstanding jobs */
2018 for (ptr = (&statusHead)->dlq_next; ptr != &statusHead;
2019 ptr = ptr->dlq_next) {
2020 statusPtr = (statusP) ptr;
2021 if (!(statusPtr->flags & ABORT_REQUEST)) {
2023 afs_com_err(whoami, 0, "Job %d still running (and not aborted)",
2024 statusPtr->jobNumber);
2025 afs_com_err(whoami, 0,
2026 "You must at least 'kill' all running jobs before quitting");
2032 /* A job still being initialized (but no status structure or job number since it
2033 * has not been handed to a butc process yet)
2035 for (td = bc_dumpTasks, i = 0; i < BC_MAXSIMDUMPS; i++, td++) {
2036 if (td->flags & BC_DI_INUSE) {
2037 afs_com_err(whoami, 0, "A job is still running");
2038 afs_com_err(whoami, 0,
2039 "You must at least 'kill' all running jobs before quitting");
2045 /* close the all temp text files before quitting */
2046 for (i = 0; i < TB_NUM; i++)
2047 bc_closeTextFile(&bc_globalConfig->configText[i],
2048 &bc_globalConfig->tmpTextFileNames[i][0]);
2054 * Labels a tape i.e. request the tape coordinator to perform this
2058 bc_LabelTapeCmd(struct cmd_syndesc *as, void *arock)
2060 char *tapename = 0, *pname = 0;
2065 code = bc_UpdateHosts();
2067 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2071 if (as->parms[0].items) { /* -name */
2072 tapename = as->parms[0].items->data;
2073 if (strlen(tapename) >= TC_MAXTAPELEN) {
2074 afs_com_err(whoami, 0, "AFS tape name '%s' is too long", tapename);
2079 if (as->parms[3].items) { /* -pname */
2081 afs_com_err(whoami, 0, "Can only specify -name or -pname");
2084 pname = as->parms[3].items->data;
2085 if (strlen(pname) >= TC_MAXTAPELEN) {
2086 afs_com_err(whoami, 0, "Permanent tape name '%s' is too long", pname);
2091 if (as->parms[1].items) {
2092 size = bc_FloatATOI(as->parms[1].items->data);
2094 afs_com_err(whoami, 0, "Bad syntax for tape size '%s'",
2095 as->parms[1].items->data);
2101 if (as->parms[2].items) {
2102 port = getPortOffset(as->parms[2].items->data);
2107 code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2114 * read the label on a tape
2116 * optional port number
2119 bc_ReadLabelCmd(struct cmd_syndesc *as, void *arock)
2124 code = bc_UpdateHosts();
2126 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2130 if (as->parms[0].items) {
2131 port = getPortOffset(as->parms[0].items->data);
2136 code = bc_ReadLabel(bc_globalConfig, port);
2143 * read content information from dump tapes, and if user desires,
2144 * add it to the database
2147 bc_ScanDumpsCmd(struct cmd_syndesc *as, void *arock)
2150 afs_int32 dbAddFlag = 0;
2153 code = bc_UpdateHosts();
2155 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2159 /* check for flag */
2160 if (as->parms[0].items != 0) { /* add scan to database */
2164 /* check for port */
2165 if (as->parms[1].items) {
2166 port = getPortOffset(as->parms[1].items->data);
2171 code = bc_ScanDumps(bc_globalConfig, dbAddFlag, port);
2175 /* bc_ParseExpiration
2178 * dates are specified as absolute or relative, the syntax is:
2179 * absolute: at %d/%d/%d [%d:%d] where [..] is optional
2180 * relative: in [%dy][%dm][%dd] where at least one component
2185 bc_ParseExpiration(struct cmd_parmdesc *paramPtr, afs_int32 *expType,
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(struct cmd_syndesc *as, void *arock)
2256 struct cmd_item *ciptr;
2259 ciptr = as->parms[0].items;
2260 if (ciptr == 0) /* no argument specified */
2263 code = DBLookupByVolume(ciptr->data);
2269 /* for ubik version */
2271 bc_dbVerifyCmd(struct cmd_syndesc *as, void *arock)
2277 struct hostent *hostPtr;
2281 extern struct udbHandleS udbHandle;
2283 detail = (as->parms[0].items ? 1 : 0); /* print more details */
2286 ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0, &status, &orphans,
2290 afs_com_err(whoami, code, "; Unable to verify database");
2294 /* verification call succeeded */
2297 printf("Database OK\n");
2299 afs_com_err(whoami, status, "; Database is NOT_OK");
2302 printf("Orphan blocks %d\n", orphans);
2305 printf("Unable to lookup host id\n");
2307 hostPtr = gethostbyaddr((char *)&host, sizeof(host), AF_INET);
2309 printf("Database checker was %d.%d.%d.%d\n",
2310 ((host & 0xFF000000) >> 24), ((host & 0xFF0000) >> 16),
2311 ((host & 0xFF00) >> 8), (host & 0xFF));
2313 printf("Database checker was %s\n", hostPtr->h_name);
2316 return ((status ? -1 : 0));
2320 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2323 deleteDump(afs_uint32 dumpid, /* The dumpid to delete */
2324 afs_int32 port, /* port==-1 means don't go to butc */
2327 afs_int32 code = 0, tcode;
2328 struct budb_dumpEntry dumpEntry;
2329 struct rx_connection *tconn = 0;
2330 afs_int32 i, taskflag, xbsadump;
2331 statusP statusPtr = 0;
2332 budb_dumpsList dumps;
2335 /* If the port is set, we will try to send a delete request to the butc */
2337 tcode = bc_UpdateHosts();
2339 afs_com_err(whoami, tcode, "; Can't retrieve tape hosts");
2343 /* Find the dump in the backup database */
2344 tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2346 afs_com_err(whoami, tcode, "; Unable to locate dumpID %u in database",
2350 xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA));
2352 /* If dump is to an XBSA server, connect to butc and send it
2353 * the dump to delete. Butc will contact the XBSA server.
2354 * The dump will not be an appended dump because XBSA butc
2355 * does not support the append option.
2357 if (xbsadump && dumpEntry.nVolumes) {
2358 tcode = ConnectButc(bc_globalConfig, port, &tconn);
2362 tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2364 if (tcode == RXGEN_OPCODE)
2365 tcode = BC_VERSIONFAIL;
2366 afs_com_err(whoami, tcode,
2367 "; Unable to delete dumpID %u via butc", dumpid);
2371 statusPtr = createStatusNode();
2373 statusPtr->taskId = taskId;
2374 statusPtr->port = port;
2375 statusPtr->jobNumber = bc_jobNumber();
2376 statusPtr->flags |= (SILENT | NOREMOVE); /* No msg & keep statusPtr */
2377 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2378 sprintf(statusPtr->taskName, "DeleteDump");
2381 /* Wait for task to finish */
2382 taskflag = waitForTask(taskId);
2383 if (taskflag & (TASK_ERROR | ABORT_DONE)) {
2384 afs_com_err(whoami, BUTX_DELETEOBJFAIL,
2385 "; Unable to delete dumpID %u via butc", dumpid);
2386 ERROR(BUTX_DELETEOBJFAIL); /* the task failed */
2393 deleteStatusNode(statusPtr); /* Clean up statusPtr - because NOREMOVE */
2395 rx_DestroyConnection(tconn); /* Destroy the connection */
2397 /* Remove the dump from the backup database */
2398 if (!code || force) {
2399 dumps.budb_dumpsList_len = 0;
2400 dumps.budb_dumpsList_val = 0;
2402 tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2404 afs_com_err(whoami, tcode,
2405 "; Unable to delete dumpID %u from database", dumpid);
2406 dumps.budb_dumpsList_len = 0;
2411 /* Display the dumps that were deleted - includes appended dumps */
2412 for (i = 0; i < dumps.budb_dumpsList_len; i++)
2413 printf(" %u%s\n", dumps.budb_dumpsList_val[i],
2414 (i > 0) ? " Appended Dump" : NULL);
2415 if (dumps.budb_dumpsList_val)
2416 free(dumps.budb_dumpsList_val);
2423 * Delete a specified dump from the database
2425 * dump id - single required arg as param 0.
2428 bc_deleteDumpCmd(struct cmd_syndesc *as, void *arock)
2432 afs_int32 rcode = 0;
2433 afs_int32 groupId = 0, havegroupid, sflags, noexecute;
2434 struct cmd_item *ti;
2435 afs_int32 fromTime = 0, toTime = 0, havetime = 0;
2437 budb_dumpsList dumps, flags;
2439 afs_int32 port = -1, force;
2441 /* Must specify at least one of -dumpid, -from, or -to */
2442 if (!as->parms[0].items && !as->parms[1].items && !as->parms[2].items
2443 && !as->parms[4].items) {
2444 afs_com_err(whoami, 0, "Must specify at least one field");
2448 /* Must have -to option with -from option */
2449 if (as->parms[1].items && !as->parms[2].items) {
2450 afs_com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2454 /* Get the time to delete from */
2455 if (as->parms[1].items) { /* -from */
2456 timeString = concatParams(as->parms[1].items);
2461 * Now parse this string for the time to start.
2463 code = ktime_DateToLong(timeString, &fromTime);
2466 afs_com_err(whoami, 0, "Can't parse 'from' date and time");
2467 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2473 port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0); /* -port */
2474 if (as->parms[5].items) /* -dbonly */
2477 force = (as->parms[6].items ? 1 : 0);
2479 havegroupid = (as->parms[4].items ? 1 : 0);
2481 groupId = atoi(as->parms[4].items->data);
2483 noexecute = (as->parms[7].items ? 1 : 0);
2485 /* Get the time to delete to */
2486 if (as->parms[2].items) { /* -to */
2487 timeString = concatParams(as->parms[2].items);
2492 * Now parse this string for the time to start. Simce
2493 * times are at minute granularity, add 59 seconds.
2495 code = ktime_DateToLong(timeString, &toTime);
2498 afs_com_err(whoami, 0, "Can't parse 'to' date and time");
2499 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2506 if (fromTime > toTime) {
2507 afs_com_err(whoami, 0,
2508 "'-from' date/time cannot be later than '-to' date/time");
2512 /* Remove speicific dump ids - if any */
2513 printf("The following dumps %s deleted:\n",
2514 (noexecute ? "would have been" : "were"));
2515 for (ti = as->parms[0].items; ti != 0; ti = ti->next) { /* -dumpid */
2516 dumpid = atoi(ti->data);
2518 code = deleteDump(dumpid, port, force);
2520 printf(" %u\n", dumpid);
2525 * Now remove dumps between to and from dates.
2527 if (havegroupid || havetime) {
2528 dumps.budb_dumpsList_len = 0;
2529 dumps.budb_dumpsList_val = 0;
2530 flags.budb_dumpsList_len = 0;
2531 flags.budb_dumpsList_val = 0;
2534 sflags |= BUDB_OP_GROUPID;
2536 sflags |= BUDB_OP_DATES;
2539 bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2541 afs_com_err(whoami, code,
2542 "; Error while deleting dumps from %u to %u", fromTime,
2547 for (i = 0; i < dumps.budb_dumpsList_len; i++) {
2548 if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP)
2552 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)
2554 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2555 /* Ignore code and continue */
2557 printf(" %u%s%s\n", dumps.budb_dumpsList_val[i],
2559 budb_dumpsList_val[i] & BUDB_OP_APPDUMP) ?
2560 " Appended Dump" : "",
2562 budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?
2563 " Database Dump" : "");
2568 if (dumps.budb_dumpsList_val)
2569 free(dumps.budb_dumpsList_val);
2570 dumps.budb_dumpsList_len = 0;
2571 dumps.budb_dumpsList_val = 0;
2572 if (flags.budb_dumpsList_val)
2573 free(flags.budb_dumpsList_val);
2574 flags.budb_dumpsList_len = 0;
2575 flags.budb_dumpsList_val = 0;
2582 bc_saveDbCmd(struct cmd_syndesc *as, void *arock)
2584 struct rx_connection *tconn;
2585 afs_int32 portOffset = 0;
2592 code = bc_UpdateHosts();
2594 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2598 if (as->parms[0].items) {
2599 portOffset = getPortOffset(as->parms[0].items->data);
2604 /* Get the time to delete to */
2605 if (as->parms[1].items) {
2606 timeString = concatParams(as->parms[1].items);
2611 * Now parse this string for the time. Since
2612 * times are at minute granularity, add 59 seconds.
2614 code = ktime_DateToLong(timeString, &toTime);
2617 afs_com_err(whoami, 0, "Can't parse '-archive' date and time");
2618 afs_com_err(whoami, 0, "%s", ktime_GetDateUsage());
2625 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2629 code = TC_SaveDb(tconn, toTime, &taskId);
2631 afs_com_err(whoami, code, "; Failed to save database");
2635 /* create status monitor block */
2636 statusPtr = createStatusNode();
2638 statusPtr->taskId = taskId;
2639 statusPtr->port = portOffset;
2640 statusPtr->jobNumber = bc_jobNumber();
2641 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2642 sprintf(statusPtr->taskName, "SaveDb");
2646 rx_DestroyConnection(tconn);
2651 bc_restoreDbCmd(struct cmd_syndesc *as, void *arock)
2653 struct rx_connection *tconn;
2654 afs_int32 portOffset = 0;
2659 code = bc_UpdateHosts();
2661 afs_com_err(whoami, code, "; Can't retrieve tape hosts");
2665 if (as->parms[0].items) {
2666 portOffset = getPortOffset(as->parms[0].items->data);
2671 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2675 code = TC_RestoreDb(tconn, &taskId);
2677 afs_com_err(whoami, code, "; Failed to restore database");
2681 /* create status monitor block */
2682 statusPtr = createStatusNode();
2684 statusPtr->taskId = taskId;
2685 statusPtr->port = portOffset;
2686 statusPtr->jobNumber = bc_jobNumber();
2687 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2688 sprintf(statusPtr->taskName, "RestoreDb");
2692 rx_DestroyConnection(tconn);
2696 /* ----------------------------------
2697 * supporting routines for database examination
2698 * ----------------------------------
2701 /* structures and defines for DBLookupByVolume */
2703 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2705 /* dumpedVol - saves interesting information so that we can print it out
2710 struct dumpedVol *next;
2712 afs_int32 initialDumpID;
2713 char tapeName[BU_MAXTAPELEN];
2716 afs_int32 createTime;
2717 afs_int32 incTime; /* actually the clone time */
2720 /* -----------------------------------------
2721 * routines for examining the database
2722 * -----------------------------------------
2726 * Lookup the volumename in the backup database and print the results
2728 * volumeName - volume to lookup
2732 DBLookupByVolume(char *volumeName)
2734 struct budb_dumpEntry dumpEntry;
2735 struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2736 afs_int32 numEntries;
2737 afs_int32 tapedumpid;
2738 afs_int32 last, next;
2740 struct dumpedVol *dvptr = 0;
2741 struct dumpedVol *tempPtr = 0;
2744 char vname[BU_MAXNAMELEN];
2747 for (pass = 0; pass < 2; pass++) {
2749 /* On second pass, search for backup volume */
2751 if (!BackupName(volumeName)) {
2752 strcpy(vname, volumeName);
2753 strcat(vname, ".backup");
2761 while (next != -1) { /*w */
2763 bcdb_LookupVolume(volumeName, &volumeEntry[0], last, &next,
2764 DBL_MAX_VOLUMES, &numEntries);
2768 /* add the volumes to the list */
2769 for (i = 0; i < numEntries; i++) { /*f */
2770 struct dumpedVol *insPtr, **prevPtr;
2773 (struct dumpedVol *)malloc(sizeof(struct dumpedVol));
2777 memset(tempPtr, 0, sizeof(*tempPtr));
2778 tempPtr->incTime = volumeEntry[i].clone;
2779 tempPtr->dumpID = volumeEntry[i].dump;
2780 strncpy(tempPtr->tapeName, volumeEntry[i].tape,
2783 /* check if we need to null terminate it - just for safety */
2784 if (strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN)
2785 tempPtr->tapeName[BU_MAXTAPELEN - 1] = 0;
2787 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2793 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2794 tempPtr->parent = dumpEntry.parent;
2795 tempPtr->level = dumpEntry.level;
2796 tempPtr->createTime = dumpEntry.created;
2798 /* add volume to list in reverse chronological order */
2802 while ((insPtr != 0)
2803 && (insPtr->createTime > tempPtr->createTime)
2805 prevPtr = &insPtr->next;
2806 insPtr = insPtr->next;
2809 /* now at the right place - insert the block */
2810 tempPtr->next = *prevPtr;
2820 ("DumpID lvl parentID creation date clone date tape name\n");
2821 for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2822 /* For the user, the tape name is its name and initial dump id */
2824 (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->
2827 /* beware the static items in compactDateString */
2828 compactDateString(&tempPtr->createTime, ds, 50);
2829 printf("%-9d %-2d %-9d %16s", tempPtr->dumpID, tempPtr->level,
2830 tempPtr->parent, ds);
2831 compactDateString(&tempPtr->incTime, ds, 50);
2832 printf(" %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2838 for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2839 dvptr = dvptr->next;
2844 afs_com_err(whoami, code, NULL);
2848 /* structures for dumpInfo */
2851 struct volumeLink *nextVolume;
2852 struct budb_volumeEntry volumeEntry;
2856 struct tapeLink *nextTape;
2857 struct budb_tapeEntry tapeEntry;
2858 struct volumeLink *firstVolume;
2863 * print information about a dump and all its tapes and volumes.
2867 dumpInfo(afs_int32 dumpid, afs_int32 detailFlag)
2869 struct budb_dumpEntry dumpEntry;
2870 struct tapeLink *head = 0;
2871 struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2872 struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2875 afs_int32 last, next, dbTime;
2876 afs_int32 tapedumpid;
2884 extern struct udbHandleS udbHandle;
2887 lastTapeLinkPtr = 0;
2889 lastVolumeLinkPtr = 0;
2891 /* first get information about the dump */
2893 code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2897 /* For the user, the tape name is its name and initial dump id */
2898 tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2900 /* Is this a database dump id or not */
2901 if (strcmp(dumpEntry.name, DUMP_TAPE_NAME) == 0)
2906 /* print out the information about the dump */
2910 printDumpEntry(&dumpEntry);
2912 time_t t = dumpEntry.created;
2914 printf("Dump: id %u, created: %s\n", dumpEntry.id,
2917 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2918 dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2922 if (!detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0)
2923 && (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA))) {
2924 printf("Backup Service: TSM: Server: %s\n",
2925 dumpEntry.tapes.tapeServer);
2928 /* now get the list of tapes */
2929 for (tapeNumber = dumpEntry.tapes.b; tapeNumber <= dumpEntry.tapes.maxTapes; tapeNumber++) { /*f */
2930 tapeLinkPtr = (struct tapeLink *)malloc(sizeof(struct tapeLink));
2932 afs_com_err(whoami, BC_NOMEM, NULL);
2936 memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2937 code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2944 /* add this tape to previous chain */
2945 if (lastTapeLinkPtr) {
2946 lastTapeLinkPtr->nextTape = tapeLinkPtr;
2947 lastTapeLinkPtr = tapeLinkPtr;
2952 lastTapeLinkPtr = head;
2956 while (next != -1) { /*wn */
2957 vl.budb_volumeList_len = 0;
2958 vl.budb_volumeList_val = 0;
2961 /* now get all the volumes in this dump. */
2962 code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client, UF_SINGLESERVER, BUDB_MAJORVERSION, BUDB_OP_DUMPID | BUDB_OP_TAPENAME, tapeLinkPtr->tapeEntry.name, /* tape name */
2963 dumpid, /* dumpid (not initial dumpid) */
2966 &next, /* nextindex */
2967 &dbTime, /* update time */
2971 if (code == BUDB_ENDOFLIST) { /* 0 volumes on tape */
2978 for (i = 0; i < vl.budb_volumeList_len; i++) {
2979 link = &tapeLinkPtr->firstVolume;
2982 (struct volumeLink *)malloc(sizeof(struct volumeLink));
2983 if (!volumeLinkPtr) {
2984 afs_com_err(whoami, BC_NOMEM, NULL);
2987 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
2989 memcpy(&volumeLinkPtr->volumeEntry,
2990 &vl.budb_volumeList_val[i],
2991 sizeof(struct budb_volumeEntry));
2993 /* now insert it onto the right place */
2995 && (volumeLinkPtr->volumeEntry.position >
2996 (*link)->volumeEntry.position)) {
2997 link = &((*link)->nextVolume);
3000 /* now link it in */
3001 volumeLinkPtr->nextVolume = *link;
3002 *link = volumeLinkPtr;
3005 if (vl.budb_volumeList_val)
3006 free(vl.budb_volumeList_val);
3010 for (tapeLinkPtr = head; tapeLinkPtr; tapeLinkPtr = tapeLinkPtr->nextTape) {
3014 printTapeEntry(&tapeLinkPtr->tapeEntry);
3016 printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name,
3018 printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
3019 compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
3020 printf("created %16s", ds);
3021 if (tapeLinkPtr->tapeEntry.expires != 0) {
3022 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3023 printf(", expires %16s", ds);
3028 /* print out all the volumes */
3030 /* print header for volume listing - db dumps have no volumes */
3031 if ((detailFlag == 0) && !dbDump)
3032 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3035 for (volumeLinkPtr = tapeLinkPtr->firstVolume; volumeLinkPtr;
3036 volumeLinkPtr = volumeLinkPtr->nextVolume) {
3038 printf("\nVolume\n");
3040 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3042 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3043 printf("%4d %s %10u %16s\n",
3044 volumeLinkPtr->volumeEntry.position, ds,
3045 volumeLinkPtr->volumeEntry.nBytes,
3046 volumeLinkPtr->volumeEntry.name);
3053 afs_com_err("dumpInfo", code, "; Can't get dump information");
3055 /* free all allocated structures */
3057 while (tapeLinkPtr) {
3058 volumeLinkPtr = tapeLinkPtr->firstVolume;
3059 while (volumeLinkPtr) {
3060 lastVolumeLinkPtr = volumeLinkPtr;
3061 volumeLinkPtr = volumeLinkPtr->nextVolume;
3062 free(lastVolumeLinkPtr);
3065 lastTapeLinkPtr = tapeLinkPtr;
3066 tapeLinkPtr = tapeLinkPtr->nextTape;
3067 free(lastTapeLinkPtr);
3073 compareDump(struct budb_dumpEntry *ptr1, struct budb_dumpEntry *ptr2)
3075 if (ptr1->created < ptr2->created)
3077 else if (ptr1->created > ptr2->created)
3083 printRecentDumps(int ndumps)
3086 afs_int32 nextindex, index = 0;
3089 struct budb_dumpEntry *dumpPtr;
3093 extern struct udbHandleS udbHandle;
3095 do { /* while (nextindex != -1) */
3096 /* initialize the dump list */
3097 dl.budb_dumpList_len = 0;
3098 dl.budb_dumpList_val = 0;
3100 /* outline algorithm */
3101 code = ubik_Call(BUDB_GetDumps, udbHandle.uh_client, 0, BUDB_MAJORVERSION, BUDB_OP_NPREVIOUS, "", /* no name */
3105 &nextindex, &dbTime, &dl);
3107 if (code == BUDB_ENDOFLIST)
3109 afs_com_err("dumpInfo", code, "; Can't get dump information");
3113 /* No need to sort, it's already sorted */
3115 if (dl.budb_dumpList_len && (index == 0))
3116 printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3117 "parentid", "lv", "created", "nt", "nvols");
3119 dumpPtr = dl.budb_dumpList_val;
3120 for (i = 1; i <= dl.budb_dumpList_len; i++) {
3121 compactDateString(&dumpPtr->created, ds, 50),
3122 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr->id,
3123 dumpPtr->parent, dumpPtr->level, ds,
3124 dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3125 dumpPtr->nVolumes, dumpPtr->name);
3126 if (dumpPtr->initialDumpID) /* an appended dump */
3127 printf(" (%u)", dumpPtr->initialDumpID);
3128 else if (dumpPtr->appendedDumpID) /* has appended dumps */
3129 printf(" (%u)", dumpPtr->id);
3135 if (dl.budb_dumpList_val)
3136 free(dl.budb_dumpList_val);
3138 } while (nextindex != -1);
3144 * list the dumps and contens of the dumps.
3150 bc_dumpInfoCmd(struct cmd_syndesc *as, void *arock)
3153 afs_int32 detailFlag;
3157 if (as->parms[0].items) {
3158 if (as->parms[1].items) {
3159 afs_com_err(whoami, 0,
3160 "These options are exclusive - select only one");
3163 ndumps = atoi(as->parms[0].items->data);
3165 afs_com_err(whoami, 0, "Must provide a positive number");
3169 code = printRecentDumps(ndumps);
3170 } else if (as->parms[1].items) {
3171 detailFlag = (as->parms[2].items ? 1 : 0); /* 1 = detailed listing */
3172 dumpid = atoi(as->parms[1].items->data);
3173 code = dumpInfo(dumpid, detailFlag);
3175 code = printRecentDumps(10);