2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #ifdef HAVE_POSIX_REGEX /* use POSIX regexp library */
21 #include <afs/com_err.h>
22 #include <afs/afsutil.h>
24 #include <afs/budb_prototypes.h>
26 #include <afs/bubasics.h> /* PA */
27 #include <afs/afsint.h>
28 #include <afs/volser.h>
29 #include <afs/voldefs.h> /* PA */
30 #include <afs/vldbint.h> /* PA */
31 #include <afs/ktime.h> /* PA */
34 #include <afs/tcdata.h>
36 #include <afs/vsutils_prototypes.h>
39 #include "error_macros.h"
40 #include "bucoord_internal.h"
41 #include "bucoord_prototypes.h"
43 extern struct bc_config
*bc_globalConfig
;
44 extern struct bc_dumpTask bc_dumpTasks
[BC_MAXSIMDUMPS
];
45 extern struct ubik_client
*cstruct
;
49 extern afs_int32 lastTaskCode
;
51 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
53 static int EvalVolumeSet1(struct bc_config
*aconfig
, struct bc_volumeSet
*avs
,
54 struct bc_volumeDump
**avols
,
55 struct ubik_client
*uclient
);
57 static int EvalVolumeSet2(struct bc_config
*aconfig
, struct bc_volumeSet
*avs
,
58 struct bc_volumeDump
**avols
,
59 struct ubik_client
*uclient
);
60 static int DBLookupByVolume(char *volumeName
);
63 bc_EvalVolumeSet(struct bc_config
*aconfig
,
64 struct bc_volumeSet
*avs
,
65 struct bc_volumeDump
**avols
,
66 struct ubik_client
*uclient
)
67 { /*bc_EvalVolumeSet */
69 static afs_int32 use
= 2;
71 if (use
== 2) { /* Use EvalVolumeSet2() */
72 code
= EvalVolumeSet2(aconfig
, avs
, avols
, uclient
);
73 if (code
== RXGEN_OPCODE
)
76 if (use
== 1) { /* Use EvalVolumeSet1() */
77 code
= EvalVolumeSet1(aconfig
, avs
, avols
, uclient
);
80 } /*bc_EvalVolumeSet */
82 struct partitionsort
{
84 struct bc_volumeDump
*vdlist
;
85 struct bc_volumeDump
*lastvdlist
;
86 struct bc_volumeDump
*dupvdlist
;
87 struct bc_volumeEntry
*vole
;
88 struct partitionsort
*next
;
92 struct partitionsort
*partitions
;
93 struct serversort
*next
;
97 getSPEntries(afs_uint32 server
, afs_int32 partition
,
98 struct serversort
**serverlist
,
99 struct serversort
**ss
,
100 struct partitionsort
**ps
)
102 if (!(*ss
) || ((*ss
)->ipaddr
!= server
)) {
104 for ((*ss
) = *serverlist
; (*ss
); *ss
= (*ss
)->next
) {
105 if ((*ss
)->ipaddr
== server
)
109 /* No server entry added. Add one */
111 *ss
= calloc(1, sizeof(struct serversort
));
113 afs_com_err(whoami
, BC_NOMEM
, NULL
);
117 (*ss
)->ipaddr
= server
;
118 (*ss
)->next
= *serverlist
;
123 if (!(*ps
) || ((*ps
)->part
!= partition
)) {
124 for (*ps
= (*ss
)->partitions
; *ps
; *ps
= (*ps
)->next
) {
125 if ((*ps
)->part
== partition
)
129 /* No partition entry added. Add one */
131 *ps
= calloc(1, sizeof(struct partitionsort
));
133 afs_com_err(whoami
, BC_NOMEM
, NULL
);
139 (*ps
)->part
= partition
;
140 (*ps
)->next
= (*ss
)->partitions
;
141 (*ss
)->partitions
= *ps
;
147 randSPEntries(struct serversort
*serverlist
,
148 struct bc_volumeDump
**avols
)
150 struct serversort
*ss
, **pss
;
151 struct partitionsort
*ps
, **pps
;
153 afs_int32 scount
, pcount
;
157 /* Seed random number generator */
158 r
= time(0) + getpid();
161 /* Count number of servers, remove one at a time */
162 for (scount
= 0, ss
= serverlist
; ss
; ss
= ss
->next
, scount
++);
163 for (; scount
; scount
--) {
164 /* Pick a random server in list and remove it */
165 r
= (rand() >> 4) % scount
;
166 for (pss
= &serverlist
, ss
= serverlist
; r
;
167 pss
= &ss
->next
, ss
= ss
->next
, r
--);
170 /* Count number of partitions, remove one at a time */
171 for (pcount
= 0, ps
= ss
->partitions
; ps
; ps
= ps
->next
, pcount
++);
172 for (; pcount
; pcount
--) {
173 /* Pick a random parition in list and remove it */
174 r
= (rand() >> 4) % pcount
;
175 for (pps
= &ss
->partitions
, ps
= ss
->partitions
; r
;
176 pps
= &ps
->next
, ps
= ps
->next
, r
--);
179 ps
->lastvdlist
->next
= *avols
;
189 EvalVolumeSet2(struct bc_config
*aconfig
,
190 struct bc_volumeSet
*avs
,
191 struct bc_volumeDump
**avols
,
192 struct ubik_client
*uclient
)
193 { /*EvalVolumeSet2 */
194 struct bc_volumeEntry
*tve
;
195 struct bc_volumeDump
*tavols
;
196 struct VldbListByAttributes attributes
;
197 afs_int32 si
, nsi
; /* startIndex and nextStartIndex */
198 afs_int32 nentries
, e
, ei
, et
, add
, l
;
199 nbulkentries bulkentries
;
200 struct nvldbentry
*entries
= 0;
201 struct bc_volumeDump
*tvd
;
202 afs_int32 code
= 0, tcode
;
204 struct serversort
*servers
= 0, *ss
= 0;
205 struct partitionsort
*ps
= 0;
207 *avols
= (struct bc_volumeDump
*)0;
208 bulkentries
.nbulkentries_len
= 0;
209 bulkentries
.nbulkentries_val
= 0;
210 memset(&attributes
, 0, sizeof(attributes
));
212 /* For each of the volume set entries - collect the volumes that match it */
213 for (tve
= avs
->ventries
; tve
; tve
= tve
->next
) {
214 /* Put together a call to the vlserver for this vlentry. The
215 * performance gain is from letting the vlserver expand the
216 * volumeset and not this routine.
219 if (tve
->server
.sin_addr
.s_addr
) { /* The server */
220 attributes
.Mask
|= VLLIST_SERVER
;
221 attributes
.server
= tve
->server
.sin_addr
.s_addr
;
223 if (tve
->partition
!= -1) { /* The partition */
224 attributes
.Mask
|= VLLIST_PARTITION
;
225 attributes
.partition
= tve
->partition
;
228 /* Now make the call to the vlserver */
229 for (si
= 0; si
!= -1; si
= nsi
) {
231 bulkentries
.nbulkentries_len
= 0;
232 bulkentries
.nbulkentries_val
= 0;
235 ubik_VL_ListAttributesN2(uclient
, 0, &attributes
,
236 tve
->name
, si
, &nentries
, &bulkentries
, &nsi
);
240 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
241 * it is not complete. The only way to tell if it is not complete
242 * is if et == 0 (which we check for below). Also, if the call didn't
243 * match any entries, then we don't know what version the vlserver
244 * is. In both cases, we return RXGEN_OPCODE and the calling routine
245 * will switch to the EvalVolumeSet1() call.
248 ERROR(RXGEN_OPCODE
); /* Use EvalVolumeSet1 */
252 if (nentries
> bulkentries
.nbulkentries_len
)
253 nentries
= bulkentries
.nbulkentries_len
;
255 /* Step through each entry and add it to the list of volumes */
256 entries
= bulkentries
.nbulkentries_val
;
257 for (e
= 0; e
< nentries
; e
++) {
258 ei
= entries
[e
].matchindex
& 0xffff;
259 et
= (entries
[e
].matchindex
>> 16) & 0xffff;
274 ERROR(RXGEN_OPCODE
); /* Use EvalVolumeSet1 */
277 /* Find server and partiton structure to hang the entry off of */
279 getSPEntries(entries
[e
].serverNumber
[ei
],
280 entries
[e
].serverPartition
[ei
], &servers
,
283 afs_com_err(whoami
, tcode
, NULL
);
287 /* Detect if this entry should be added (not a duplicate).
288 * Use ps->dupvdlist and ps->vole to only search volumes from
289 * previous volume set entries.
292 if (tve
!= avs
->ventries
) {
293 l
= strlen(entries
[e
].name
);
294 if (ps
->vole
!= tve
) {
296 ps
->dupvdlist
= ps
->vdlist
;
298 for (tavols
= ps
->dupvdlist
; add
&& tavols
;
299 tavols
= tavols
->next
) {
300 if (strncmp(tavols
->name
, entries
[e
].name
, l
) == 0) {
301 if ((strcmp(&entries
[e
].name
[l
], ".backup") == 0)
302 || (strcmp(&entries
[e
].name
[l
], ".readonly")
304 || (strcmp(&entries
[e
].name
[l
], "") == 0))
311 /* Allocate a volume dump structure and its name */
312 tvd
= calloc(1, sizeof(struct bc_volumeDump
));
314 afs_com_err(whoami
, BC_NOMEM
, NULL
);
318 tvd
->name
= malloc(strlen(entries
[e
].name
) + 10);
320 afs_com_err(whoami
, BC_NOMEM
, NULL
);
325 /* Fill it in and thread onto avols list */
326 strcpy(tvd
->name
, entries
[e
].name
);
328 strcat(tvd
->name
, ".backup");
329 else if (et
== ROVOL
)
330 strcat(tvd
->name
, ".readonly");
331 tvd
->vid
= entries
[e
].volumeId
[et
];
334 tvd
->partition
= entries
[e
].serverPartition
[ei
];
335 tvd
->server
.sin_addr
.s_addr
= entries
[e
].serverNumber
[ei
];
336 tvd
->server
.sin_port
= 0; /* default FS port */
337 tvd
->server
.sin_family
= AF_INET
;
338 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
339 tvd
->server
.sin_len
= sizeof(struct sockaddr_in
);
342 /* String tvd off of partition struct */
343 tvd
->next
= ps
->vdlist
;
346 ps
->lastvdlist
= tvd
;
352 /* Free memory allocated during VL call */
353 if (bulkentries
.nbulkentries_val
) {
354 free(bulkentries
.nbulkentries_val
);
355 bulkentries
.nbulkentries_val
= 0;
361 /* Randomly link the volumedump entries together */
362 randSPEntries(servers
, avols
);
363 fprintf(stderr
, "Total number of volumes : %u\n", count
);
366 if (bulkentries
.nbulkentries_val
) {
367 free(bulkentries
.nbulkentries_val
);
370 } /*EvalVolumeSet2 */
372 /*-----------------------------------------------------------------------------
376 * Takes the entries in a volumeset and expands them into a list of
377 * volumes. Every VLDB volume entry is looked at and compared to the
380 * When matching a VLDB volume entry to a volumeset entry,
381 * 1. If the RW volume entry matches, that RW volume is used.
382 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
383 * 3. Finally, if the RO volume entry matches, the RO volume is used.
384 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
385 * "user.troy" and "user.troy.backup". The rules will use
386 * the RW volume "user.troy".
388 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
389 * that volume is used and matches against any remaining volumeset entries
391 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
392 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
393 * would have matched its RW volume "user.troy", but the first
394 * match is used and the second match isn't even done.
397 * aconfig : Global configuration info.
399 * avols : Ptr to linked list of entries describing volumes to dump.
400 * uclient : Ptr to Ubik client structure.
403 * 0 on successful volume set evaluation,
404 * Lower-level codes otherwise.
407 * Expand only the single volume set provided (avs); don't go down its chain.
411 *-----------------------------------------------------------------------------
414 EvalVolumeSet1(struct bc_config
*aconfig
,
415 struct bc_volumeSet
*avs
,
416 struct bc_volumeDump
**avols
,
417 struct ubik_client
*uclient
)
418 { /*EvalVolumeSet1 */
419 afs_int32 code
; /*Result of various calls */
420 struct bc_volumeDump
*tvd
; /*Ptr to new dump instance */
421 struct bc_volumeEntry
*tve
, *ctve
; /*Ptr to new volume entry instance */
422 char patt
[256]; /*Composite regex; also, target string */
423 int volType
= 0; /*Type of volume that worked */
424 afs_int32 index
; /*Current VLDB entry index */
425 afs_int32 count
; /*Needed by VL_ListEntry() */
426 afs_int32 next_index
; /*Next index to list */
427 struct vldbentry entry
; /*VLDB entry */
428 int srvpartpair
; /*Loop counter: server/partition pair */
432 struct serversort
*servers
= 0, *ss
= 0;
433 struct partitionsort
*ps
= 0;
434 #ifdef HAVE_POSIX_REGEX
436 int need_regfree
= 0;
441 *avols
= (struct bc_volumeDump
*)0;
442 ctve
= (struct bc_volumeEntry
*)0; /* no compiled entry */
444 /* For each vldb entry.
445 * Variable next_index is set to the index of the next VLDB entry
446 * in the enumeration.
448 for (index
= 0; 1; index
= next_index
) { /*w */
449 memset(&entry
, 0, sizeof(entry
));
450 code
= ubik_VL_ListEntry(uclient
, /*Ubik client structure */
452 index
, /*Current index */
453 &count
, /*Ptr to working variable */
454 &next_index
, /*Ptr to next index value to list */
455 &entry
); /*Ptr to entry to fill */
459 break; /* If the next index is invalid, bail out now. */
461 /* For each entry in the volume set */
462 found
= 0; /* No match in volume set yet */
463 for (tve
= avs
->ventries
; tve
; tve
= tve
->next
) { /*ve */
464 /* for each server in the vldb entry */
465 for (srvpartpair
= 0; srvpartpair
< entry
.nServers
; srvpartpair
++) { /*s */
466 /* On the same server */
467 if (tve
->server
.sin_addr
.s_addr
468 && !VLDB_IsSameAddrs(tve
->server
.sin_addr
.s_addr
,
469 entry
.serverNumber
[srvpartpair
],
477 /* On the same partition */
478 if ((tve
->partition
!= -1)
479 && (tve
->partition
!= entry
.serverPartition
[srvpartpair
]))
482 /* If the volume entry is not compiled, then compile it */
484 sprintf(patt
, "^%s$", tve
->name
);
485 #ifdef HAVE_POSIX_REGEX
486 if (regcomp(&re
, patt
, REG_NOSUB
) != 0) {
487 afs_com_err(whoami
, 0, "Can't compile regular expression '%s'", patt
);
492 errm
= (char *)re_comp(patt
);
494 afs_com_err(whoami
, 0,
495 "Can't compile regular expression '%s': %s",
503 /* If the RW name matches the volume set entry, take
504 * it and exit. First choice is to use the RW volume.
506 if (entry
.serverFlags
[srvpartpair
] & VLSF_RWVOL
) {
507 if (entry
.flags
& VLF_RWEXISTS
) {
508 sprintf(patt
, "%s", entry
.name
);
509 #ifdef HAVE_POSIX_REGEX
510 code
= regexec(&re
, patt
, 0, NULL
, 0);
513 code
= re_exec(patt
);
517 foundentry
= srvpartpair
;
523 /* If the BK name matches the volume set entry, take
524 * it and exit. Second choice is to use the BK volume.
526 if (entry
.flags
& VLF_BACKEXISTS
) {
527 sprintf(patt
, "%s.backup", entry
.name
);
528 #ifdef HAVE_POSIX_REGEX
529 code
= regexec(&re
, patt
, 0, NULL
, 0);
532 code
= re_exec(patt
);
536 foundentry
= srvpartpair
;
543 /* If the RO name matches the volume set entry, remember
544 * it, but continue searching. Further entries may be
545 * RW or backup entries that will match.
547 else if (!found
&& (entry
.serverFlags
[srvpartpair
] & VLSF_ROVOL
)
548 && (entry
.flags
& VLF_ROEXISTS
)) {
549 sprintf(patt
, "%s.readonly", entry
.name
);
550 #ifdef HAVE_POSIX_REGEX
551 code
= regexec(&re
, patt
, 0, NULL
, 0);
554 code
= re_exec(patt
);
558 foundentry
= srvpartpair
;
564 afs_com_err(whoami
, 0, "Internal error in regex package");
567 /* If found a match, then create a new volume dump entry */
569 /* Find server and partition structure to hang the entry off of */
571 getSPEntries(entry
.serverNumber
[foundentry
],
572 entry
.serverPartition
[foundentry
], &servers
,
575 afs_com_err(whoami
, code
, NULL
);
580 tvd
= calloc(1, sizeof(struct bc_volumeDump
));
582 afs_com_err(whoami
, BC_NOMEM
, NULL
);
586 tvd
->name
= malloc(strlen(entry
.name
) + 10);
588 afs_com_err(whoami
, BC_NOMEM
, NULL
);
593 strcpy(tvd
->name
, entry
.name
);
594 if (volType
== BACKVOL
)
595 strcat(tvd
->name
, ".backup");
596 else if (volType
== ROVOL
)
597 strcat(tvd
->name
, ".readonly");
598 tvd
->vid
= entry
.volumeId
[volType
];
600 tvd
->volType
= volType
;
601 tvd
->partition
= entry
.serverPartition
[foundentry
];
602 tvd
->server
.sin_addr
.s_addr
= entry
.serverNumber
[foundentry
];
603 tvd
->server
.sin_port
= 0; /* default FS port */
604 tvd
->server
.sin_family
= AF_INET
;
606 /* String tvd off of partition struct */
607 tvd
->next
= ps
->vdlist
;
610 ps
->lastvdlist
= tvd
;
616 #ifdef HAVE_POSIX_REGEX
621 /* Randomly link the volumedump entries together */
622 randSPEntries(servers
, avols
);
624 fprintf(stderr
, "Total number of volumes : %u\n", total
);
626 } /*EvalVolumeSet1 */
629 compactTimeString(time_t *date
, char *string
, afs_int32 size
)
636 if (*date
== NEVERDATE
) {
637 sprintf(string
, "NEVER");
639 ltime
= localtime(date
);
640 strftime(string
, size
, "%m/%d/%Y %H:%M", ltime
);
646 * print out a date in compact format, 16 chars, format is
649 * date_long - ptr to a long containing the time
651 * ptr to a string containing a representation of the date
654 compactDateString(afs_uint32
*date_long
, char *string
, afs_int32 size
)
656 time_t t
= *date_long
;
657 return compactTimeString(&t
, string
, size
);
662 bc_SafeATOI(char *anum
)
666 for (; *anum
; anum
++) {
667 if ((*anum
< '0') || (*anum
> '9'))
669 total
= (10 * total
) + (afs_int32
) (*anum
- '0');
675 * Take a string and parse it for a number (could be float) followed
676 * by a character representing the units (K,M,G,T). Default is 'K'.
677 * Return the size in KBytes.
680 bc_FloatATOI(char *anum
)
684 afs_int32 fraction
= 0; /* > 0 if past the decimal */
686 for (; *anum
; anum
++) {
687 if ((*anum
== 't') || (*anum
== 'T')) {
688 total
*= 1024 * 1024 * 1024;
691 if ((*anum
== 'g') || (*anum
== 'G')) {
692 total
*= 1024 * 1024;
695 if ((*anum
== 'm') || (*anum
== 'M')) {
699 if ((*anum
== 'k') || (*anum
== 'K')) {
706 if ((*anum
< '0') || (*anum
> '9'))
710 total
= (10. * total
) + (float)(*anum
- '0');
712 total
+= ((float)(*anum
- '0')) / (float)fraction
;
717 total
+= 0.5; /* Round up */
718 if (total
> 0x7fffffff) /* Don't go over 2G */
720 rtotal
= (afs_int32
) total
;
724 /* make a copy of a string so that it can be freed later */
726 bc_CopyString(char *astring
)
731 return (NULL
); /* propagate null strings easily */
732 tp
= strdup(astring
);
734 afs_com_err(whoami
, BC_NOMEM
, NULL
);
742 * Concatenates the parameters of an option and returns the string.
747 concatParams(struct cmd_item
*itemPtr
)
749 struct cmd_item
*tempPtr
;
750 afs_int32 length
= 0;
753 /* compute the length of string required */
754 for (tempPtr
= itemPtr
; tempPtr
; tempPtr
= tempPtr
->next
) {
755 length
+= strlen(tempPtr
->data
);
756 length
++; /* space or null terminator */
759 if (length
== 0) { /* no string (0 length) */
760 afs_com_err(whoami
, 0, "Can't have zero length date and time string");
764 string
= malloc(length
); /* allocate the string */
766 afs_com_err(whoami
, BC_NOMEM
, NULL
);
771 tempPtr
= itemPtr
; /* now assemble the string */
773 strcat(string
, tempPtr
->data
);
774 tempPtr
= tempPtr
->next
;
779 return (string
); /* return the string */
783 * print out an interface status node as received from butc
787 printIfStatus(struct tciStatusS
*statusPtr
)
789 printf("Task %d: %s: ", statusPtr
->taskId
, statusPtr
->taskName
);
790 if (statusPtr
->nKBytes
)
791 printf("%ld Kbytes transferred", (long unsigned int) statusPtr
->nKBytes
);
792 if (strlen(statusPtr
->volumeName
) != 0) {
793 if (statusPtr
->nKBytes
)
795 printf("volume %s", statusPtr
->volumeName
);
800 if (statusPtr
->flags
& ABORT_REQUEST
)
801 printf(" [abort request rcvd]");
803 if (statusPtr
->flags
& ABORT_DONE
)
804 printf(" [abort complete]");
806 if (statusPtr
->flags
& OPR_WAIT
)
807 printf(" [operator wait]");
809 if (statusPtr
->flags
& CALL_WAIT
)
810 printf(" [callout in progress]");
812 if (statusPtr
->flags
& DRIVE_WAIT
)
813 printf(" [drive wait]");
815 if (statusPtr
->flags
& TASK_DONE
)
821 getPortOffset(char *port
)
823 afs_int32 portOffset
;
825 portOffset
= bc_SafeATOI(port
);
827 if (portOffset
< 0) {
828 afs_com_err(whoami
, 0, "Can't decode port offset '%s'", port
);
830 } else if (portOffset
> BC_MAXPORTOFFSET
) {
831 afs_com_err(whoami
, 0, "%u exceeds max port offset %u", portOffset
,
838 /* bc_GetTapeStatusCmd
839 * display status of all tasks on a particular tape coordinator
842 bc_GetTapeStatusCmd(struct cmd_syndesc
*as
, void *arock
)
845 struct rx_connection
*tconn
;
846 afs_int32 portOffset
= 0;
851 struct tciStatusS status
;
853 code
= bc_UpdateHosts();
855 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
859 if (as
->parms
[0].items
) {
860 portOffset
= getPortOffset(as
->parms
[0].items
->data
);
865 code
= ConnectButc(bc_globalConfig
, portOffset
, &tconn
);
869 flags
= TSK_STAT_FIRST
;
873 while ((flags
& TSK_STAT_END
) == 0) {
874 code
= TC_ScanStatus(tconn
, &taskId
, &status
, &flags
);
876 if (code
== TC_NOTASKS
)
878 afs_com_err(whoami
, code
, "; Can't get status from butc");
881 if ((flags
& TSK_STAT_NOTFOUND
))
882 break; /* Can't find the task id */
883 flags
&= ~TSK_STAT_FIRST
; /* turn off flag */
885 printIfStatus(&status
);
890 printf("Tape coordinator is idle\n");
892 if (flags
& TSK_STAT_ADSM
)
893 printf("TSM Tape coordinator\n");
894 else if (flags
& TSK_STAT_XBSA
)
895 printf("XBSA Tape coordinator\n");
900 extern struct Lock dispatchLock
;
903 * wait for all jobs to terminate
906 bc_WaitForNoJobs(void)
909 int usefulJobRunning
= 1;
910 int printWaiting
= 1;
912 extern dlqlinkT statusHead
;
914 while (usefulJobRunning
) {
915 usefulJobRunning
= (dlqEmpty(&statusHead
) ? 0 : 1);
916 if (dispatchLock
.excl_locked
)
917 usefulJobRunning
= 1;
918 for (i
= 0; (!usefulJobRunning
&& (i
< BC_MAXSIMDUMPS
)); i
++) {
919 if (bc_dumpTasks
[i
].flags
& BC_DI_INUSE
)
920 usefulJobRunning
= 1;
923 /* Wait 5 seconds and check again */
924 if (usefulJobRunning
) {
926 afs_com_err(whoami
, 0, "waiting for job termination");
932 return (lastTaskCode
);
936 * print status on running jobs
938 * ignored - a null "as" prints only jobs.
941 bc_JobsCmd(struct cmd_syndesc
*as
, void *arock
)
950 extern dlqlinkT statusHead
;
952 dlqInit(&atJobsHead
);
955 ptr
= (&statusHead
)->dlq_next
;
956 while (ptr
!= &statusHead
) {
957 statusPtr
= (statusP
) ptr
;
960 if (statusPtr
->scheduledDump
) {
961 dlqUnlink((dlqlinkP
) statusPtr
);
962 dlqLinkb(&atJobsHead
, (dlqlinkP
) statusPtr
);
964 printf("Job %d:", statusPtr
->jobNumber
);
966 printf(" %s", statusPtr
->taskName
);
968 if (statusPtr
->dbDumpId
)
969 printf(": DumpID %u", statusPtr
->dbDumpId
);
970 if (statusPtr
->nKBytes
)
971 printf(", %ld Kbytes", afs_printable_int32_ld(statusPtr
->nKBytes
));
972 if (strlen(statusPtr
->volumeName
) != 0)
973 printf(", volume %s", statusPtr
->volumeName
);
975 if (statusPtr
->flags
& CONTACT_LOST
)
976 printf(" [butc contact lost]");
978 if (statusPtr
->flags
& ABORT_REQUEST
)
979 printf(" [abort request]");
981 if (statusPtr
->flags
& ABORT_SENT
)
982 printf(" [abort sent]");
984 if (statusPtr
->flags
& OPR_WAIT
)
985 printf(" [operator wait]");
987 if (statusPtr
->flags
& CALL_WAIT
)
988 printf(" [callout in progress]");
990 if (statusPtr
->flags
& DRIVE_WAIT
)
991 printf(" [drive wait]");
997 * Now print the scheduled dumps.
999 if (!dlqEmpty(&statusHead
) && as
)
1000 printf("\n"); /* blank line between running and scheduled dumps */
1003 while (!dlqEmpty(&atJobsHead
)) {
1004 ptr
= (&atJobsHead
)->dlq_next
;
1005 youngest
= (statusP
) ptr
;
1007 ptr
= ptr
->dlq_next
;
1008 while (ptr
!= &atJobsHead
) { /* Find the dump that starts the earliest */
1009 statusPtr
= (statusP
) ptr
;
1010 if (statusPtr
->scheduledDump
< youngest
->scheduledDump
)
1011 youngest
= statusPtr
;
1012 ptr
= ptr
->dlq_next
;
1015 /* Print token expiration time */
1016 if ((tokenExpires
> prevTime
)
1017 && (tokenExpires
<= youngest
->scheduledDump
) && as
1018 && (tokenExpires
!= NEVERDATE
)) {
1019 if (tokenExpires
> time(0)) {
1020 compactTimeString(&tokenExpires
, ds
, 50);
1021 printf(" %16s: TOKEN EXPIRATION\n", ds
);
1023 printf(" TOKEN HAS EXPIRED\n");
1026 prevTime
= youngest
->scheduledDump
;
1028 /* Print the info */
1029 compactDateString(&youngest
->scheduledDump
, ds
, 50);
1030 printf("Job %d:", youngest
->jobNumber
);
1031 printf(" %16s: %s", ds
, youngest
->cmdLine
);
1034 /* return to original list */
1035 dlqUnlink((dlqlinkP
) youngest
);
1036 dlqLinkb(&statusHead
, (dlqlinkP
) youngest
);
1039 /* Print token expiration time if havn't already */
1040 if ((tokenExpires
== NEVERDATE
) && as
)
1041 printf(" : TOKEN NEVER EXPIRES\n");
1042 else if ((tokenExpires
> prevTime
) && as
) {
1043 if (tokenExpires
> time(0)) {
1044 compactTimeString(&tokenExpires
, ds
, 50);
1045 printf(" %16s: TOKEN EXPIRATION\n", ds
);
1047 printf(" : TOKEN HAS EXPIRED\n");
1056 bc_KillCmd(struct cmd_syndesc
*as
, void *arock
)
1060 struct bc_dumpTask
*td
;
1067 extern dlqlinkT statusHead
;
1069 tp
= as
->parms
[0].items
->data
;
1070 if (strchr(tp
, '.') == 0) {
1071 slot
= bc_SafeATOI(tp
);
1073 afs_com_err(whoami
, 0, "Bad syntax for number '%s'", tp
);
1078 ptr
= (&statusHead
)->dlq_next
;
1079 while (ptr
!= &statusHead
) {
1080 statusPtr
= (statusP
) ptr
;
1081 if (statusPtr
->jobNumber
== slot
) {
1082 statusPtr
->flags
|= ABORT_REQUEST
;
1086 ptr
= ptr
->dlq_next
;
1090 fprintf(stderr
, "Job %d not found\n", slot
);
1095 for (i
= 0; i
< BC_MAXSIMDUMPS
; i
++, td
++) {
1096 if (td
->flags
& BC_DI_INUSE
) {
1098 strcpy(tbuffer
, td
->volSetName
);
1099 strcat(tbuffer
, ".");
1100 strcat(tbuffer
, tailCompPtr(td
->dumpName
));
1101 if (strcmp(tbuffer
, tp
) == 0)
1105 if (i
>= BC_MAXSIMDUMPS
) {
1106 afs_com_err(whoami
, 0, "Can't find job %s", tp
);
1111 statusPtr
= findStatus(td
->dumpID
);
1113 if (statusPtr
== 0) {
1114 afs_com_err(whoami
, 0, "Can't locate status - internal error");
1118 statusPtr
->flags
|= ABORT_REQUEST
;
1124 /* restore a volume or volumes */
1126 bc_VolRestoreCmd(struct cmd_syndesc
*as
, void *arock
)
1129 * parm 0 is the new server to restore to
1130 * parm 1 is the new partition to restore to
1131 * parm 2 is volume(s) to restore
1132 * parm 3 is the new extension, if any, for the volume name.
1133 * parm 4 gives the new volume # to restore this volume as (removed).
1134 * parm 4 date is a string representing the date
1136 * We handle four types of restores. If old is set, then we restore the
1137 * volume with the same name and ID. If old is not set, we allocate
1138 * a new volume ID for the restored volume. If a new extension is specified,
1139 * we add that extension to the volume name of the restored volume.
1141 struct bc_volumeEntry tvolumeEntry
; /* entry within the volume set */
1142 struct bc_volumeDump
*volsToRestore
= (struct bc_volumeDump
*)0;
1143 struct bc_volumeDump
*lastVol
= (struct bc_volumeDump
*)0;
1144 struct bc_volumeDump
*tvol
; /* temp for same */
1145 struct sockaddr_in destServ
; /* machine to which to restore volumes */
1146 afs_int32 destPartition
; /* partition to which to restore volumes */
1148 struct cmd_item
*ti
;
1152 afs_int32 dumpID
= 0;
1153 char *newExt
, *timeString
;
1155 afs_int32
*ports
= NULL
;
1156 afs_int32 portCount
= 0;
1159 code
= bc_UpdateHosts();
1161 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
1165 /* specified other destination host */
1166 tp
= as
->parms
[0].items
->data
;
1167 if (bc_ParseHost(tp
, &destServ
)) {
1168 afs_com_err(whoami
, 0, "Failed to locate destination host '%s'", tp
);
1172 /* specified other destination partition */
1173 tp
= as
->parms
[1].items
->data
;
1174 if (bc_GetPartitionID(tp
, &destPartition
)) {
1175 afs_com_err(whoami
, 0, "Can't parse destination partition '%s'", tp
);
1179 for (ti
= as
->parms
[2].items
; ti
; ti
= ti
->next
) {
1180 /* build list of volume items */
1181 tvol
= calloc(1, sizeof(struct bc_volumeDump
));
1183 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1187 tvol
->name
= malloc(VOLSER_MAXVOLNAME
+ 1);
1189 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1192 strncpy(tvol
->name
, ti
->data
, VOLSER_OLDMAXVOLNAME
);
1193 tvol
->entry
= &tvolumeEntry
;
1196 lastVol
->next
= tvol
; /* thread onto end of list */
1198 volsToRestore
= tvol
;
1202 if (as
->parms
[4].items
) {
1203 timeString
= concatParams(as
->parms
[4].items
);
1207 code
= ktime_DateToLong(timeString
, &fromDate
);
1210 afs_com_err(whoami
, 0, "Can't parse restore date and time");
1211 afs_com_err(whoami
, 0, "%s", ktime_GetDateUsage());
1215 fromDate
= 0x7fffffff; /* latest one */
1218 newExt
= (as
->parms
[3].items
? as
->parms
[3].items
->data
: NULL
);
1221 /* Read all the port offsets into the ports array. The first element in the
1222 * array is for full restore and the rest are for incremental restores
1224 if (as
->parms
[5].items
) {
1225 for (ti
= as
->parms
[5].items
; ti
; ti
= ti
->next
)
1227 ports
= malloc(portCount
* sizeof(afs_int32
));
1229 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1233 for (ti
= as
->parms
[5].items
, i
= 0; ti
; ti
= ti
->next
, i
++) {
1234 ports
[i
] = getPortOffset(ti
->data
);
1240 dontExecute
= (as
->parms
[6].items
? 1 : 0); /* -n */
1242 if (as
->parms
[7].items
)
1244 dumpID
= atoi(as
->parms
[7].items
->data
);
1250 * Perform the call to start the restore.
1253 bc_StartDmpRst(bc_globalConfig
, "volume", "restore", volsToRestore
,
1254 &destServ
, destPartition
, fromDate
, newExt
, oldFlag
,
1255 /*parentDump */ dumpID
, /*dumpLevel */ 0,
1256 bc_Restorer
, ports
, portCount
,
1257 /*dumpSched */ NULL
, /*append */ 0, dontExecute
);
1259 afs_com_err(whoami
, code
, "; Failed to queue restore");
1264 /* restore a whole partition or server */
1266 /* bc_DiskRestoreCmd
1267 * restore a whole partition
1269 * first, reqd - machine (server) to restore
1270 * second, reqd - partition to restore
1275 bc_DiskRestoreCmd(struct cmd_syndesc
*as
, void *arock
)
1277 struct bc_volumeSet tvolumeSet
; /* temporary volume set for EvalVolumeSet call */
1278 struct bc_volumeEntry tvolumeEntry
; /* entry within the volume set */
1279 struct bc_volumeDump
*volsToRestore
= (struct bc_volumeDump
*)0;
1280 struct sockaddr_in destServ
; /* machine to which to restore volumes */
1281 afs_int32 destPartition
; /* partition to which to restore volumes */
1287 afs_int32
*ports
= NULL
;
1288 afs_int32 portCount
= 0;
1290 struct bc_volumeDump
*prev
, *tvol
, *nextvol
;
1291 struct cmd_item
*ti
;
1294 /* parm 0 is the server to restore
1295 * parm 1 is the partition to restore
1297 * parm 8 and above as in VolRestoreCmd:
1298 * parm 8 is the new server to restore to
1299 * parm 9 is the new partition to restore to
1302 code
= bc_UpdateVolumeSet();
1304 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
1307 code
= bc_UpdateHosts();
1309 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
1313 /* create a volume set corresponding to the volume pattern we've been given */
1314 memset(&tvolumeSet
, 0, sizeof(tvolumeSet
));
1315 memset(&tvolumeEntry
, 0, sizeof(tvolumeEntry
));
1316 tvolumeSet
.name
= "TempVolumeSet";
1317 tvolumeSet
.ventries
= &tvolumeEntry
;
1318 tvolumeEntry
.serverName
= as
->parms
[0].items
->data
;
1319 tvolumeEntry
.partname
= as
->parms
[1].items
->data
;
1321 if (bc_GetPartitionID(tvolumeEntry
.partname
, &tvolumeEntry
.partition
)) {
1322 afs_com_err(whoami
, 0, "Can't parse partition '%s'",
1323 tvolumeEntry
.partname
);
1327 if (bc_ParseHost(tvolumeEntry
.serverName
, &tvolumeEntry
.server
)) {
1328 afs_com_err(whoami
, 0, "Can't locate host '%s'", tvolumeEntry
.serverName
);
1332 /* specified other destination host */
1333 if (as
->parms
[8].items
) {
1334 tp
= as
->parms
[8].items
->data
;
1335 if (bc_ParseHost(tp
, &destServ
)) {
1336 afs_com_err(whoami
, 0, "Can't locate destination host '%s'", tp
);
1339 } else /* use destination host == original host */
1340 memcpy(&destServ
, &tvolumeEntry
.server
, sizeof(destServ
));
1342 /* specified other destination partition */
1343 if (as
->parms
[9].items
) {
1344 tp
= as
->parms
[9].items
->data
;
1345 if (bc_GetPartitionID(tp
, &destPartition
)) {
1346 afs_com_err(whoami
, 0, "Can't parse destination partition '%s'", tp
);
1349 } else /* use original partition */
1350 destPartition
= tvolumeEntry
.partition
;
1352 tvolumeEntry
.name
= ".*"; /* match all volumes (this should be a parameter) */
1354 if (as
->parms
[2].items
) {
1355 for (ti
= as
->parms
[2].items
; ti
; ti
= ti
->next
)
1357 ports
= malloc(portCount
* sizeof(afs_int32
));
1359 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1363 for (ti
= as
->parms
[2].items
, i
= 0; ti
; ti
= ti
->next
, i
++) {
1364 ports
[i
] = getPortOffset(ti
->data
);
1370 newExt
= (as
->parms
[10].items
? as
->parms
[10].items
->data
: NULL
);
1371 dontExecute
= (as
->parms
[11].items
? 1 : 0); /* -n */
1374 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1377 bc_EvalVolumeSet(bc_globalConfig
, &tvolumeSet
, &volsToRestore
,
1380 afs_com_err(whoami
, code
, "; Failed to evaluate volume set");
1384 /* Since we want only RW volumes, remove any
1385 * BK or RO volumes from the list.
1387 for (prev
= 0, tvol
= volsToRestore
; tvol
; tvol
= nextvol
) {
1388 nextvol
= tvol
->next
;
1389 if (BackupName(tvol
->name
)) {
1392 "Will not restore volume %s (its RW does not reside on the partition)\n",
1395 if (tvol
== volsToRestore
) {
1396 volsToRestore
= nextvol
;
1398 prev
->next
= nextvol
;
1408 fromDate
= 0x7fffffff; /* last one before this date */
1409 oldFlag
= 1; /* do restore same volid (and name) */
1412 * Perform the call to start the dump.
1415 bc_StartDmpRst(bc_globalConfig
, "disk", "restore", volsToRestore
,
1416 &destServ
, destPartition
, fromDate
, newExt
, oldFlag
,
1417 /*parentDump */ 0, /*dumpLevel */ 0,
1418 bc_Restorer
, ports
, portCount
,
1419 /*dumpSched */ NULL
, /*append */ 0, dontExecute
);
1421 afs_com_err(whoami
, code
, "; Failed to queue restore");
1426 /* bc_VolsetRestoreCmd
1427 * restore a volumeset or list of volumes.
1431 bc_VolsetRestoreCmd(struct cmd_syndesc
*as
, void *arock
)
1438 afs_int32
*ports
= NULL
;
1439 afs_int32 portCount
= 0;
1442 struct bc_volumeSet
*volsetPtr
; /* Ptr to list of generated volume info */
1443 struct bc_volumeDump
*volsToRestore
= (struct bc_volumeDump
*)0;
1444 struct bc_volumeDump
*lastVol
= (struct bc_volumeDump
*)0;
1445 struct sockaddr_in destServer
; /* machine to which to restore volume */
1446 afs_int32 destPartition
; /* partition to which to restore volumes */
1447 struct cmd_item
*ti
;
1450 code
= bc_UpdateVolumeSet();
1452 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
1455 code
= bc_UpdateHosts();
1457 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
1461 if (as
->parms
[0].items
) {
1462 if (as
->parms
[1].items
) {
1463 afs_com_err(whoami
, 0, "Can't have both -name and -file options");
1467 volsetName
= as
->parms
[0].items
->data
;
1468 volsetPtr
= bc_FindVolumeSet(bc_globalConfig
, volsetName
);
1470 afs_com_err(whoami
, 0,
1471 "Can't find volume set '%s' in backup database",
1476 /* Expand out the volume set into its component list of volumes. */
1478 bc_EvalVolumeSet(bc_globalConfig
, volsetPtr
, &volsToRestore
,
1481 afs_com_err(whoami
, code
, "; Failed to evaluate volume set");
1484 } else if (as
->parms
[1].items
) {
1487 char server
[50], partition
[50], volume
[50], rest
[256];
1489 struct bc_volumeDump
*tvol
;
1491 fd
= fopen(as
->parms
[1].items
->data
, "r");
1493 afs_com_err(whoami
, errno
, "; Cannot open file '%s'",
1494 as
->parms
[1].items
->data
);
1498 while (fgets(line
, 255, fd
)) {
1500 sscanf(line
, "%s %s %s %s", server
, partition
, volume
, rest
);
1506 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1507 fprintf(stderr
, " %s", line
);
1511 if (bc_ParseHost(server
, &destServer
)) {
1512 afs_com_err(whoami
, 0, "Failed to locate host '%s'", server
);
1516 if (bc_GetPartitionID(partition
, &destPartition
)) {
1517 afs_com_err(whoami
, 0,
1518 "Failed to parse destination partition '%s'",
1523 /* Allocate a volumeDump structure and link it in */
1524 tvol
= calloc(1, sizeof(struct bc_volumeDump
));
1526 tvol
->name
= malloc(VOLSER_MAXVOLNAME
+ 1);
1528 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1531 strncpy(tvol
->name
, volume
, VOLSER_OLDMAXVOLNAME
);
1532 memcpy(&tvol
->server
, &destServer
, sizeof(destServer
));
1533 tvol
->partition
= destPartition
;
1536 lastVol
->next
= tvol
; /* thread onto end of list */
1538 volsToRestore
= tvol
;
1543 afs_com_err(whoami
, 0, "-name or -file option required");
1548 /* Get the port offset for the restore */
1549 if (as
->parms
[2].items
) {
1550 for (ti
= as
->parms
[2].items
; ti
; ti
= ti
->next
)
1552 ports
= malloc(portCount
* sizeof(afs_int32
));
1554 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1558 for (ti
= as
->parms
[2].items
, i
= 0; ti
; ti
= ti
->next
, i
++) {
1559 ports
[i
] = getPortOffset(ti
->data
);
1565 newExt
= (as
->parms
[3].items
? as
->parms
[3].items
->data
: NULL
);
1566 dontExecute
= (as
->parms
[4].items
? 1 : 0);
1568 fromDate
= 0x7fffffff; /* last one before this date */
1569 oldFlag
= 1; /* do restore same volid (and name) */
1571 /* Perform the call to start the restore */
1572 code
= bc_StartDmpRst(bc_globalConfig
, "disk", "restore", volsToRestore
,
1573 /*destserver */ NULL
, /*destpartition */ 0, fromDate
,
1575 /*parentDump */ 0, /*dumpLevel */ 0,
1576 bc_Restorer
, ports
, portCount
,
1577 /*dumpSched */ NULL
, /*append */ 0, dontExecute
);
1579 afs_com_err(whoami
, code
, "; Failed to queue restore");
1584 /*-----------------------------------------------------------------------------
1588 * Perform a dump of the set of volumes provided.
1589 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1592 * as : Parsed command line information.
1593 * arock : Ptr to misc stuff; not used.
1597 * The result of bc_StartDump() otherwise
1604 *---------------------------------------------------------------------------
1609 bc_DumpCmd(struct cmd_syndesc
*as
, void *arock
)
1611 char *dumpPath
= NULL
;
1612 char *vsName
= NULL
; /*Ptrs to various names */
1613 struct bc_volumeSet
*tvs
= NULL
; /*Ptr to list of generated volume info */
1614 struct bc_dumpSchedule
*tds
;
1615 struct bc_dumpSchedule
*baseds
= NULL
; /*Ptr to dump schedule node */
1616 struct bc_volumeDump
*tve
, *volsToDump
; /*Ptr to individual vols to be dumped */
1617 struct budb_dumpEntry dumpEntry
, de
, fde
; /* dump entry */
1620 afs_int32 parent
; /* parent dump */
1621 afs_int32 level
; /* this dump's level # */
1622 afs_int32 problemFindingDump
; /* can't find parent(s) */
1624 afs_int32
*portp
= NULL
;
1625 afs_int32 doAt
, atTime
; /* Time a timed-dump is to start at */
1628 int doAppend
= 0; /* Append the dump to dump set */
1629 afs_int32 code
; /* Return code */
1630 int loadfile
; /* whether to load a file or not */
1634 extern struct bc_dumpTask bc_dumpTasks
[];
1636 code
= bc_UpdateDumpSchedule();
1638 afs_com_err(whoami
, code
, "; Can't retrieve dump schedule");
1641 code
= bc_UpdateVolumeSet();
1643 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
1646 code
= bc_UpdateHosts();
1648 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
1653 * Some parameters cannot be specified together
1654 * The "-file" option cannot exist with the "-volume", "-dump",
1655 * "-portoffset", or "-append" option
1657 if (as
->parms
[6].items
) {
1659 if (as
->parms
[0].items
|| as
->parms
[1].items
|| as
->parms
[2].items
1660 || as
->parms
[4].items
) {
1661 afs_com_err(whoami
, 0, "Invalid option specified with -file option");
1666 if (!as
->parms
[0].items
|| !as
->parms
[1].items
) {
1667 afs_com_err(whoami
, 0,
1668 "Must specify volume set name and dump level name");
1674 * Get the time we are to perform this dump
1676 if (as
->parms
[3].items
) {
1679 timeString
= concatParams(as
->parms
[3].items
);
1684 * Now parse this string for the time to start.
1686 code
= ktime_DateToLong(timeString
, &atTime
);
1689 afs_com_err(whoami
, 0, "Can't parse dump start date and time");
1690 afs_com_err(whoami
, 0, "%s", ktime_GetDateUsage());
1696 dontExecute
= (as
->parms
[5].items
? 1 : 0); /* -n */
1699 * If this dump is not a load file, then check the parameters.
1701 if (!loadfile
) { /*6 */
1702 vsName
= as
->parms
[0].items
->data
; /* get volume set name */
1703 dumpPath
= as
->parms
[1].items
->data
; /* get dump path */
1705 /* get the port number, if one was specified */
1706 if (as
->parms
[2].items
) {
1707 portp
= malloc(sizeof(afs_int32
));
1709 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1713 *portp
= getPortOffset(as
->parms
[2].items
->data
);
1718 doAppend
= (as
->parms
[4].items
? 1 : 0); /* -append */
1721 * Get a hold of the given volume set and dump set.
1723 tvs
= bc_FindVolumeSet(bc_globalConfig
, vsName
);
1725 afs_com_err(whoami
, 0,
1726 "Can't find volume set '%s' in backup database", vsName
);
1729 baseds
= bc_FindDumpSchedule(bc_globalConfig
, dumpPath
);
1731 afs_com_err(whoami
, 0,
1732 "Can't find dump schedule '%s' in backup database",
1741 * If given the "-at" option, then add this to the jobs list and return
1744 * Create a status node for this timed dump.
1745 * Fill in the time to dump and the cmd line for the dump leaving off
1746 * the -at option. If the -n option is there, it is scheduled with
1747 * the Timed dump as opposed to not scheduling the time dump at all.
1750 if (atTime
< time(0)) {
1751 afs_com_err(whoami
, 0,
1752 "Time of dump is earlier then current time - not added");
1754 statusPtr
= createStatusNode();
1756 statusPtr
->scheduledDump
= atTime
;
1758 /* Determine length of the dump command */
1760 length
+= 4; /* "dump" */
1762 length
+= 6; /* " -file" */
1763 length
+= 1 + strlen(as
->parms
[6].items
->data
); /* " <file>" */
1765 /* length += 11; *//* " -volumeset" */
1766 length
+= 1 + strlen(as
->parms
[0].items
->data
); /* " <volset> */
1768 /* length += 6; *//* " -dump" */
1769 length
+= 1 + strlen(as
->parms
[1].items
->data
); /* " <dumpset> */
1771 if (as
->parms
[2].items
) {
1772 /* length += 12; *//* " -portoffset" */
1773 length
+= 1 + strlen(as
->parms
[2].items
->data
); /* " <port>" */
1776 if (as
->parms
[4].items
)
1777 length
+= 8; /* " -append" */
1781 length
+= 3; /* " -n" */
1782 length
++; /* end-of-line */
1784 /* Allocate status block for this timed dump */
1785 sprintf(statusPtr
->taskName
, "Scheduled Dump");
1786 statusPtr
->jobNumber
= bc_jobNumber();
1787 statusPtr
->scheduledDump
= atTime
;
1788 statusPtr
->cmdLine
= malloc(length
);
1789 if (!statusPtr
->cmdLine
) {
1790 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1794 /* Now reconstruct the dump command */
1795 statusPtr
->cmdLine
[0] = 0;
1796 strcat(statusPtr
->cmdLine
, "dump");
1798 strcat(statusPtr
->cmdLine
, " -file");
1799 strcat(statusPtr
->cmdLine
, " ");
1800 strcat(statusPtr
->cmdLine
, as
->parms
[6].items
->data
);
1802 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1803 strcat(statusPtr
->cmdLine
, " ");
1804 strcat(statusPtr
->cmdLine
, as
->parms
[0].items
->data
);
1806 /* strcat(statusPtr->cmdLine, " -dump"); */
1807 strcat(statusPtr
->cmdLine
, " ");
1808 strcat(statusPtr
->cmdLine
, as
->parms
[1].items
->data
);
1810 if (as
->parms
[2].items
) {
1811 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1812 strcat(statusPtr
->cmdLine
, " ");
1813 strcat(statusPtr
->cmdLine
, as
->parms
[2].items
->data
);
1816 if (as
->parms
[4].items
)
1817 strcat(statusPtr
->cmdLine
, " -append");
1820 strcat(statusPtr
->cmdLine
, " -n");
1822 printf("Add scheduled dump as job %d\n", statusPtr
->jobNumber
);
1823 if ((atTime
> tokenExpires
) && (tokenExpires
!= NEVERDATE
))
1824 afs_com_err(whoami
, 0,
1825 "Warning: job %d starts after expiration of AFS token",
1826 statusPtr
->jobNumber
);
1835 * Read and execute the load file if specified. The work of reading is done
1836 * in the main routine prior the dispatch call. loadFile and dontExecute are
1837 * global variables so this can take place in main.
1840 loadFile
= strdup(as
->parms
[6].items
->data
);
1842 afs_com_err(whoami
, BC_NOMEM
, NULL
);
1849 * We are doing a real dump (no load file or timed dump).
1851 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName
,
1854 /* For each dump-level above this one, see if the volumeset was dumped
1855 * at the level. We search all dump-levels since a higher dump-level
1856 * may have actually been done AFTER a lower dump level.
1858 parent
= level
= problemFindingDump
= 0;
1859 for (tds
= baseds
->parent
; tds
; tds
= tds
->parent
) {
1860 /* Find the most recent dump of the volume-set at this dump-level.
1861 * Raise problem flag if didn't find a dump and a parent not yet found.
1863 code
= bcdb_FindLatestDump(vsName
, tds
->name
, &dumpEntry
);
1866 problemFindingDump
= 1; /* skipping a dump level */
1870 /* We found the most recent dump at this level. Now check
1871 * if we should use it by seeing if its full dump hierarchy
1872 * exists. If it doesn't, we don't want to base our incremental
1875 if (!parent
|| (dumpEntry
.id
> parent
)) {
1876 /* Follow the parent dumps to see if they are all there */
1877 for (d
= dumpEntry
.parent
; d
; d
= de
.parent
) {
1878 code
= bcdb_FindDumpByID(d
, &de
);
1883 /* If we found the entire level, remember it. Otherwise raise flag.
1884 * If we had already found a dump, raise the problem flag.
1888 problemFindingDump
= 1;
1889 parent
= dumpEntry
.id
;
1890 level
= dumpEntry
.level
+ 1;
1891 memcpy(&fde
, &dumpEntry
, sizeof(dumpEntry
));
1893 /* Dump hierarchy not complete so can't base off the latest */
1894 problemFindingDump
= 1;
1899 /* If the problemflag was raise, it means we are not doing the
1900 * dump at the level we requested it be done at.
1902 if (problemFindingDump
) {
1903 afs_com_err(whoami
, 0,
1904 "Warning: Doing level %d dump due to missing higher-level dumps",
1907 printf("Parent dump: dump %s (DumpID %u)\n", fde
.name
, parent
);
1909 } else if (parent
) {
1910 printf("Found parent: dump %s (DumpID %u)\n", fde
.name
, parent
);
1913 /* Expand out the volume set into its component list of volumes. */
1914 code
= bc_EvalVolumeSet(bc_globalConfig
, tvs
, &volsToDump
, cstruct
);
1916 afs_com_err(whoami
, code
, "; Failed to evaluate volume set");
1920 printf("No volumes to dump\n");
1924 /* Determine what the clone time of the volume was when it was
1925 * last dumped (tve->date). This is the time from when an
1926 * incremental should be done (remains zero if a full dump).
1929 for (tve
= volsToDump
; tve
; tve
= tve
->next
) {
1930 code
= bcdb_FindClone(parent
, tve
->name
, &tve
->date
);
1934 /* Get the time the volume was last cloned and see if the volume has
1935 * changed since then. Only do this when the "-n" flag is specified
1936 * because butc will get the cloneDate at time of dump.
1940 volImageTime(tve
->server
.sin_addr
.s_addr
, tve
->partition
,
1941 tve
->vid
, tve
->volType
, &tve
->cloneDate
);
1945 if (tve
->cloneDate
&& (tve
->cloneDate
== tve
->date
)) {
1946 afs_com_err(whoami
, 0,
1947 "Warning: Timestamp on volume %s unchanged from previous dump",
1955 printf("Would have dumped the following volumes:\n");
1957 printf("Preparing to dump the following volumes:\n");
1958 for (tve
= volsToDump
; tve
; tve
= tve
->next
) {
1959 printf("\t%s (%u)\n", tve
->name
, tve
->vid
);
1964 code
= bc_StartDmpRst(bc_globalConfig
, dumpPath
, vsName
, volsToDump
,
1965 /*destServer */ NULL
, /*destPartition */ 0,
1967 /*newExt */ NULL
, /*oldFlag */ 0,
1968 parent
, level
, bc_Dumper
, portp
, /*portCount */ 1,
1969 baseds
, doAppend
, dontExecute
);
1971 afs_com_err(whoami
, code
, "; Failed to queue dump");
1978 * terminate the backup process. Insists that that all running backup
1979 * jobs be terminated before it will quit
1984 bc_QuitCmd(struct cmd_syndesc
*as
, void *arock
)
1987 struct bc_dumpTask
*td
;
1988 extern dlqlinkT statusHead
;
1992 /* Check the status list for outstanding jobs */
1994 for (ptr
= (&statusHead
)->dlq_next
; ptr
!= &statusHead
;
1995 ptr
= ptr
->dlq_next
) {
1996 statusPtr
= (statusP
) ptr
;
1997 if (!(statusPtr
->flags
& ABORT_REQUEST
)) {
1999 afs_com_err(whoami
, 0, "Job %d still running (and not aborted)",
2000 statusPtr
->jobNumber
);
2001 afs_com_err(whoami
, 0,
2002 "You must at least 'kill' all running jobs before quitting");
2008 /* A job still being initialized (but no status structure or job number since it
2009 * has not been handed to a butc process yet)
2011 for (td
= bc_dumpTasks
, i
= 0; i
< BC_MAXSIMDUMPS
; i
++, td
++) {
2012 if (td
->flags
& BC_DI_INUSE
) {
2013 afs_com_err(whoami
, 0, "A job is still running");
2014 afs_com_err(whoami
, 0,
2015 "You must at least 'kill' all running jobs before quitting");
2021 /* close the all temp text files before quitting */
2022 for (i
= 0; i
< TB_NUM
; i
++)
2023 bc_closeTextFile(&bc_globalConfig
->configText
[i
],
2024 &bc_globalConfig
->tmpTextFileNames
[i
][0]);
2030 * Labels a tape i.e. request the tape coordinator to perform this
2034 bc_LabelTapeCmd(struct cmd_syndesc
*as
, void *arock
)
2036 char *tapename
= 0, *pname
= 0;
2041 code
= bc_UpdateHosts();
2043 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
2047 if (as
->parms
[0].items
) { /* -name */
2048 tapename
= as
->parms
[0].items
->data
;
2049 if (strlen(tapename
) >= TC_MAXTAPELEN
) {
2050 afs_com_err(whoami
, 0, "AFS tape name '%s' is too long", tapename
);
2055 if (as
->parms
[3].items
) { /* -pname */
2057 afs_com_err(whoami
, 0, "Can only specify -name or -pname");
2060 pname
= as
->parms
[3].items
->data
;
2061 if (strlen(pname
) >= TC_MAXTAPELEN
) {
2062 afs_com_err(whoami
, 0, "Permanent tape name '%s' is too long", pname
);
2067 if (as
->parms
[1].items
) {
2068 size
= bc_FloatATOI(as
->parms
[1].items
->data
);
2070 afs_com_err(whoami
, 0, "Bad syntax for tape size '%s'",
2071 as
->parms
[1].items
->data
);
2077 if (as
->parms
[2].items
) {
2078 port
= getPortOffset(as
->parms
[2].items
->data
);
2083 code
= bc_LabelTape(tapename
, pname
, size
, bc_globalConfig
, port
);
2090 * read the label on a tape
2092 * optional port number
2095 bc_ReadLabelCmd(struct cmd_syndesc
*as
, void *arock
)
2100 code
= bc_UpdateHosts();
2102 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
2106 if (as
->parms
[0].items
) {
2107 port
= getPortOffset(as
->parms
[0].items
->data
);
2112 code
= bc_ReadLabel(bc_globalConfig
, port
);
2119 * read content information from dump tapes, and if user desires,
2120 * add it to the database
2123 bc_ScanDumpsCmd(struct cmd_syndesc
*as
, void *arock
)
2126 afs_int32 dbAddFlag
= 0;
2129 code
= bc_UpdateHosts();
2131 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
2135 /* check for flag */
2136 if (as
->parms
[0].items
!= 0) { /* add scan to database */
2140 /* check for port */
2141 if (as
->parms
[1].items
) {
2142 port
= getPortOffset(as
->parms
[1].items
->data
);
2147 code
= bc_ScanDumps(bc_globalConfig
, dbAddFlag
, port
);
2151 /* bc_ParseExpiration
2154 * dates are specified as absolute or relative, the syntax is:
2155 * absolute: at %d/%d/%d [%d:%d] where [..] is optional
2156 * relative: in [%dy][%dm][%dd] where at least one component
2161 bc_ParseExpiration(struct cmd_parmdesc
*paramPtr
, afs_int32
*expType
,
2164 struct cmd_item
*itemPtr
;
2165 struct ktime_date kt
;
2166 char *dateString
= 0;
2169 *expType
= BC_NO_EXPDATE
;
2172 if (!paramPtr
->items
)
2173 ERROR(0); /* no expiration specified */
2175 /* some form of expiration date specified. First validate the prefix */
2176 itemPtr
= paramPtr
->items
;
2178 if (strcmp(itemPtr
->data
, "at") == 0) {
2179 *expType
= BC_ABS_EXPDATE
;
2181 dateString
= concatParams(itemPtr
->next
);
2185 code
= ktime_DateToLong(dateString
, expDate
);
2188 } else if (strcmp(itemPtr
->data
, "in") == 0) {
2189 *expType
= BC_REL_EXPDATE
;
2191 dateString
= concatParams(itemPtr
->next
);
2195 code
= ParseRelDate(dateString
, &kt
);
2198 *expDate
= ktimeRelDate_ToLong(&kt
);
2200 dateString
= concatParams(itemPtr
);
2204 if (ktime_DateToLong(dateString
, expDate
) == 0) {
2205 *expType
= BC_ABS_EXPDATE
;
2206 code
= ktime_DateToLong(dateString
, expDate
);
2209 } else if (ParseRelDate(dateString
, &kt
) == 0) {
2210 *expType
= BC_REL_EXPDATE
;
2211 *expDate
= ktimeRelDate_ToLong(&kt
);
2223 /* database lookup command and routines */
2226 * Currently a single option, volumename to search for. Reports
2227 * all dumps containing the specified volume
2230 bc_dblookupCmd(struct cmd_syndesc
*as
, void *arock
)
2232 struct cmd_item
*ciptr
;
2235 ciptr
= as
->parms
[0].items
;
2236 if (ciptr
== 0) /* no argument specified */
2239 code
= DBLookupByVolume(ciptr
->data
);
2245 /* for ubik version */
2247 bc_dbVerifyCmd(struct cmd_syndesc
*as
, void *arock
)
2253 struct hostent
*hostPtr
;
2257 extern struct udbHandleS udbHandle
;
2259 detail
= (as
->parms
[0].items
? 1 : 0); /* print more details */
2262 ubik_BUDB_DbVerify(udbHandle
.uh_client
, 0, &status
, &orphans
,
2266 afs_com_err(whoami
, code
, "; Unable to verify database");
2270 /* verification call succeeded */
2273 printf("Database OK\n");
2275 afs_com_err(whoami
, status
, "; Database is NOT_OK");
2278 printf("Orphan blocks %d\n", orphans
);
2281 printf("Unable to lookup host id\n");
2283 hostPtr
= gethostbyaddr((char *)&host
, sizeof(host
), AF_INET
);
2285 printf("Database checker was %d.%d.%d.%d\n",
2286 ((host
& 0xFF000000) >> 24), ((host
& 0xFF0000) >> 16),
2287 ((host
& 0xFF00) >> 8), (host
& 0xFF));
2289 printf("Database checker was %s\n", hostPtr
->h_name
);
2292 return ((status
? -1 : 0));
2296 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2299 deleteDump(afs_uint32 dumpid
, /* The dumpid to delete */
2300 afs_int32 port
, /* port==-1 means don't go to butc */
2303 afs_int32 code
= 0, tcode
;
2304 struct budb_dumpEntry dumpEntry
;
2305 struct rx_connection
*tconn
= 0;
2306 afs_int32 i
, taskflag
, xbsadump
;
2307 statusP statusPtr
= 0;
2308 budb_dumpsList dumps
;
2311 /* If the port is set, we will try to send a delete request to the butc */
2313 tcode
= bc_UpdateHosts();
2315 afs_com_err(whoami
, tcode
, "; Can't retrieve tape hosts");
2319 /* Find the dump in the backup database */
2320 tcode
= bcdb_FindDumpByID(dumpid
, &dumpEntry
);
2322 afs_com_err(whoami
, tcode
, "; Unable to locate dumpID %u in database",
2326 xbsadump
= (dumpEntry
.flags
& (BUDB_DUMP_ADSM
| BUDB_DUMP_BUTA
));
2328 /* If dump is to an XBSA server, connect to butc and send it
2329 * the dump to delete. Butc will contact the XBSA server.
2330 * The dump will not be an appended dump because XBSA butc
2331 * does not support the append option.
2333 if (xbsadump
&& dumpEntry
.nVolumes
) {
2334 tcode
= ConnectButc(bc_globalConfig
, port
, &tconn
);
2338 tcode
= TC_DeleteDump(tconn
, dumpid
, &taskId
);
2340 if (tcode
== RXGEN_OPCODE
)
2341 tcode
= BC_VERSIONFAIL
;
2342 afs_com_err(whoami
, tcode
,
2343 "; Unable to delete dumpID %u via butc", dumpid
);
2347 statusPtr
= createStatusNode();
2349 statusPtr
->taskId
= taskId
;
2350 statusPtr
->port
= port
;
2351 statusPtr
->jobNumber
= bc_jobNumber();
2352 statusPtr
->flags
|= (SILENT
| NOREMOVE
); /* No msg & keep statusPtr */
2353 statusPtr
->flags
&= ~STARTING
; /* clearstatus to examine */
2354 sprintf(statusPtr
->taskName
, "DeleteDump");
2357 /* Wait for task to finish */
2358 taskflag
= waitForTask(taskId
);
2359 if (taskflag
& (TASK_ERROR
| ABORT_DONE
)) {
2360 afs_com_err(whoami
, BUTX_DELETEOBJFAIL
,
2361 "; Unable to delete dumpID %u via butc", dumpid
);
2362 ERROR(BUTX_DELETEOBJFAIL
); /* the task failed */
2369 deleteStatusNode(statusPtr
); /* Clean up statusPtr - because NOREMOVE */
2371 rx_DestroyConnection(tconn
); /* Destroy the connection */
2373 /* Remove the dump from the backup database */
2374 if (!code
|| force
) {
2375 dumps
.budb_dumpsList_len
= 0;
2376 dumps
.budb_dumpsList_val
= 0;
2378 tcode
= bcdb_deleteDump(dumpid
, 0, 0, &dumps
);
2380 afs_com_err(whoami
, tcode
,
2381 "; Unable to delete dumpID %u from database", dumpid
);
2382 dumps
.budb_dumpsList_len
= 0;
2387 /* Display the dumps that were deleted - includes appended dumps */
2388 for (i
= 0; i
< dumps
.budb_dumpsList_len
; i
++)
2389 printf(" %u%s\n", dumps
.budb_dumpsList_val
[i
],
2390 (i
> 0) ? " Appended Dump" : NULL
);
2391 if (dumps
.budb_dumpsList_val
)
2392 free(dumps
.budb_dumpsList_val
);
2399 * Delete a specified dump from the database
2401 * dump id - single required arg as param 0.
2404 bc_deleteDumpCmd(struct cmd_syndesc
*as
, void *arock
)
2408 afs_int32 rcode
= 0;
2409 afs_int32 groupId
= 0, havegroupid
, sflags
, noexecute
;
2410 struct cmd_item
*ti
;
2411 afs_int32 fromTime
= 0, toTime
= 0, havetime
= 0;
2413 budb_dumpsList dumps
, flags
;
2415 afs_int32 port
= -1, force
;
2417 /* Must specify at least one of -dumpid, -from, or -to */
2418 if (!as
->parms
[0].items
&& !as
->parms
[1].items
&& !as
->parms
[2].items
2419 && !as
->parms
[4].items
) {
2420 afs_com_err(whoami
, 0, "Must specify at least one field");
2424 /* Must have -to option with -from option */
2425 if (as
->parms
[1].items
&& !as
->parms
[2].items
) {
2426 afs_com_err(whoami
, 0, "Must specify '-to' field with '-from' field");
2430 /* Get the time to delete from */
2431 if (as
->parms
[1].items
) { /* -from */
2432 timeString
= concatParams(as
->parms
[1].items
);
2437 * Now parse this string for the time to start.
2439 code
= ktime_DateToLong(timeString
, &fromTime
);
2442 afs_com_err(whoami
, 0, "Can't parse 'from' date and time");
2443 afs_com_err(whoami
, 0, "%s", ktime_GetDateUsage());
2449 port
= (as
->parms
[3].items
? getPortOffset(as
->parms
[3].items
->data
) : 0); /* -portoffset */
2450 if (as
->parms
[5].items
) /* -dbonly */
2453 force
= (as
->parms
[6].items
? 1 : 0);
2455 havegroupid
= (as
->parms
[4].items
? 1 : 0);
2457 groupId
= atoi(as
->parms
[4].items
->data
);
2459 if (as
->parms
[7].items
|| as
->parms
[8].items
) {
2460 /* -noexecute (hidden) or -dryrun used */
2466 /* Get the time to delete to */
2467 if (as
->parms
[2].items
) { /* -to */
2468 timeString
= concatParams(as
->parms
[2].items
);
2473 * Now parse this string for the time to start. Simce
2474 * times are at minute granularity, add 59 seconds.
2476 code
= ktime_DateToLong(timeString
, &toTime
);
2479 afs_com_err(whoami
, 0, "Can't parse 'to' date and time");
2480 afs_com_err(whoami
, 0, "%s", ktime_GetDateUsage());
2487 if (fromTime
> toTime
) {
2488 afs_com_err(whoami
, 0,
2489 "'-from' date/time cannot be later than '-to' date/time");
2493 /* Remove speicific dump ids - if any */
2494 printf("The following dumps %s deleted:\n",
2495 (noexecute
? "would have been" : "were"));
2496 for (ti
= as
->parms
[0].items
; ti
!= 0; ti
= ti
->next
) { /* -dumpid */
2497 dumpid
= atoi(ti
->data
);
2499 code
= deleteDump(dumpid
, port
, force
);
2501 printf(" %u\n", dumpid
);
2506 * Now remove dumps between to and from dates.
2508 if (havegroupid
|| havetime
) {
2509 dumps
.budb_dumpsList_len
= 0;
2510 dumps
.budb_dumpsList_val
= 0;
2511 flags
.budb_dumpsList_len
= 0;
2512 flags
.budb_dumpsList_val
= 0;
2515 sflags
|= BUDB_OP_GROUPID
;
2517 sflags
|= BUDB_OP_DATES
;
2520 bcdb_listDumps(sflags
, groupId
, fromTime
, toTime
, &dumps
, &flags
);
2522 afs_com_err(whoami
, code
,
2523 "; Error while deleting dumps from %u to %u", fromTime
,
2528 for (i
= 0; i
< dumps
.budb_dumpsList_len
; i
++) {
2529 if (flags
.budb_dumpsList_val
[i
] & BUDB_OP_DBDUMP
)
2533 if (flags
.budb_dumpsList_val
[i
] & BUDB_OP_APPDUMP
)
2535 code
= deleteDump(dumps
.budb_dumpsList_val
[i
], port
, force
);
2536 /* Ignore code and continue */
2538 printf(" %u%s%s\n", dumps
.budb_dumpsList_val
[i
],
2540 budb_dumpsList_val
[i
] & BUDB_OP_APPDUMP
) ?
2541 " Appended Dump" : "",
2543 budb_dumpsList_val
[i
] & BUDB_OP_DBDUMP
) ?
2544 " Database Dump" : "");
2549 if (dumps
.budb_dumpsList_val
)
2550 free(dumps
.budb_dumpsList_val
);
2551 dumps
.budb_dumpsList_len
= 0;
2552 dumps
.budb_dumpsList_val
= 0;
2553 if (flags
.budb_dumpsList_val
)
2554 free(flags
.budb_dumpsList_val
);
2555 flags
.budb_dumpsList_len
= 0;
2556 flags
.budb_dumpsList_val
= 0;
2563 bc_saveDbCmd(struct cmd_syndesc
*as
, void *arock
)
2565 struct rx_connection
*tconn
;
2566 afs_int32 portOffset
= 0;
2573 code
= bc_UpdateHosts();
2575 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
2579 if (as
->parms
[0].items
) {
2580 portOffset
= getPortOffset(as
->parms
[0].items
->data
);
2585 /* Get the time to delete to */
2586 if (as
->parms
[1].items
) {
2587 timeString
= concatParams(as
->parms
[1].items
);
2592 * Now parse this string for the time. Since
2593 * times are at minute granularity, add 59 seconds.
2595 code
= ktime_DateToLong(timeString
, &toTime
);
2598 afs_com_err(whoami
, 0, "Can't parse '-archive' date and time");
2599 afs_com_err(whoami
, 0, "%s", ktime_GetDateUsage());
2606 code
= ConnectButc(bc_globalConfig
, portOffset
, &tconn
);
2610 code
= TC_SaveDb(tconn
, toTime
, &taskId
);
2612 afs_com_err(whoami
, code
, "; Failed to save database");
2616 /* create status monitor block */
2617 statusPtr
= createStatusNode();
2619 statusPtr
->taskId
= taskId
;
2620 statusPtr
->port
= portOffset
;
2621 statusPtr
->jobNumber
= bc_jobNumber();
2622 statusPtr
->flags
&= ~STARTING
; /* clearstatus to examine */
2623 sprintf(statusPtr
->taskName
, "SaveDb");
2627 rx_DestroyConnection(tconn
);
2632 bc_restoreDbCmd(struct cmd_syndesc
*as
, void *arock
)
2634 struct rx_connection
*tconn
;
2635 afs_int32 portOffset
= 0;
2640 code
= bc_UpdateHosts();
2642 afs_com_err(whoami
, code
, "; Can't retrieve tape hosts");
2646 if (as
->parms
[0].items
) {
2647 portOffset
= getPortOffset(as
->parms
[0].items
->data
);
2652 code
= ConnectButc(bc_globalConfig
, portOffset
, &tconn
);
2656 code
= TC_RestoreDb(tconn
, &taskId
);
2658 afs_com_err(whoami
, code
, "; Failed to restore database");
2662 /* create status monitor block */
2663 statusPtr
= createStatusNode();
2665 statusPtr
->taskId
= taskId
;
2666 statusPtr
->port
= portOffset
;
2667 statusPtr
->jobNumber
= bc_jobNumber();
2668 statusPtr
->flags
&= ~STARTING
; /* clearstatus to examine */
2669 sprintf(statusPtr
->taskName
, "RestoreDb");
2673 rx_DestroyConnection(tconn
);
2677 /* ----------------------------------
2678 * supporting routines for database examination
2679 * ----------------------------------
2682 /* structures and defines for DBLookupByVolume */
2684 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2686 /* dumpedVol - saves interesting information so that we can print it out
2691 struct dumpedVol
*next
;
2693 afs_int32 initialDumpID
;
2694 char tapeName
[BU_MAXTAPELEN
];
2697 afs_int32 createTime
;
2698 afs_int32 incTime
; /* actually the clone time */
2701 /* -----------------------------------------
2702 * routines for examining the database
2703 * -----------------------------------------
2707 * Lookup the volumename in the backup database and print the results
2709 * volumeName - volume to lookup
2713 DBLookupByVolume(char *volumeName
)
2715 struct budb_dumpEntry dumpEntry
;
2716 struct budb_volumeEntry volumeEntry
[DBL_MAX_VOLUMES
];
2717 afs_int32 numEntries
;
2718 afs_int32 tapedumpid
;
2719 afs_int32 last
, next
;
2721 struct dumpedVol
*dvptr
= 0;
2722 struct dumpedVol
*tempPtr
= 0;
2725 char vname
[BU_MAXNAMELEN
];
2728 for (pass
= 0; pass
< 2; pass
++) {
2730 /* On second pass, search for backup volume */
2732 if (!BackupName(volumeName
)) {
2733 strcpy(vname
, volumeName
);
2734 strcat(vname
, ".backup");
2742 while (next
!= -1) { /*w */
2744 bcdb_LookupVolume(volumeName
, &volumeEntry
[0], last
, &next
,
2745 DBL_MAX_VOLUMES
, &numEntries
);
2749 /* add the volumes to the list */
2750 for (i
= 0; i
< numEntries
; i
++) { /*f */
2751 struct dumpedVol
*insPtr
, **prevPtr
;
2753 tempPtr
= calloc(1, sizeof(struct dumpedVol
));
2757 tempPtr
->incTime
= volumeEntry
[i
].clone
;
2758 tempPtr
->dumpID
= volumeEntry
[i
].dump
;
2759 strncpy(tempPtr
->tapeName
, volumeEntry
[i
].tape
,
2762 /* check if we need to null terminate it - just for safety */
2763 if (strlen(volumeEntry
[i
].tape
) >= BU_MAXTAPELEN
)
2764 tempPtr
->tapeName
[BU_MAXTAPELEN
- 1] = 0;
2766 code
= bcdb_FindDumpByID(tempPtr
->dumpID
, &dumpEntry
);
2772 tempPtr
->initialDumpID
= dumpEntry
.initialDumpID
;
2773 tempPtr
->parent
= dumpEntry
.parent
;
2774 tempPtr
->level
= dumpEntry
.level
;
2775 tempPtr
->createTime
= dumpEntry
.created
;
2777 /* add volume to list in reverse chronological order */
2781 while ((insPtr
!= 0)
2782 && (insPtr
->createTime
> tempPtr
->createTime
)
2784 prevPtr
= &insPtr
->next
;
2785 insPtr
= insPtr
->next
;
2788 /* now at the right place - insert the block */
2789 tempPtr
->next
= *prevPtr
;
2799 ("DumpID lvl parentID creation date clone date tape name\n");
2800 for (tempPtr
= dvptr
; tempPtr
; tempPtr
= tempPtr
->next
) {
2801 /* For the user, the tape name is its name and initial dump id */
2803 (tempPtr
->initialDumpID
? tempPtr
->initialDumpID
: tempPtr
->
2806 /* beware the static items in compactDateString */
2807 compactDateString(&tempPtr
->createTime
, ds
, 50);
2808 printf("%-9d %-2d %-9d %16s", tempPtr
->dumpID
, tempPtr
->level
,
2809 tempPtr
->parent
, ds
);
2810 compactDateString(&tempPtr
->incTime
, ds
, 50);
2811 printf(" %16s %s (%u)\n", ds
, tempPtr
->tapeName
, tapedumpid
);
2817 for (tempPtr
= dvptr
; tempPtr
; tempPtr
= dvptr
) {
2818 dvptr
= dvptr
->next
;
2823 afs_com_err(whoami
, code
, NULL
);
2827 /* structures for dumpInfo */
2830 struct volumeLink
*nextVolume
;
2831 struct budb_volumeEntry volumeEntry
;
2835 struct tapeLink
*nextTape
;
2836 struct budb_tapeEntry tapeEntry
;
2837 struct volumeLink
*firstVolume
;
2842 * print information about a dump and all its tapes and volumes.
2846 dumpInfo(afs_int32 dumpid
, afs_int32 detailFlag
)
2848 struct budb_dumpEntry dumpEntry
;
2849 struct tapeLink
*head
= 0;
2850 struct tapeLink
*tapeLinkPtr
, *lastTapeLinkPtr
;
2851 struct volumeLink
**link
, *volumeLinkPtr
, *lastVolumeLinkPtr
;
2854 afs_int32 last
, next
, dbTime
;
2855 afs_int32 tapedumpid
;
2863 extern struct udbHandleS udbHandle
;
2866 lastTapeLinkPtr
= 0;
2868 lastVolumeLinkPtr
= 0;
2870 /* first get information about the dump */
2872 code
= bcdb_FindDumpByID(dumpid
, &dumpEntry
);
2876 /* For the user, the tape name is its name and initial dump id */
2877 tapedumpid
= (dumpEntry
.initialDumpID
? dumpEntry
.initialDumpID
: dumpid
);
2879 /* Is this a database dump id or not */
2880 if (strcmp(dumpEntry
.name
, DUMP_TAPE_NAME
) == 0)
2885 /* print out the information about the dump */
2889 printDumpEntry(&dumpEntry
);
2891 time_t t
= dumpEntry
.created
;
2893 printf("Dump: id %u, created: %s\n", dumpEntry
.id
,
2896 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2897 dumpEntry
.id
, dumpEntry
.level
, dumpEntry
.nVolumes
,
2901 if (!detailFlag
&& (strlen(dumpEntry
.tapes
.tapeServer
) > 0)
2902 && (dumpEntry
.flags
& (BUDB_DUMP_ADSM
| BUDB_DUMP_BUTA
))) {
2903 printf("Backup Service: TSM: Server: %s\n",
2904 dumpEntry
.tapes
.tapeServer
);
2907 /* now get the list of tapes */
2908 for (tapeNumber
= dumpEntry
.tapes
.b
; tapeNumber
<= dumpEntry
.tapes
.maxTapes
; tapeNumber
++) { /*f */
2909 tapeLinkPtr
= calloc(1, sizeof(struct tapeLink
));
2911 afs_com_err(whoami
, BC_NOMEM
, NULL
);
2915 code
= bcdb_FindTapeSeq(dumpid
, tapeNumber
, &tapeLinkPtr
->tapeEntry
);
2922 /* add this tape to previous chain */
2923 if (lastTapeLinkPtr
) {
2924 lastTapeLinkPtr
->nextTape
= tapeLinkPtr
;
2925 lastTapeLinkPtr
= tapeLinkPtr
;
2930 lastTapeLinkPtr
= head
;
2934 while (next
!= -1) { /*wn */
2935 vl
.budb_volumeList_len
= 0;
2936 vl
.budb_volumeList_val
= 0;
2939 /* now get all the volumes in this dump. */
2940 code
= ubik_Call_SingleServer(BUDB_GetVolumes
, udbHandle
.uh_client
, UF_SINGLESERVER
, BUDB_MAJORVERSION
, BUDB_OP_DUMPID
| BUDB_OP_TAPENAME
, tapeLinkPtr
->tapeEntry
.name
, /* tape name */
2941 dumpid
, /* dumpid (not initial dumpid) */
2944 &next
, /* nextindex */
2945 &dbTime
, /* update time */
2949 if (code
== BUDB_ENDOFLIST
) { /* 0 volumes on tape */
2956 for (i
= 0; i
< vl
.budb_volumeList_len
; i
++) {
2957 link
= &tapeLinkPtr
->firstVolume
;
2959 volumeLinkPtr
= calloc(1, sizeof(struct volumeLink
));
2960 if (!volumeLinkPtr
) {
2961 afs_com_err(whoami
, BC_NOMEM
, NULL
);
2965 memcpy(&volumeLinkPtr
->volumeEntry
,
2966 &vl
.budb_volumeList_val
[i
],
2967 sizeof(struct budb_volumeEntry
));
2969 /* now insert it onto the right place */
2971 && (volumeLinkPtr
->volumeEntry
.position
>
2972 (*link
)->volumeEntry
.position
)) {
2973 link
= &((*link
)->nextVolume
);
2976 /* now link it in */
2977 volumeLinkPtr
->nextVolume
= *link
;
2978 *link
= volumeLinkPtr
;
2981 if (vl
.budb_volumeList_val
)
2982 free(vl
.budb_volumeList_val
);
2986 for (tapeLinkPtr
= head
; tapeLinkPtr
; tapeLinkPtr
= tapeLinkPtr
->nextTape
) {
2990 printTapeEntry(&tapeLinkPtr
->tapeEntry
);
2992 printf("Tape: name %s (%u)\n", tapeLinkPtr
->tapeEntry
.name
,
2994 printf("nVolumes %d, ", tapeLinkPtr
->tapeEntry
.nVolumes
);
2995 compactDateString(&tapeLinkPtr
->tapeEntry
.written
, ds
, 50);
2996 printf("created %16s", ds
);
2997 if (tapeLinkPtr
->tapeEntry
.expires
!= 0) {
2998 compactDateString(&tapeLinkPtr
->tapeEntry
.expires
, ds
, 50);
2999 printf(", expires %16s", ds
);
3004 /* print out all the volumes */
3006 /* print header for volume listing - db dumps have no volumes */
3007 if ((detailFlag
== 0) && !dbDump
)
3008 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes",
3011 for (volumeLinkPtr
= tapeLinkPtr
->firstVolume
; volumeLinkPtr
;
3012 volumeLinkPtr
= volumeLinkPtr
->nextVolume
) {
3014 printf("\nVolume\n");
3016 printVolumeEntry(&volumeLinkPtr
->volumeEntry
);
3018 compactDateString(&volumeLinkPtr
->volumeEntry
.clone
, ds
, 50),
3019 printf("%4d %s %10u %16s\n",
3020 volumeLinkPtr
->volumeEntry
.position
, ds
,
3021 volumeLinkPtr
->volumeEntry
.nBytes
,
3022 volumeLinkPtr
->volumeEntry
.name
);
3029 afs_com_err("dumpInfo", code
, "; Can't get dump information");
3031 /* free all allocated structures */
3033 while (tapeLinkPtr
) {
3034 volumeLinkPtr
= tapeLinkPtr
->firstVolume
;
3035 while (volumeLinkPtr
) {
3036 lastVolumeLinkPtr
= volumeLinkPtr
;
3037 volumeLinkPtr
= volumeLinkPtr
->nextVolume
;
3038 free(lastVolumeLinkPtr
);
3041 lastTapeLinkPtr
= tapeLinkPtr
;
3042 tapeLinkPtr
= tapeLinkPtr
->nextTape
;
3043 free(lastTapeLinkPtr
);
3049 compareDump(struct budb_dumpEntry
*ptr1
, struct budb_dumpEntry
*ptr2
)
3051 if (ptr1
->created
< ptr2
->created
)
3053 else if (ptr1
->created
> ptr2
->created
)
3059 printRecentDumps(int ndumps
)
3062 afs_int32 nextindex
, index
= 0;
3065 struct budb_dumpEntry
*dumpPtr
;
3069 extern struct udbHandleS udbHandle
;
3071 do { /* while (nextindex != -1) */
3072 /* initialize the dump list */
3073 dl
.budb_dumpList_len
= 0;
3074 dl
.budb_dumpList_val
= 0;
3076 /* outline algorithm */
3077 code
= ubik_BUDB_GetDumps(udbHandle
.uh_client
, 0, BUDB_MAJORVERSION
, BUDB_OP_NPREVIOUS
, "", /* no name */
3081 &nextindex
, &dbTime
, &dl
);
3083 if (code
== BUDB_ENDOFLIST
)
3085 afs_com_err("dumpInfo", code
, "; Can't get dump information");
3089 /* No need to sort, it's already sorted */
3091 if (dl
.budb_dumpList_len
&& (index
== 0))
3092 printf("%10s %10s %2s %-16s %2s %5s dump name\n", "dumpid",
3093 "parentid", "lv", "created", "nt", "nvols");
3095 dumpPtr
= dl
.budb_dumpList_val
;
3096 for (i
= 1; i
<= dl
.budb_dumpList_len
; i
++) {
3097 compactDateString(&dumpPtr
->created
, ds
, 50),
3098 printf("%10u %10u %-2d %16s %2d %5d %s", dumpPtr
->id
,
3099 dumpPtr
->parent
, dumpPtr
->level
, ds
,
3100 dumpPtr
->tapes
.maxTapes
- dumpPtr
->tapes
.b
+ 1,
3101 dumpPtr
->nVolumes
, dumpPtr
->name
);
3102 if (dumpPtr
->initialDumpID
) /* an appended dump */
3103 printf(" (%u)", dumpPtr
->initialDumpID
);
3104 else if (dumpPtr
->appendedDumpID
) /* has appended dumps */
3105 printf(" (%u)", dumpPtr
->id
);
3111 if (dl
.budb_dumpList_val
)
3112 free(dl
.budb_dumpList_val
);
3114 } while (nextindex
!= -1);
3120 * list the dumps and contens of the dumps.
3126 bc_dumpInfoCmd(struct cmd_syndesc
*as
, void *arock
)
3129 afs_int32 detailFlag
;
3133 if (as
->parms
[0].items
) {
3134 if (as
->parms
[1].items
) {
3135 afs_com_err(whoami
, 0,
3136 "These options are exclusive - select only one");
3139 ndumps
= atoi(as
->parms
[0].items
->data
);
3141 afs_com_err(whoami
, 0, "Must provide a positive number");
3145 code
= printRecentDumps(ndumps
);
3146 } else if (as
->parms
[1].items
) {
3147 detailFlag
= (as
->parms
[2].items
? 1 : 0); /* 1 = detailed listing */
3148 dumpid
= atoi(as
->parms
[1].items
->data
);
3149 code
= dumpInfo(dumpid
, detailFlag
);
3151 code
= printRecentDumps(10);