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 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
27 #include <afs/com_err.h>
30 #include <afs/bubasics.h> /* PA */
31 #include <afs/volser.h>
32 #include <afs/voldefs.h> /* PA */
33 #include <afs/vldbint.h> /* PA */
34 #include <afs/ktime.h> /* PA */
40 #include <afs/tcdata.h>
43 #include "error_macros.h"
46 extern struct bc_config *bc_globalConfig;
47 extern char *ktime_GetDateUsage();
48 extern struct bc_dumpSchedule *bc_FindDumpSchedule();
49 extern struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *cfg, char *name);
50 extern struct bc_dumpTask bc_dumpTasks[BC_MAXSIMDUMPS];
51 extern struct ubik_client *cstruct;
52 extern int bc_Dumper(); /* function to do dumps */
53 extern int bc_Restorer(); /* function to do restores */
54 extern void bc_HandleMisc();
55 extern afs_int32 ScanDumpHdr();
56 extern afs_int32 ScanTapeVolume();
58 extern int VL_ListEntry();
59 extern int VL_ListAttributesN2();
60 extern struct ktc_token ttoken;
61 extern char *tailCompPtr();
62 extern statusP createStatusNode();
65 extern afs_int32 lastTaskCode;
68 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.S_un.S_addr
70 #define HOSTADDR(sockaddr) (sockaddr)->sin_addr.s_addr
73 int bc_EvalVolumeSet(aconfig, avs, avols, uclient)
74 struct bc_config *aconfig;
75 register struct bc_volumeSet *avs;
76 struct bc_volumeDump **avols;
77 struct ubik_client *uclient;
78 { /*bc_EvalVolumeSet*/
81 static afs_int32 use=2;
83 if (use == 2) { /* Use EvalVolumeSet2() */
84 code = EvalVolumeSet2(aconfig, avs, avols, uclient);
85 if (code == RXGEN_OPCODE) use = 1;
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;
107 afs_int32 getSPEntries(server, partition, serverlist, ss, ps)
110 struct serversort **serverlist, **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 com_err(whoami, BC_NOMEM, "");
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 com_err(whoami, BC_NOMEM, "");
151 memset(*ps, 0, sizeof(struct partitionsort));
152 (*ps)->part = partition;
153 (*ps)->next = (*ss)->partitions;
154 (*ss)->partitions = *ps;
159 afs_int32 randSPEntries(serverlist, avols)
160 struct serversort *serverlist;
161 struct bc_volumeDump **avols;
163 struct serversort *ss, **pss, *tss;
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;
200 int EvalVolumeSet2(aconfig, avs, avols, uclient)
201 struct bc_config *aconfig;
202 struct bc_volumeSet *avs;
203 struct bc_volumeDump **avols;
204 struct ubik_client *uclient;
206 struct bc_volumeEntry *tve;
207 struct bc_volumeDump *tavols;
208 struct VldbListByAttributes attributes;
209 afs_int32 si, nsi; /* startIndex and nextStartIndex */
210 afs_int32 nentries, e, ei, et, add, l;
211 nbulkentries bulkentries;
212 struct nvldbentry *entries=0;
213 struct bc_volumeDump *tvd;
214 afs_int32 code=0, tcode;
216 struct serversort *servers=0, *lastserver=0, *ss=0, *nss;
217 struct partitionsort *ps=0, *nps;
219 *avols = (struct bc_volumeDump *) 0;
220 bulkentries.nbulkentries_len = 0;
221 bulkentries.nbulkentries_val = 0;
223 /* For each of the volume set entries - collect the volumes that match it */
224 for (tve=avs->ventries; tve; tve=tve->next) {
225 /* Put together a call to the vlserver for this vlentry. The
226 * performance gain is from letting the vlserver expand the
227 * volumeset and not this routine.
230 if (tve->server.sin_addr.s_addr) { /* The server */
231 attributes.Mask |= VLLIST_SERVER;
232 attributes.server = tve->server.sin_addr.s_addr;
234 if (tve->partition != -1) { /* The partition */
235 attributes.Mask |= VLLIST_PARTITION;
236 attributes.partition = tve->partition;
239 /* Now make the call to the vlserver */
240 for (si=0; si != -1; si=nsi) {
242 bulkentries.nbulkentries_len = 0;
243 bulkentries.nbulkentries_val = 0;
245 tcode = ubik_Call(VL_ListAttributesN2, uclient, 0,
246 &attributes, tve->name, si,
247 &nentries, &bulkentries, &nsi);
248 if (tcode) ERROR(tcode);
250 /* The 3.4 vlserver has a VL_ListAttributesN2() RPC call, but
251 * it is not complete. The only way to tell if it is not complete
252 * is if et == 0 (which we check for below). Also, if the call didn't
253 * match any entries, then we don't know what version the vlserver
254 * is. In both cases, we return RXGEN_OPCODE and the calling routine
255 * will switch to the EvalVolumeSet1() call.
257 if (nentries == 0) ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
259 /* Step through each entry and add it to the list of volumes */
260 entries = bulkentries.nbulkentries_val;
261 for (e=0; e<nentries; e++) {
262 ei = entries[e].matchindex & 0xffff;
263 et = (entries[e].matchindex >> 16) & 0xffff;
265 case ITSRWVOL: {et = RWVOL; break;}
266 case ITSBACKVOL:{et = BACKVOL; break;}
267 case ITSROVOL: {et = ROVOL; break;}
268 default: ERROR(RXGEN_OPCODE); /* Use EvalVolumeSet1 */
271 /* Find server and partiton structure to hang the entry off of */
272 tcode = getSPEntries(entries[e].serverNumber[ei],
273 entries[e].serverPartition[ei],
276 com_err(whoami, tcode, "");
280 /* Detect if this entry should be added (not a duplicate).
281 * Use ps->dupvdlist and ps->vole to only search volumes from
282 * previous volume set entries.
285 if (tve != avs->ventries) {
286 l = strlen(entries[e].name);
287 if (ps->vole != tve) {
289 ps->dupvdlist = ps->vdlist;
291 for (tavols=ps->dupvdlist; add && tavols; tavols=tavols->next) {
292 if (strncmp(tavols->name, entries[e].name, l) == 0) {
293 if ( (strcmp(&entries[e].name[l], ".backup") == 0) ||
294 (strcmp(&entries[e].name[l], ".readonly") == 0) ||
295 (strcmp(&entries[e].name[l], "") == 0) )
302 /* Allocate a volume dump structure and its name */
303 tvd = (struct bc_volumeDump *) malloc(sizeof(struct bc_volumeDump));
305 com_err(whoami, BC_NOMEM, "");
308 memset(tvd, 0, sizeof(*tvd));
310 tvd->name = (char *) malloc(strlen(entries[e].name)+10);
312 com_err(whoami, BC_NOMEM, "");
317 /* Fill it in and thread onto avols list */
318 strcpy(tvd->name, entries[e].name);
319 if (et == BACKVOL) strcat(tvd->name, ".backup");
320 else if (et == ROVOL) strcat(tvd->name, ".readonly");
321 tvd->vid = entries[e].volumeId[et];
324 tvd->partition = entries[e].serverPartition[ei];
325 tvd->server.sin_addr.s_addr = entries[e].serverNumber[ei];
326 tvd->server.sin_port = 0; /* default FS port */
327 tvd->server.sin_family = AF_INET;
328 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
329 tvd->server.sin_len = sizeof(struct sockaddr_in);
332 /* String tvd off of partition struct */
333 tvd->next = ps->vdlist;
336 ps->lastvdlist = tvd;
342 /* Free memory allocated during VL call */
343 if (bulkentries.nbulkentries_val) {
344 free((char *)bulkentries.nbulkentries_val);
345 bulkentries.nbulkentries_val = 0;
351 /* Randomly link the volumedump entries together */
352 randSPEntries(servers, avols);
353 fprintf (stderr, "Total number of volumes : %u\n", count);
356 if (bulkentries.nbulkentries_val) {
357 free((char *)bulkentries.nbulkentries_val);
362 /*-----------------------------------------------------------------------------
366 * Takes the entries in a volumeset and expands them into a list of
367 * volumes. Every VLDB volume entry is looked at and compared to the
370 * When matching a VLDB volume entry to a volumeset entry,
371 * 1. If the RW volume entry matches, that RW volume is used.
372 * 2. Otherwise, if the BK volume entry matches, the BK volume is used.
373 * 3. Finally, if the RO volume entry matches, the RO volume is used.
374 * For instance: A volumeset entry of ".* .* user.t.*" will match volume
375 * "user.troy" and "user.troy.backup". The rules will use
376 * the RW volume "user.troy".
378 * When a VLDB volume entry matches a volumeset entry (be it RW, BK or RO),
379 * that volume is used and matches against any remaining volumeset entries
381 * For instance: A 1st volumeset entry ".* .* .*.backup" will match with
382 * "user.troy.backup". Its 2nd volumeset entry ".* .* .*"
383 * would have matched its RW volume "user.troy", but the first
384 * match is used and the second match isn't even done.
387 * aconfig : Global configuration info.
389 * avols : Ptr to linked list of entries describing volumes to dump.
390 * uclient : Ptr to Ubik client structure.
393 * 0 on successful volume set evaluation,
394 * Lower-level codes otherwise.
397 * Expand only the single volume set provided (avs); don't go down its chain.
401 *-----------------------------------------------------------------------------
403 int EvalVolumeSet1(aconfig, avs, avols, uclient)
404 struct bc_config *aconfig;
405 struct bc_volumeSet *avs;
406 struct bc_volumeDump **avols;
407 struct ubik_client *uclient;
409 afs_int32 code; /*Result of various calls*/
411 struct bc_volumeDump *tvd; /*Ptr to new dump instance*/
412 struct bc_volumeEntry *tve, *ctve; /*Ptr to new volume entry instance*/
413 char patt[256]; /*Composite regex; also, target string*/
414 int volType; /*Type of volume that worked */
415 afs_int32 index; /*Current VLDB entry index*/
416 afs_int32 count; /*Needed by VL_ListEntry()*/
417 afs_int32 next_index; /*Next index to list*/
418 struct vldbentry entry; /*VLDB entry*/
419 int srvpartpair; /*Loop counter: server/partition pair*/
421 int found, foundentry;
422 struct serversort *servers=0, *ss=0;
423 struct partitionsort *ps=0;
425 *avols = (struct bc_volumeDump *) 0;
426 ctve = (struct bc_volumeEntry *) 0; /* no compiled entry */
428 /* For each vldb entry.
429 * Variable next_index is set to the index of the next VLDB entry
430 * in the enumeration.
432 for (index=0; 1; index=next_index)
434 memset(&entry, 0, sizeof(entry));
435 code = ubik_Call(VL_ListEntry, /*Routine to invoke*/
436 uclient, /*Ubik client structure*/
438 index, /*Current index*/
439 &count, /*Ptr to working variable*/
440 &next_index, /*Ptr to next index value to list*/
441 &entry); /*Ptr to entry to fill*/
442 if (code) return code;
443 if (!next_index) break; /* If the next index is invalid, bail out now. */
445 /* For each entry in the volume set */
446 found = 0; /* No match in volume set yet */
447 for (tve=avs->ventries; tve; tve=tve->next)
449 /* for each server in the vldb entry */
450 for (srvpartpair=0; srvpartpair < entry.nServers; srvpartpair++)
452 /* On the same server */
453 if ( tve->server.sin_addr.s_addr &&
454 !VLDB_IsSameAddrs(tve->server.sin_addr.s_addr,
455 entry.serverNumber[srvpartpair],
457 if (code) return(code);
462 /* On the same partition */
463 if ( (tve->partition != -1) &&
464 (tve->partition != entry.serverPartition[srvpartpair]) )
467 /* If the volume entry is not compiled, then compile it */
470 sprintf(patt, "^%s$", tve->name);
471 errm = (char*)re_comp(patt);
474 com_err(whoami, 0, "Can't compile regular expression '%s': %s",
481 /* If the RW name matches the volume set entry, take
482 * it and exit. First choice is to use the RW volume.
484 if (entry.serverFlags[srvpartpair] & ITSRWVOL)
486 if (entry.flags & RW_EXISTS)
488 sprintf(patt, "%s", entry.name);
489 code = re_exec(patt);
493 foundentry = srvpartpair;
499 /* If the BK name matches the volume set entry, take
500 * it and exit. Second choice is to use the BK volume.
502 if (entry.flags & BACK_EXISTS)
504 sprintf(patt, "%s.backup", entry.name);
505 code = re_exec(patt);
509 foundentry = srvpartpair;
516 /* If the RO name matches the volume set entry, remember
517 * it, but continue searching. Further entries may be
518 * RW or backup entries that will match.
521 (entry.serverFlags[srvpartpair] & ITSROVOL) &&
522 (entry.flags & RO_EXISTS) )
524 sprintf(patt, "%s.readonly", entry.name);
525 code = re_exec(patt);
529 foundentry = srvpartpair;
535 com_err(whoami, 0, "Internal error in regex package");
538 /* If found a match, then create a new volume dump entry */
541 /* Find server and partition structure to hang the entry off of */
542 code = getSPEntries(entry.serverNumber[foundentry],
543 entry.serverPartition[foundentry],
546 com_err(whoami, code, "");
551 tvd = (struct bc_volumeDump *) malloc(sizeof(struct bc_volumeDump));
554 com_err(whoami, BC_NOMEM,"");
557 memset(tvd, 0, sizeof(*tvd));
559 tvd->name = (char *) malloc(strlen(entry.name)+10);
562 com_err(whoami, BC_NOMEM, "");
567 strcpy(tvd->name, entry.name);
568 if (volType == BACKVOL) strcat(tvd->name, ".backup");
569 else if (volType == ROVOL) strcat(tvd->name, ".readonly");
570 tvd->vid = entry.volumeId[volType];
572 tvd->volType = volType;
573 tvd->partition = entry.serverPartition[foundentry];
574 tvd->server.sin_addr.s_addr = entry.serverNumber[foundentry];
575 tvd->server.sin_port = 0; /* default FS port */
576 tvd->server.sin_family = AF_INET;
578 /* String tvd off of partition struct */
579 tvd->next = ps->vdlist;
582 ps->lastvdlist = tvd;
589 /* Randomly link the volumedump entries together */
590 randSPEntries(servers, avols);
592 fprintf (stderr, "Total number of volumes : %u\n", total);
597 * print out a date in compact format, 16 chars, format is
600 * date_long - ptr to a long containing the time
602 * ptr to a string containing a representation of the date
604 char *compactDateString(date_long, string, size)
605 afs_int32 *date_long, size;
610 if (!string) return 0;
612 if (*date_long == NEVERDATE) {
613 sprintf(string, "NEVER");
615 ltime = localtime(date_long);
616 /* prints date in U.S. format of mm/dd/yyyy */
617 strftime (string, size, "%m/%d/%Y %H:%M", ltime);
622 afs_int32 bc_SafeATOI(anum)
627 for (; *anum; anum++) {
628 if ((*anum < '0') || (*anum > '9')) return -1;
629 total = (10 * total) + (afs_int32)(*anum - '0');
635 * Take a string and parse it for a number (could be float) followed
636 * by a character representing the units (K,M,G,T). Default is 'K'.
637 * Return the size in KBytes.
639 afs_int32 bc_FloatATOI(anum)
644 afs_int32 fraction = 0; /* > 0 if past the decimal */
646 for (; *anum; anum++) {
647 if ((*anum == 't') || (*anum == 'T')) {
648 total *= 1024*1024*1024;
651 if ((*anum == 'g') || (*anum == 'G')) {
655 if ((*anum == 'm') || (*anum == 'M')) {
659 if ((*anum == 'k') || (*anum == 'K')) {
666 if ((*anum < '0') || (*anum > '9')) return -1;
669 total = (10. * total) + (float)(*anum - '0');
671 total += ((float)(*anum - '0')) / (float)fraction;
676 total += 0.5; /* Round up */
677 if (total > 0x7fffffff) /* Don't go over 2G */
679 rtotal = (afs_int32) total;
683 /* make a copy of a string so that it can be freed later */
684 char *bc_CopyString(astring)
690 if (!astring) return((char *) 0); /* propagate null strings easily */
691 tlen = strlen(astring);
692 tp = (char *) malloc(tlen+1); /* don't forget the terminating null */
695 com_err(whoami,BC_NOMEM,"");
704 * Concatenates the parameters of an option and returns the string.
708 char *concatParams(itemPtr)
709 struct cmd_item *itemPtr;
711 struct cmd_item *tempPtr;
712 afs_int32 length = 0;
715 /* compute the length of string required */
716 for (tempPtr = itemPtr; tempPtr; tempPtr = tempPtr->next)
718 length += strlen(tempPtr->data);
719 length++; /* space or null terminator */
722 if ( length == 0 ) /* no string (0 length) */
724 com_err(whoami, 0, "Can't have zero length date and time string");
728 string = (char *) malloc(length); /* allocate the string */
731 com_err(whoami,BC_NOMEM,"");
736 tempPtr = itemPtr; /* now assemble the string */
739 strcat(string, tempPtr->data);
740 tempPtr = tempPtr->next;
741 if ( tempPtr ) strcat(string, " ");
744 return(string); /* return the string */
748 * print out an interface status node as received from butc
751 printIfStatus(statusPtr)
752 struct tciStatusS *statusPtr;
754 printf("Task %d: %s: ", statusPtr->taskId, statusPtr->taskName);
755 if ( statusPtr->nKBytes )
756 printf("%ld Kbytes transferred", statusPtr->nKBytes);
757 if ( strlen(statusPtr->volumeName) != 0 )
759 if ( statusPtr->nKBytes ) printf(", ");
760 printf("volume %s", statusPtr->volumeName);
765 if ( statusPtr->flags & ABORT_REQUEST )
766 printf(" [abort request rcvd]");
768 if ( statusPtr->flags & ABORT_DONE )
769 printf(" [abort complete]");
771 if ( statusPtr->flags & OPR_WAIT )
772 printf(" [operator wait]");
774 if ( statusPtr->flags & CALL_WAIT )
775 printf(" [callout in progress]");
777 if ( statusPtr->flags & DRIVE_WAIT )
778 printf(" [drive wait]");
780 if ( statusPtr->flags & TASK_DONE )
785 afs_int32 getPortOffset(port)
788 afs_int32 portOffset;
790 portOffset = bc_SafeATOI(port);
792 if (portOffset < 0) {
793 com_err(whoami,0,"Can't decode port offset '%s'", port);
796 else if (portOffset > BC_MAXPORTOFFSET) {
797 com_err(whoami,0,"%u exceeds max port offset %u",portOffset,BC_MAXPORTOFFSET);
803 /* bc_GetTapeStatusCmd
804 * display status of all tasks on a particular tape coordinator
807 bc_GetTapeStatusCmd(as, arock)
808 struct cmd_syndesc *as;
812 afs_int32 index, dumpID;
813 struct rx_connection *tconn;
814 afs_int32 portOffset = 0;
819 struct tciStatusS status;
821 code = bc_UpdateHosts();
823 com_err(whoami, code, "; Can't retrieve tape hosts");
827 if (as->parms[0].items) {
828 portOffset = getPortOffset(as->parms[0].items->data);
829 if (portOffset < 0) return(BC_BADARG);
832 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
833 if (code) return(code);
835 flags = TSK_STAT_FIRST;
839 while ( (flags & TSK_STAT_END) == 0 )
841 code = TC_ScanStatus(tconn, &taskId, &status, &flags);
844 if (code == TC_NOTASKS) break;
845 com_err(whoami, code, "; Can't get status from butc");
848 if ( (flags & TSK_STAT_NOTFOUND) ) break; /* Can't find the task id */
849 flags &= ~TSK_STAT_FIRST; /* turn off flag */
851 printIfStatus(&status);
856 printf("Tape coordinator is idle\n");
858 if (flags & TSK_STAT_ADSM)
859 printf("TSM Tape coordinator\n");
860 else if (flags & TSK_STAT_XBSA)
861 printf("XBSA Tape coordinator\n");
866 extern struct Lock dispatchLock;
869 * wait for all jobs to terminate
876 int usefulJobRunning = 1;
878 extern dlqlinkT statusHead;
882 com_err(whoami, 0, "waiting for job termination");
884 while (usefulJobRunning)
886 usefulJobRunning = (dlqEmpty(&statusHead) ? 0 : 1);
887 if (dispatchLock.excl_locked) usefulJobRunning = 1;
888 for (i=0; (!usefulJobRunning && (i<BC_MAXSIMDUMPS)); i++)
890 if (bc_dumpTasks[i].flags & BC_DI_INUSE)
891 usefulJobRunning = 1;
894 /* Wait 5 seconds and check again */
895 if (usefulJobRunning) IOMGR_Sleep(5);
897 return(lastTaskCode);
901 * print status on running jobs
903 * ignored - a null "as" prints only jobs.
906 bc_JobsCmd(as, arock)
907 struct cmd_syndesc *as;
912 struct bc_dumpTask *td;
919 extern dlqlinkT statusHead;
921 dlqInit(&atJobsHead);
924 ptr = (&statusHead)->dlq_next;
925 while ( ptr != &statusHead )
927 statusPtr = (statusP) ptr;
930 if ( statusPtr->scheduledDump )
932 dlqUnlink((dlqlinkP)statusPtr);
933 dlqLinkb(&atJobsHead, (dlqlinkP)statusPtr);
937 printf("Job %d:", statusPtr->jobNumber);
939 printf(" %s", statusPtr->taskName);
941 if ( statusPtr->dbDumpId )
942 printf(": DumpID %u", statusPtr->dbDumpId);
943 if ( statusPtr->nKBytes )
944 printf(", %ld Kbytes", statusPtr->nKBytes);
945 if ( strlen(statusPtr->volumeName) != 0 )
946 printf(", volume %s", statusPtr->volumeName);
948 if ( statusPtr->flags & CONTACT_LOST )
949 printf(" [butc contact lost]");
951 if ( statusPtr->flags & ABORT_REQUEST )
952 printf(" [abort request]");
954 if ( statusPtr->flags & ABORT_SENT )
955 printf(" [abort sent]");
957 if ( statusPtr->flags & OPR_WAIT )
958 printf(" [operator wait]");
960 if ( statusPtr->flags & CALL_WAIT )
961 printf(" [callout in progress]");
963 if ( statusPtr->flags & DRIVE_WAIT )
964 printf(" [drive wait]");
970 * Now print the scheduled dumps.
972 if ( !dlqEmpty(&statusHead) && as )
973 printf("\n"); /* blank line between running and scheduled dumps */
976 while ( !dlqEmpty(&atJobsHead) )
978 ptr = (&atJobsHead) -> dlq_next;
979 youngest = (statusP) ptr;
982 while ( ptr != &atJobsHead ) /* Find the dump that starts the earliest */
984 statusPtr = (statusP) ptr;
985 if ( statusPtr->scheduledDump < youngest->scheduledDump )
986 youngest = statusPtr;
990 /* Print token expiration time */
991 if ( (ttoken.endTime > prevTime) && (ttoken.endTime <= youngest->scheduledDump) &&
992 as && (ttoken.endTime != NEVERDATE))
994 if (ttoken.endTime > time(0)) {
995 compactDateString(&ttoken.endTime, ds, 50);
996 printf(" %16s: TOKEN EXPIRATION\n", ds);
998 printf(" TOKEN HAS EXPIRED\n");
1001 prevTime = youngest->scheduledDump;
1003 /* Print the info */
1004 compactDateString(&youngest->scheduledDump, ds, 50);
1005 printf("Job %d:", youngest->jobNumber);
1006 printf(" %16s: %s", ds, youngest->cmdLine);
1009 /* return to original list */
1010 dlqUnlink((dlqlinkP)youngest);
1011 dlqLinkb(&statusHead, (dlqlinkP)youngest);
1014 /* Print token expiration time if havn't already */
1015 if ( (ttoken.endTime == NEVERDATE) && as )
1016 printf(" : TOKEN NEVER EXPIRES\n");
1017 else if ( (ttoken.endTime > prevTime) && as )
1019 if (ttoken.endTime > time(0)) {
1020 compactDateString(&ttoken.endTime, ds, 50);
1021 printf(" %16s: TOKEN EXPIRATION\n", ds);
1023 printf(" : TOKEN HAS EXPIRED\n");
1031 bc_KillCmd(as, arock)
1032 struct cmd_syndesc *as;
1037 struct bc_dumpTask *td;
1045 extern dlqlinkT statusHead;
1046 extern statusP findStatus();
1049 tp = as->parms[0].items->data;
1050 if (strchr(tp, '.') == 0)
1052 slot = bc_SafeATOI(tp);
1055 com_err(whoami,0,"Bad syntax for number '%s'", tp);
1060 ptr = (&statusHead)->dlq_next;
1061 while ( ptr != &statusHead )
1063 statusPtr = (statusP) ptr;
1064 if ( statusPtr->jobNumber == slot )
1066 statusPtr->flags |= ABORT_REQUEST;
1070 ptr = ptr->dlq_next;
1074 fprintf(stderr, "Job %d not found\n", slot);
1081 for(i=0;i<BC_MAXSIMDUMPS;i++, td++)
1083 if (td->flags & BC_DI_INUSE)
1086 strcpy(tbuffer, td->volSetName);
1087 strcat(tbuffer, ".");
1088 strcat(tbuffer, tailCompPtr(td->dumpName));
1089 if (strcmp(tbuffer, tp) == 0)
1093 if (i >= BC_MAXSIMDUMPS)
1095 com_err(whoami,0,"Can't find job %s", tp);
1100 statusPtr = findStatus(td->dumpID);
1102 if ( statusPtr == 0 )
1104 com_err(whoami, 0, "Can't locate status - internal error");
1108 statusPtr->flags |= ABORT_REQUEST;
1115 /* restore a volume or volumes */
1116 bc_VolRestoreCmd(as, arock)
1117 struct cmd_syndesc *as;
1121 * parm 0 is the new server to restore to
1122 * parm 1 is the new partition to restore to
1123 * parm 2 is volume(s) to restore
1124 * parm 3 is the new extension, if any, for the volume name.
1125 * parm 4 gives the new volume # to restore this volume as (removed).
1126 * parm 4 date is a string representing the date
1128 * We handle four types of restores. If old is set, then we restore the
1129 * volume with the same name and ID. If old is not set, we allocate
1130 * a new volume ID for the restored volume. If a new extension is specified,
1131 * we add that extension to the volume name of the restored volume.
1133 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1134 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1135 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1136 struct bc_volumeDump *tvol; /* temp for same */
1137 struct sockaddr_in destServ; /* machine to which to restore volumes */
1138 afs_int32 destPartition; /* partition to which to restore volumes */
1140 struct cmd_item *ti;
1144 char *newExt, *timeString;
1145 afs_int32 i, portRemain;
1146 afs_int32 *ports = (afs_int32 *)0;
1147 afs_int32 portCount=0;
1150 code = bc_UpdateHosts();
1152 com_err(whoami, code, "; Can't retrieve tape hosts");
1156 /* specified other destination host */
1157 if (as->parms[0].items)
1159 tp = as->parms[0].items->data;
1160 if (bc_ParseHost(tp, &destServ))
1162 com_err(whoami,0,"Failed to locate destination host '%s'", tp);
1167 /* specified other destination partition */
1168 if (as->parms[1].items)
1170 tp = as->parms[1].items->data;
1171 if (bc_GetPartitionID(tp, &destPartition))
1173 com_err(whoami,0,"Can't parse destination partition '%s'", tp);
1178 for (ti=as->parms[2].items; ti; ti=ti->next)
1180 /* build list of volume items */
1181 tvol = (struct bc_volumeDump *) malloc(sizeof(struct bc_volumeDump));
1184 com_err(whoami,BC_NOMEM,"");
1187 memset(tvol, 0, sizeof(struct bc_volumeDump));
1189 tvol->name = (char *) malloc(VOLSER_MAXVOLNAME +1);
1192 com_err(whoami,BC_NOMEM,"");
1195 strncpy(tvol->name, ti->data,VOLSER_OLDMAXVOLNAME);
1196 tvol->entry = &tvolumeEntry;
1198 if (lastVol) lastVol->next = tvol; /* thread onto end of list */
1199 else volsToRestore = tvol;
1203 if (as->parms[4].items)
1205 timeString = concatParams(as->parms[4].items);
1206 if (!timeString) return(-1);
1208 code = ktime_DateToLong(timeString, &fromDate);
1212 com_err(whoami,0,"Can't parse restore date and time");
1213 com_err(whoami,0,"%s", ktime_GetDateUsage());
1219 fromDate = 0x7fffffff; /* latest one */
1222 newExt = (as->parms[3].items ? as->parms[3].items->data : (char *)0);
1225 /* Read all the port offsets into the ports array. The first element in the
1226 * array is for full restore and the rest are for incremental restores
1228 if (as->parms[5].items) {
1229 for (ti=as->parms[5].items; ti; ti=ti->next) portCount++;
1230 ports = (afs_int32 *)malloc(portCount*sizeof(afs_int32));
1232 com_err(whoami,BC_NOMEM,"");
1236 for (ti=as->parms[5].items, i=0; ti; ti=ti->next, i++) {
1237 ports[i] = getPortOffset(ti->data);
1238 if (ports[i] < 0) return(BC_BADARG);
1242 dontExecute = (as->parms[6].items ? 1 : 0); /* -n */
1245 * Perform the call to start the restore.
1247 code = bc_StartDmpRst(bc_globalConfig, "volume", "restore", volsToRestore, &destServ,
1248 destPartition, fromDate, newExt, oldFlag,
1249 /*parentDump*/0, /*dumpLevel*/0,
1250 bc_Restorer, ports, portCount,
1251 /*dumpSched*/0, /*append*/0, dontExecute);
1252 if (code) com_err(whoami,code,"; Failed to queue restore");
1257 /* restore a whole partition or server */
1259 /* bc_DiskRestoreCmd
1260 * restore a whole partition
1262 * first, reqd - machine (server) to restore
1263 * second, reqd - partition to restore
1267 bc_DiskRestoreCmd(as, arock)
1268 struct cmd_syndesc *as;
1270 struct bc_volumeSet tvolumeSet; /* temporary volume set for EvalVolumeSet call */
1271 struct bc_volumeEntry tvolumeEntry; /* entry within the volume set */
1272 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1273 struct sockaddr_in destServ; /* machine to which to restore volumes */
1274 afs_int32 destPartition; /* partition to which to restore volumes */
1280 afs_int32 *ports = (afs_int32 *)0;
1281 afs_int32 portCount=0;
1283 struct bc_volumeDump *prev, *tvol, *nextvol;
1284 struct cmd_item *ti;
1287 /* parm 0 is the server to restore
1288 * parm 1 is the partition to restore
1290 * parm 8 and above as in VolRestoreCmd:
1291 * parm 8 is the new server to restore to
1292 * parm 9 is the new partition to restore to
1295 code = bc_UpdateVolumeSet();
1297 com_err(whoami, code, "; Can't retrieve volume sets");
1300 code = bc_UpdateHosts();
1302 com_err(whoami, code, "; Can't retrieve tape hosts");
1306 /* create a volume set corresponding to the volume pattern we've been given */
1307 memset(&tvolumeSet, 0, sizeof(tvolumeSet));
1308 memset(&tvolumeEntry, 0, sizeof(tvolumeEntry));
1309 tvolumeSet.name = "TempVolumeSet";
1310 tvolumeSet.ventries = &tvolumeEntry;
1311 tvolumeEntry.serverName = as->parms[0].items->data;
1312 tvolumeEntry.partname = as->parms[1].items->data;
1314 if (bc_GetPartitionID(tvolumeEntry.partname, &tvolumeEntry.partition))
1316 com_err(whoami,0,"Can't parse partition '%s'", tvolumeEntry.partname);
1320 if (bc_ParseHost(tvolumeEntry.serverName, &tvolumeEntry.server))
1322 com_err(whoami,0,"Can't locate host '%s'", tvolumeEntry.serverName);
1326 /* specified other destination host */
1327 if (as->parms[8].items)
1329 tp = as->parms[8].items->data;
1330 if (bc_ParseHost(tp, &destServ))
1332 com_err(whoami,0,"Can't locate destination host '%s'", tp);
1336 else /* use destination host == original host */
1337 memcpy(&destServ, &tvolumeEntry.server, sizeof(destServ));
1339 /* specified other destination partition */
1340 if (as->parms[9].items)
1342 tp = as->parms[9].items->data;
1343 if (bc_GetPartitionID(tp, &destPartition))
1345 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) portCount++;
1356 ports = (afs_int32 *)malloc(portCount*sizeof(afs_int32));
1358 com_err(whoami,BC_NOMEM,"");
1362 for (ti=as->parms[2].items, i=0; ti; ti=ti->next, i++) {
1363 ports[i] = getPortOffset(ti->data);
1364 if (ports[i] < 0) return(BC_BADARG);
1368 newExt = (as->parms[10].items ? as->parms[10].items->data : (char *)0);
1369 dontExecute = (as->parms[11].items ? 1 : 0); /* -n */
1372 * Expand out the volume set into its component list of volumes, by calling VLDB server.
1374 code = bc_EvalVolumeSet(bc_globalConfig, &tvolumeSet, &volsToRestore, cstruct);
1377 com_err(whoami,code,"; Failed to evaluate volume set");
1381 /* Since we want only RW volumes, remove any
1382 * BK or RO volumes from the list.
1384 for (prev=0, tvol=volsToRestore; tvol; tvol=nextvol) {
1385 nextvol = tvol->next;
1386 if (BackupName(tvol->name)) {
1388 fprintf (stderr, "Will not restore volume %s (its RW does not reside on the partition)\n", tvol->name);
1390 if (tvol == volsToRestore) {
1391 volsToRestore = nextvol;
1393 prev->next = nextvol;
1395 if (tvol->name) free(tvol->name);
1402 fromDate = 0x7fffffff; /* last one before this date */
1403 oldFlag = 1; /* do restore same volid (and name) */
1406 * Perform the call to start the dump.
1408 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore, &destServ,
1409 destPartition, fromDate, newExt, oldFlag,
1410 /*parentDump*/0, /*dumpLevel*/0,
1411 bc_Restorer, ports, portCount,
1412 /*dumpSched*/0, /*append*/0, dontExecute);
1413 if (code) com_err(whoami,code,"; Failed to queue restore");
1418 /* bc_VolsetRestoreCmd
1419 * restore a volumeset or list of volumes.
1422 bc_VolsetRestoreCmd (as, arock)
1423 struct cmd_syndesc *as;
1431 afs_int32 *ports = (afs_int32 *)0;
1432 afs_int32 portCount=0;
1434 afs_int32 portoffset = 0;
1436 struct bc_volumeSet *volsetPtr; /* Ptr to list of generated volume info*/
1437 struct bc_volumeDump *volsToRestore = (struct bc_volumeDump *)0;
1438 struct bc_volumeDump *lastVol = (struct bc_volumeDump *)0;
1439 struct sockaddr_in destServer; /* machine to which to restore volume */
1440 afs_int32 destPartition; /* partition to which to restore volumes */
1441 struct cmd_item *ti;
1444 code = bc_UpdateVolumeSet();
1446 com_err(whoami, code, "; Can't retrieve volume sets");
1449 code = bc_UpdateHosts();
1451 com_err(whoami, code, "; Can't retrieve tape hosts");
1455 if (as->parms[0].items)
1457 if (as->parms[1].items)
1459 com_err(whoami, 0, "Can't have both -name and -file options");
1463 volsetName = as->parms[0].items->data;
1464 volsetPtr = bc_FindVolumeSet(bc_globalConfig, volsetName);
1467 com_err(whoami,0,"Can't find volume set '%s' in backup database", volsetName);
1471 /* Expand out the volume set into its component list of volumes. */
1472 code = bc_EvalVolumeSet(bc_globalConfig, volsetPtr, &volsToRestore, cstruct);
1475 com_err(whoami,code,"; Failed to evaluate volume set");
1479 else if (as->parms[1].items)
1483 char server[50], partition[50], volume[50], rest[256];
1485 struct bc_volumeDump *tvol;
1487 fd = fopen(as->parms[1].items->data, "r");
1490 com_err(whoami,errno,"; Cannot open file '%s'", as->parms[1].items->data);
1494 while ( fgets(line, 255, fd) )
1496 count = sscanf(line, "%s %s %s %s", server, partition, volume, rest);
1498 if (count <= 0) continue;
1502 "Invalid volumeset file format: Wrong number of arguments: Ignoring\n");
1503 fprintf(stderr, " %s", line);
1507 if ( bc_ParseHost(server,&destServer) )
1509 com_err(whoami,0,"Failed to locate host '%s'", server);
1513 if ( bc_GetPartitionID(partition,&destPartition) )
1515 com_err(whoami,0,"Failed to parse destination partition '%s'", partition);
1519 /* Allocate a volumeDump structure and link it in */
1520 tvol = (struct bc_volumeDump *) malloc(sizeof(struct bc_volumeDump));
1521 memset(tvol, 0, sizeof(struct bc_volumeDump));
1523 tvol->name = (char *) malloc(VOLSER_MAXVOLNAME+1);
1526 com_err(whoami,BC_NOMEM,"");
1529 strncpy(tvol->name, volume, VOLSER_OLDMAXVOLNAME);
1530 memcpy(&tvol->server, &destServer, sizeof(destServer));
1531 tvol->partition = destPartition;
1533 if (lastVol) lastVol->next = tvol; /* thread onto end of list */
1534 else volsToRestore = tvol;
1541 com_err(whoami,0,"-name or -file option required");
1546 /* Get the port offset for the restore */
1547 if (as->parms[2].items) {
1548 for (ti=as->parms[2].items; ti; ti=ti->next) portCount++;
1549 ports = (afs_int32 *)malloc(portCount*sizeof(afs_int32));
1551 com_err(whoami,BC_NOMEM,"");
1555 for (ti=as->parms[2].items, i=0; ti; ti=ti->next, i++) {
1556 ports[i] = getPortOffset(ti->data);
1557 if (ports[i] < 0) return(BC_BADARG);
1561 newExt = (as->parms[3].items ? as->parms[3].items->data : (char *)0);
1562 dontExecute = (as->parms[4].items ? 1 : 0);
1564 fromDate = 0x7fffffff; /* last one before this date */
1565 oldFlag = 1; /* do restore same volid (and name) */
1567 /* Perform the call to start the restore */
1568 code = bc_StartDmpRst(bc_globalConfig, "disk", "restore", volsToRestore,
1569 /*destserver*/ 0, /*destpartition*/ 0, fromDate, newExt, oldFlag,
1570 /*parentDump*/0, /*dumpLevel*/0,
1571 bc_Restorer, ports, portCount,
1572 /*dumpSched*/0, /*append*/0, dontExecute);
1573 if (code) com_err(whoami,code,"; Failed to queue restore");
1578 /*-----------------------------------------------------------------------------
1582 * Perform a dump of the set of volumes provided.
1583 * user specifies: -volumeset .. -dump .. [-portoffset ..] [-n]
1586 * as : Parsed command line information.
1587 * arock : Ptr to misc stuff; not used.
1591 * The result of bc_StartDump() otherwise
1598 *---------------------------------------------------------------------------
1602 int bc_DumpCmd(as, arock)
1603 struct cmd_syndesc *as;
1606 static char rn[] = "bc_DumpCmd"; /*Routine name*/
1607 char *dumpPath, *vsName; /*Ptrs to various names*/
1608 struct bc_volumeSet *tvs; /*Ptr to list of generated volume info*/
1609 struct bc_dumpSchedule *tds, *baseds; /*Ptr to dump schedule node*/
1610 struct bc_volumeDump *tve, *volsToDump; /*Ptr to individual vols to be dumped*/
1611 struct bc_volumeDump *ntve, *tves, *ptves, *rtves;
1612 struct budb_dumpEntry dumpEntry, de, fde; /* dump entry */
1615 afs_int32 parent; /* parent dump */
1616 afs_int32 level; /* this dump's level # */
1617 afs_int32 problemFindingDump; /* can't find parent(s) */
1619 afs_int32 *portp = (afs_int32 *)0;
1620 afs_int32 portCount = 0;
1621 afs_int32 doAt, atTime; /* Time a timed-dump is to start at */
1624 int doAppend; /* Append the dump to dump set */
1625 afs_int32 code; /* Return code */
1626 int loadfile; /* whether to load a file or not */
1628 struct bc_dumpTask *dumpTaskPtr; /* for dump thread */
1629 afs_int32 dumpTaskSlot;
1632 int r, nservers, ns, serverfound;
1634 extern struct bc_dumpTask bc_dumpTasks[];
1635 extern afs_int32 bcdb_FindLastVolClone();
1636 extern afs_int32 volImageTime();
1638 code = bc_UpdateDumpSchedule();
1640 com_err(whoami, code, "; Can't retrieve dump schedule");
1643 code = bc_UpdateVolumeSet();
1645 com_err(whoami, code, "; Can't retrieve volume sets");
1648 code = bc_UpdateHosts();
1650 com_err(whoami, code, "; Can't retrieve tape hosts");
1655 * Some parameters cannot be specified together
1656 * The "-file" option cannot exist with the "-volume", "-dump",
1657 * "-portoffset", or "-append" option
1659 if (as->parms[6].items)
1662 if ( as->parms[0].items || as->parms[1].items ||
1663 as->parms[2].items || as->parms[4].items )
1665 com_err(whoami,0,"Invalid option specified with -file option");
1672 if ( !as->parms[0].items || !as->parms[1].items )
1674 com_err(whoami,0,"Must specify volume set name and dump level name");
1680 * Get the time we are to perform this dump
1682 if (as->parms[3].items)
1686 timeString = concatParams(as->parms[3].items);
1687 if (!timeString) return(-1);
1690 * Now parse this string for the time to start.
1692 code = ktime_DateToLong(timeString, &atTime);
1696 com_err(whoami,0,"Can't parse dump start date and time");
1697 com_err(whoami,0,"%s", ktime_GetDateUsage());
1704 dontExecute = (as->parms[5].items ? 1 : 0); /* -n */
1707 * If this dump is not a load file, then check the parameters.
1711 vsName = as->parms[0].items->data; /* get volume set name */
1712 dumpPath = as->parms[1].items->data; /* get dump path */
1714 /* get the port number, if one was specified */
1715 if (as->parms[2].items)
1718 portp = (afs_int32 *)malloc(sizeof(afs_int32));
1721 com_err(whoami,BC_NOMEM,"");
1725 *portp = getPortOffset(as->parms[2].items->data);
1726 if (*portp < 0) return(BC_BADARG);
1729 doAppend = (as->parms[4].items ? 1 : 0); /* -append */
1732 * Get a hold of the given volume set and dump set.
1734 tvs = bc_FindVolumeSet(bc_globalConfig, vsName);
1737 com_err(whoami,0, "Can't find volume set '%s' in backup database", vsName);
1740 baseds = bc_FindDumpSchedule(bc_globalConfig, dumpPath);
1743 com_err(whoami,0, "Can't find dump schedule '%s' in backup database", dumpPath);
1750 * If given the "-at" option, then add this to the jobs list and return
1753 * Create a status node for this timed dump.
1754 * Fill in the time to dump and the cmd line for the dump leaving off
1755 * the -at option. If the -n option is there, it is scheduled with
1756 * the Timed dump as opposed to not scheduling the time dump at all.
1760 if ( atTime < time(0) )
1762 com_err(whoami,0,"Time of dump is earlier then current time - not added");
1766 statusPtr = createStatusNode();
1768 statusPtr->scheduledDump = atTime;
1770 /* Determine length of the dump command */
1772 length += 4; /* "dump" */
1775 length += 6; /* " -file" */
1776 length += 1 + strlen(as->parms[6].items->data); /* " <file>" */
1780 /* length += 11; */ /* " -volumeset" */
1781 length += 1 + strlen(as->parms[0].items->data); /* " <volset> */
1783 /* length += 6; */ /* " -dump" */
1784 length += 1 + strlen(as->parms[1].items->data); /* " <dumpset> */
1786 if (as->parms[2].items)
1788 /* length += 12; */ /* " -portoffset" */
1789 length += 1 + strlen(as->parms[2].items->data); /* " <port>" */
1792 if (as->parms[4].items)
1793 length += 8; /* " -append" */
1797 length += 3; /* " -n" */
1798 length++; /* end-of-line */
1800 /* Allocate status block for this timed dump */
1801 sprintf(statusPtr->taskName, "Scheduled Dump");
1802 statusPtr->jobNumber = bc_jobNumber();
1803 statusPtr->scheduledDump = atTime;
1804 statusPtr->cmdLine = (char *) malloc(length);
1805 if (!statusPtr->cmdLine)
1807 com_err(whoami,BC_NOMEM,"");
1811 /* Now reconstruct the dump command */
1812 statusPtr->cmdLine[0] = 0;
1813 strcat(statusPtr->cmdLine, "dump");
1816 strcat(statusPtr->cmdLine, " -file");
1817 strcat(statusPtr->cmdLine, " ");
1818 strcat(statusPtr->cmdLine, as->parms[6].items->data);
1822 /* strcat(statusPtr->cmdLine, " -volumeset"); */
1823 strcat(statusPtr->cmdLine, " ");
1824 strcat(statusPtr->cmdLine, as->parms[0].items->data);
1826 /* strcat(statusPtr->cmdLine, " -dump"); */
1827 strcat(statusPtr->cmdLine, " ");
1828 strcat(statusPtr->cmdLine, as->parms[1].items->data);
1830 if (as->parms[2].items)
1832 /* strcat(statusPtr->cmdLine, " -portoffset"); */
1833 strcat(statusPtr->cmdLine, " ");
1834 strcat(statusPtr->cmdLine, as->parms[2].items->data);
1837 if (as->parms[4].items)
1838 strcat(statusPtr->cmdLine, " -append");
1841 strcat(statusPtr->cmdLine, " -n");
1843 printf("Add scheduled dump as job %d\n", statusPtr->jobNumber);
1844 if ( (atTime > ttoken.endTime) && (ttoken.endTime != NEVERDATE) )
1845 com_err(whoami,0,"Warning: job %d starts after expiration of AFS token",
1846 statusPtr->jobNumber);
1855 * Read and execute the load file if specified. The work of reading is done
1856 * in the main routine prior the dispatch call. loadFile and dontExecute are
1857 * global variables so this can take place in main.
1861 loadFile = (char *) malloc(strlen(as->parms[6].items->data)+1);
1864 com_err(whoami,BC_NOMEM,"");
1867 strcpy(loadFile, as->parms[6].items->data);
1872 * We are doing a real dump (no load file or timed dump).
1874 printf("Starting dump of volume set '%s' (dump level '%s')\n", vsName, dumpPath);
1876 /* For each dump-level above this one, see if the volumeset was dumped
1877 * at the level. We search all dump-levels since a higher dump-level
1878 * may have actually been done AFTER a lower dump level.
1880 parent = level = problemFindingDump = 0;
1881 for (tds = baseds->parent; tds; tds = tds->parent) {
1882 /* Find the most recent dump of the volume-set at this dump-level.
1883 * Raise problem flag if didn't find a dump and a parent not yet found.
1885 code = bcdb_FindLatestDump(vsName, tds->name, &dumpEntry);
1887 if (!parent) problemFindingDump = 1; /* skipping a dump level */
1891 /* We found the most recent dump at this level. Now check
1892 * if we should use it by seeing if its full dump hierarchy
1893 * exists. If it doesn't, we don't want to base our incremental
1896 if (!parent || (dumpEntry.id > parent)) {
1897 /* Follow the parent dumps to see if they are all there */
1898 for (d=dumpEntry.parent; d; d=de.parent) {
1899 code = bcdb_FindDumpByID(d, &de);
1903 /* If we found the entire level, remember it. Otherwise raise flag.
1904 * If we had already found a dump, raise the problem flag.
1907 if (parent) problemFindingDump = 1;
1908 parent = dumpEntry.id;
1909 level = dumpEntry.level+1;
1910 memcpy(&fde, &dumpEntry, sizeof(dumpEntry));
1913 /* Dump hierarchy not complete so can't base off the latest */
1914 problemFindingDump = 1;
1919 /* If the problemflag was raise, it means we are not doing the
1920 * dump at the level we requested it be done at.
1922 if (problemFindingDump) {
1924 "Warning: Doing level %d dump due to missing higher-level dumps", level);
1926 printf("Parent dump: dump %s (DumpID %u)\n", fde.name, parent);
1930 printf("Found parent: dump %s (DumpID %u)\n", fde.name, parent);
1933 /* Expand out the volume set into its component list of volumes. */
1934 code = bc_EvalVolumeSet(bc_globalConfig, tvs, &volsToDump, cstruct);
1937 com_err(whoami,code,"; Failed to evaluate volume set");
1942 printf("No volumes to dump\n");
1946 /* Determine what the clone time of the volume was when it was
1947 * last dumped (tve->date). This is the time from when an
1948 * incremental should be done (remains zero if a full dump).
1952 for (tve=volsToDump; tve; tve=tve->next)
1954 code = bcdb_FindClone(parent, tve->name, &tve->date);
1955 if (code) tve->date = 0;
1957 /* Get the time the volume was last cloned and see if the volume has
1958 * changed since then. Only do this when the "-n" flag is specified
1959 * because butc will get the cloneDate at time of dump.
1963 code = volImageTime(tve->server.sin_addr.s_addr, tve->partition,
1964 tve->vid, tve->volType, &tve->cloneDate);
1965 if (code) tve->cloneDate = 0;
1967 if (tve->cloneDate && (tve->cloneDate == tve->date))
1970 "Warning: Timestamp on volume %s unchanged from previous dump",
1977 if (dontExecute) printf("Would have dumped the following volumes:\n");
1978 else printf("Preparing to dump the following volumes:\n");
1979 for (tve=volsToDump; tve; tve=tve->next) {
1980 printf("\t%s (%u)\n", tve->name, tve->vid);
1982 if (dontExecute) return(0);
1984 code = bc_StartDmpRst(bc_globalConfig, dumpPath, vsName, volsToDump,
1985 /*destServer*/0, /*destPartition*/0, /*fromDate*/0,
1986 /*newExt*/0, /*oldFlag*/0,
1987 parent, level, bc_Dumper, portp, /*portCount*/1, baseds,
1988 doAppend, dontExecute);
1989 if (code) com_err(whoami,code,"; Failed to queue dump");
1996 * terminate the backup process. Insists that that all running backup
1997 * jobs be terminated before it will quit
2002 bc_QuitCmd(as, arock)
2003 struct cmd_syndesc *as;
2007 struct bc_dumpTask *td;
2008 extern dlqlinkT statusHead;
2012 /* Check the status list for outstanding jobs */
2014 for ( ptr=(&statusHead)->dlq_next; ptr!=&statusHead; ptr=ptr->dlq_next )
2016 statusPtr = (statusP) ptr;
2017 if ( !(statusPtr->flags & ABORT_REQUEST) )
2020 com_err(whoami,0,"Job %d still running (and not aborted)", statusPtr->jobNumber);
2021 com_err(whoami,0,"You must at least 'kill' all running jobs before quitting");
2027 /* A job still being initialized (but no status structure or job number since it
2028 * has not been handed to a butc process yet)
2030 for(td=bc_dumpTasks, i=0; i<BC_MAXSIMDUMPS; i++, td++)
2032 if (td->flags & BC_DI_INUSE)
2034 com_err(whoami,0,"A job is still running");
2035 com_err(whoami,0,"You must at least 'kill' all running jobs before quitting");
2041 /* close the all temp text files before quitting */
2042 for(i=0; i<TB_NUM; i++)
2043 bc_closeTextFile(&bc_globalConfig->configText[i], &bc_globalConfig->tmpTextFileNames[i][0]);
2049 * Labels a tape i.e. request the tape coordinator to perform this
2053 bc_LabelTapeCmd(as, arock)
2054 struct cmd_syndesc *as;
2057 char *tapename=0, *pname=0;
2062 code = bc_UpdateHosts();
2064 com_err(whoami, code, "; Can't retrieve tape hosts");
2068 if(as->parms[0].items) { /* -name */
2069 tapename = as->parms[0].items->data;
2070 if (strlen(tapename) >= TC_MAXTAPELEN) {
2071 com_err(whoami,0,"AFS tape name '%s' is too long", tapename);
2076 if(as->parms[3].items) { /* -pname */
2078 com_err(whoami, 0, "Can only specify -name or -pname");
2081 pname = as->parms[3].items->data;
2082 if (strlen(pname) >= TC_MAXTAPELEN) {
2083 com_err(whoami,0,"Permanent tape name '%s' is too long", pname);
2088 if (as->parms[1].items)
2090 size = bc_FloatATOI(as->parms[1].items->data);
2092 com_err(whoami,0,"Bad syntax for tape size '%s'",
2093 as->parms[1].items->data);
2100 if (as->parms[2].items)
2102 port = getPortOffset(as->parms[2].items->data);
2103 if (port < 0) return(BC_BADARG);
2106 code = bc_LabelTape(tapename, pname, size, bc_globalConfig, port);
2107 if (code) return code;
2112 * read the label on a tape
2114 * optional port number
2117 bc_ReadLabelCmd(as, arock)
2118 struct cmd_syndesc *as;
2124 code = bc_UpdateHosts();
2126 com_err(whoami, code, "; Can't retrieve tape hosts");
2130 if (as->parms[0].items) {
2131 port = getPortOffset(as->parms[0].items->data);
2132 if (port < 0) return(BC_BADARG);
2135 code = bc_ReadLabel(bc_globalConfig, port);
2136 if (code) return code;
2141 * read content information from dump tapes, and if user desires,
2142 * add it to the database
2145 bc_ScanDumpsCmd(as, arock)
2146 struct cmd_syndesc *as;
2150 afs_int32 dbAddFlag = 0;
2153 code = bc_UpdateHosts();
2155 com_err(whoami, code, "; Can't retrieve tape hosts");
2159 /* check for flag */
2160 if ( as->parms[0].items != 0 ) /* add scan to database */
2165 /* check for port */
2166 if (as->parms[1].items) {
2167 port = getPortOffset(as->parms[1].items->data);
2168 if (port < 0) return(BC_BADARG);
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(paramPtr, expType, expDate)
2186 struct cmd_parmdesc *paramPtr;
2190 struct cmd_item *itemPtr;
2191 struct ktime_date kt;
2192 char *dateString = 0;
2195 *expType = BC_NO_EXPDATE;
2198 if ( !paramPtr->items ) ERROR(0); /* no expiration specified */
2200 /* some form of expiration date specified. First validate the prefix */
2201 itemPtr = paramPtr->items;
2203 if ( strcmp(itemPtr->data, "at") == 0 )
2205 *expType = BC_ABS_EXPDATE;
2207 dateString = concatParams(itemPtr->next);
2208 if (!dateString) ERROR(1);
2210 code = ktime_DateToLong(dateString, expDate);
2214 if ( strcmp(itemPtr->data, "in") == 0 )
2216 *expType = BC_REL_EXPDATE;
2218 dateString = concatParams(itemPtr->next);
2219 if (!dateString) ERROR(1);
2221 code = ParseRelDate(dateString, &kt);
2223 *expDate = ktimeRelDate_ToLong(&kt);
2227 dateString = concatParams(itemPtr);
2228 if (!dateString) ERROR(1);
2230 if ( ktime_DateToLong(dateString,expDate) == 0 )
2232 *expType = BC_ABS_EXPDATE;
2233 code = ktime_DateToLong(dateString, expDate);
2236 else if ( ParseRelDate(dateString,&kt) == 0 )
2238 *expType = BC_REL_EXPDATE;
2239 *expDate = ktimeRelDate_ToLong(&kt);
2248 if (dateString) free(dateString);
2252 /* database lookup command and routines */
2255 * Currently a single option, volumename to search for. Reports
2256 * all dumps containing the specified volume
2259 bc_dblookupCmd(as, arock)
2260 struct cmd_syndesc *as;
2263 struct cmd_item *ciptr;
2266 ciptr = as->parms[0].items;
2267 if ( ciptr == 0 ) /* no argument specified */
2270 code = DBLookupByVolume(ciptr->data);
2276 /* for ubik version */
2278 bc_dbVerifyCmd(as, arock)
2279 struct cmd_syndesc *as;
2286 struct hostent *hostPtr;
2290 extern struct udbHandleS udbHandle;
2291 extern afs_int32 BUDB_DbVerify();
2293 detail = (as->parms[0].items ? 1 : 0); /* print more details */
2295 code = ubik_Call(BUDB_DbVerify, udbHandle.uh_client, 0,
2296 &status, &orphans, &host);
2300 com_err(whoami, code, "; Unable to verify database");
2304 /* verification call succeeded */
2307 printf("Database OK\n");
2309 com_err(whoami, status, "; Database is NOT_OK");
2313 printf("Orphan blocks %d\n", orphans);
2316 printf("Unable to lookup host id\n");
2319 hostPtr = gethostbyaddr((char *) &host, sizeof(host), AF_INET);
2321 printf("Database checker was %d.%d.%d.%d\n",
2322 ((host&0xFF000000)>>24), ((host&0xFF0000)>>16),
2323 ((host&0xFF00)>>8), (host&0xFF));
2325 printf("Database checker was %s\n", hostPtr->h_name);
2328 return((status ? -1 : 0));
2332 * Delete a dump. If port is >= 0, it means try to delete from XBSA server
2334 deleteDump(dumpid, port, force)
2335 afs_uint32 dumpid; /* The dumpid to delete */
2336 afs_int32 port; /* port==-1 means don't go to butc */
2339 afs_int32 code=0, tcode;
2340 struct budb_dumpEntry dumpEntry;
2341 struct rx_connection *tconn=0;
2342 afs_int32 i, taskflag, xbsadump;
2343 statusP statusPtr=0;
2344 budb_dumpsList dumps;
2347 /* If the port is set, we will try to send a delete request to the butc */
2349 tcode = bc_UpdateHosts();
2351 com_err(whoami, tcode, "; Can't retrieve tape hosts");
2355 /* Find the dump in the backup database */
2356 tcode = bcdb_FindDumpByID(dumpid, &dumpEntry);
2358 com_err(whoami, tcode, "; Unable to locate dumpID %u in database", dumpid);
2361 xbsadump = (dumpEntry.flags & (BUDB_DUMP_ADSM|BUDB_DUMP_BUTA));
2363 /* If dump is to an XBSA server, connect to butc and send it
2364 * the dump to delete. Butc will contact the XBSA server.
2365 * The dump will not be an appended dump because XBSA butc
2366 * does not support the append option.
2368 if (xbsadump && dumpEntry.nVolumes) {
2369 tcode = ConnectButc(bc_globalConfig, port, &tconn);
2370 if (tcode) ERROR(tcode);
2372 tcode = TC_DeleteDump(tconn, dumpid, &taskId);
2374 if (tcode == RXGEN_OPCODE) tcode = BC_VERSIONFAIL;
2375 com_err(whoami, tcode, "; Unable to delete dumpID %u via butc", dumpid);
2379 statusPtr = createStatusNode();
2381 statusPtr->taskId = taskId;
2382 statusPtr->port = port;
2383 statusPtr->jobNumber = bc_jobNumber();
2384 statusPtr->flags |= (SILENT|NOREMOVE); /* No msg & keep statusPtr */
2385 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2386 sprintf(statusPtr->taskName, "DeleteDump");
2389 /* Wait for task to finish */
2390 taskflag = waitForTask(taskId);
2391 if (taskflag & (TASK_ERROR|ABORT_DONE)) {
2392 com_err(whoami, BUTX_DELETEOBJFAIL, "; Unable to delete dumpID %u via butc", dumpid);
2393 ERROR(BUTX_DELETEOBJFAIL); /* the task failed */
2399 if (statusPtr) deleteStatusNode(statusPtr); /* Clean up statusPtr - because NOREMOVE */
2400 if (tconn) rx_DestroyConnection(tconn); /* Destroy the connection */
2402 /* Remove the dump from the backup database */
2403 if (!code || force) {
2404 dumps.budb_dumpsList_len = 0;
2405 dumps.budb_dumpsList_val = 0;
2407 tcode = bcdb_deleteDump(dumpid, 0, 0, &dumps);
2409 com_err(whoami, tcode, "; Unable to delete dumpID %u from database", dumpid);
2410 dumps.budb_dumpsList_len = 0;
2411 if (!code) code = tcode;
2414 /* Display the dumps that were deleted - includes appended dumps */
2415 for (i=0; i<dumps.budb_dumpsList_len; i++)
2416 printf(" %u%s\n", dumps.budb_dumpsList_val[i],
2417 (i>0)?" Appended Dump":"");
2418 if (dumps.budb_dumpsList_val) free(dumps.budb_dumpsList_val);
2425 * Delete a specified dump from the database
2427 * dump id - single required arg as param 0.
2430 bc_deleteDumpCmd(as, arock)
2431 struct cmd_syndesc *as;
2436 afs_int32 rcode = 0;
2437 afs_int32 groupId=0, havegroupid, sflags, noexecute;
2438 struct cmd_item *ti;
2439 afs_uint32 fromTime=0, toTime=0, havetime=0;
2441 budb_dumpsList dumps, flags;
2443 afs_int32 port=-1, dbonly=0, force;
2446 /* Must specify at least one of -dumpid, -from, or -to */
2447 if ( !as->parms[0].items && !as->parms[1].items && !as->parms[2].items &&
2448 !as->parms[4].items) {
2449 com_err(whoami, 0, "Must specify at least one field");
2453 /* Must have -to option with -from option */
2454 if ( as->parms[1].items && !as->parms[2].items ) {
2455 com_err(whoami, 0, "Must specify '-to' field with '-from' field");
2459 /* Get the time to delete from */
2460 if ( as->parms[1].items ) { /* -from */
2461 timeString = concatParams(as->parms[1].items);
2462 if (!timeString) return(-1);
2465 * Now parse this string for the time to start.
2467 code = ktime_DateToLong(timeString, &fromTime);
2471 com_err(whoami,0,"Can't parse 'from' date and time");
2472 com_err(whoami,0,"%s", ktime_GetDateUsage());
2478 port = (as->parms[3].items ? getPortOffset(as->parms[3].items->data) : 0); /* -port */
2479 if (as->parms[5].items) /* -dbonly */
2482 force = (as->parms[6].items ? 1 : 0);
2484 havegroupid = (as->parms[4].items ? 1 : 0);
2485 if (havegroupid) groupId = atoi(as->parms[4].items->data);
2487 noexecute = (as->parms[7].items ? 1 : 0);
2489 /* Get the time to delete to */
2490 if ( as->parms[2].items ) { /* -to */
2491 timeString = concatParams(as->parms[2].items);
2492 if (!timeString) return(-1);
2495 * Now parse this string for the time to start. Simce
2496 * times are at minute granularity, add 59 seconds.
2498 code = ktime_DateToLong(timeString, &toTime);
2502 com_err(whoami,0,"Can't parse 'to' date and time");
2503 com_err(whoami,0,"%s", ktime_GetDateUsage());
2510 if ( fromTime > toTime ) {
2511 com_err(whoami, 0, "'-from' date/time cannot be later than '-to' date/time");
2515 /* Remove speicific dump ids - if any */
2516 printf("The following dumps %s deleted:\n", (noexecute?"would have been":"were"));
2517 for ( ti=as->parms[0].items; ti != 0; ti=ti->next ) { /* -dumpid */
2518 dumpid = atoi(ti->data);
2520 code = deleteDump(dumpid, port, force);
2522 printf(" %u\n", dumpid);
2527 * Now remove dumps between to and from dates.
2529 if (havegroupid || havetime) {
2530 dumps.budb_dumpsList_len = 0;
2531 dumps.budb_dumpsList_val = 0;
2532 flags.budb_dumpsList_len = 0;
2533 flags.budb_dumpsList_val = 0;
2535 if (havegroupid) sflags |= BUDB_OP_GROUPID;
2536 if (havetime) sflags |= BUDB_OP_DATES;
2538 code = bcdb_listDumps(sflags, groupId, fromTime, toTime, &dumps, &flags);
2540 com_err(whoami, code, "; Error while deleting dumps from %u to %u",
2545 for (i=0; i<dumps.budb_dumpsList_len; i++) {
2546 if (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP) continue;
2549 if (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP) continue;
2550 code = deleteDump(dumps.budb_dumpsList_val[i], port, force);
2551 /* Ignore code and continue */
2553 printf(" %u%s%s\n", dumps.budb_dumpsList_val[i],
2554 (flags.budb_dumpsList_val[i] & BUDB_OP_APPDUMP)?" Appended Dump":"",
2555 (flags.budb_dumpsList_val[i] & BUDB_OP_DBDUMP) ?" Database Dump":"");
2560 if (dumps.budb_dumpsList_val) free(dumps.budb_dumpsList_val);
2561 dumps.budb_dumpsList_len = 0;
2562 dumps.budb_dumpsList_val = 0;
2563 if (flags.budb_dumpsList_val) free(flags.budb_dumpsList_val);
2564 flags.budb_dumpsList_len = 0;
2565 flags.budb_dumpsList_val = 0;
2571 bc_saveDbCmd(as, arock)
2572 struct cmd_syndesc *as;
2575 struct rx_connection *tconn;
2576 afs_int32 portOffset = 0;
2583 code = bc_UpdateHosts();
2585 com_err(whoami, code, "; Can't retrieve tape hosts");
2589 if (as->parms[0].items) {
2590 portOffset = getPortOffset(as->parms[0].items->data);
2591 if (portOffset < 0) return(BC_BADARG);
2594 /* Get the time to delete to */
2595 if ( as->parms[1].items )
2597 timeString = concatParams(as->parms[1].items);
2598 if (!timeString) return(-1);
2601 * Now parse this string for the time. Since
2602 * times are at minute granularity, add 59 seconds.
2604 code = ktime_DateToLong(timeString, &toTime);
2608 com_err(whoami,0,"Can't parse '-archive' date and time");
2609 com_err(whoami,0,"%s", ktime_GetDateUsage());
2617 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2618 if (code) return(code);
2620 code = TC_SaveDb(tconn, toTime, &taskId);
2623 com_err(whoami, code, "; Failed to save database");
2627 /* create status monitor block */
2628 statusPtr = createStatusNode();
2630 statusPtr->taskId = taskId;
2631 statusPtr->port = portOffset;
2632 statusPtr->jobNumber = bc_jobNumber();
2633 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2634 sprintf(statusPtr->taskName, "SaveDb");
2638 rx_DestroyConnection(tconn);
2642 bc_restoreDbCmd(as, arock)
2643 struct cmd_syndesc *as;
2646 struct rx_connection *tconn;
2647 afs_int32 portOffset = 0;
2652 code = bc_UpdateHosts();
2654 com_err(whoami, code, "; Can't retrieve tape hosts");
2658 if (as->parms[0].items) {
2659 portOffset = getPortOffset(as->parms[0].items->data);
2660 if (portOffset < 0) return(BC_BADARG);
2663 code = ConnectButc(bc_globalConfig, portOffset, &tconn);
2664 if (code) return(code);
2666 code = TC_RestoreDb(tconn, &taskId);
2669 com_err(whoami, code, "; Failed to restore database");
2673 /* create status monitor block */
2674 statusPtr = createStatusNode();
2676 statusPtr->taskId = taskId;
2677 statusPtr->port = portOffset;
2678 statusPtr->jobNumber = bc_jobNumber();
2679 statusPtr->flags &= ~STARTING; /* clearstatus to examine */
2680 sprintf(statusPtr->taskName, "RestoreDb");
2684 rx_DestroyConnection(tconn);
2688 /* ----------------------------------
2689 * supporting routines for database examination
2690 * ----------------------------------
2693 /* structures and defines for DBLookupByVolume */
2695 #define DBL_MAX_VOLUMES 20 /* max. for each dump */
2697 /* dumpedVol - saves interesting information so that we can print it out
2703 struct dumpedVol *next;
2705 afs_int32 initialDumpID;
2706 char tapeName[BU_MAXTAPELEN];
2709 afs_int32 createTime;
2710 afs_int32 incTime; /* actually the clone time */
2713 /* -----------------------------------------
2714 * routines for examining the database
2715 * -----------------------------------------
2719 * Lookup the volumename in the backup database and print the results
2721 * volumeName - volume to lookup
2724 DBLookupByVolume(volumeName)
2727 struct budb_dumpEntry dumpEntry;
2728 struct budb_volumeEntry volumeEntry[DBL_MAX_VOLUMES];
2729 afs_int32 numEntries;
2730 afs_int32 tapedumpid;
2731 afs_int32 last,next;
2733 struct dumpedVol *dvptr = 0;
2734 struct dumpedVol *tempPtr = 0;
2737 char vname[BU_MAXNAMELEN];
2740 for (pass = 0; pass < 2; pass++) {
2742 /* On second pass, search for backup volume */
2744 if (!BackupName(volumeName)) {
2745 strcpy(vname, volumeName);
2746 strcat(vname, ".backup");
2755 while ( next != -1 )
2757 code = bcdb_LookupVolume(volumeName, &volumeEntry[0],
2758 last, &next, DBL_MAX_VOLUMES, &numEntries);
2761 /* add the volumes to the list */
2762 for ( i = 0; i < numEntries; i++ )
2764 struct dumpedVol *insPtr, **prevPtr;
2766 tempPtr = (struct dumpedVol *) malloc(sizeof(struct dumpedVol));
2767 if (!tempPtr) ERROR(BC_NOMEM);
2769 memset(tempPtr, 0, sizeof(*tempPtr));
2770 tempPtr->incTime = volumeEntry[i].clone;
2771 tempPtr->dumpID = volumeEntry[i].dump;
2772 strncpy(tempPtr->tapeName, volumeEntry[i].tape, BU_MAXTAPELEN);
2774 /* check if we need to null terminate it - just for safety */
2775 if ( strlen(volumeEntry[i].tape) >= BU_MAXTAPELEN )
2776 tempPtr->tapeName[BU_MAXTAPELEN-1] = 0;
2778 code = bcdb_FindDumpByID(tempPtr->dumpID, &dumpEntry);
2785 tempPtr->initialDumpID = dumpEntry.initialDumpID;
2786 tempPtr->parent = dumpEntry.parent;
2787 tempPtr->level = dumpEntry.level;
2788 tempPtr->createTime = dumpEntry.created;
2790 /* add volume to list in reverse chronological order */
2794 while ( (insPtr != 0)
2795 && (insPtr->createTime > tempPtr->createTime)
2798 prevPtr = &insPtr->next;
2799 insPtr = insPtr->next;
2802 /* now at the right place - insert the block */
2803 tempPtr->next = *prevPtr;
2812 printf("DumpID lvl parentID creation date clone date tape name\n");
2813 for (tempPtr = dvptr; tempPtr; tempPtr = tempPtr->next) {
2814 /* For the user, the tape name is its name and initial dump id */
2815 tapedumpid = (tempPtr->initialDumpID ? tempPtr->initialDumpID : tempPtr->dumpID);
2817 /* beware the static items in compactDateString */
2818 compactDateString(&tempPtr->createTime, ds, 50);
2819 printf("%-9d %-2d %-9d %16s",
2820 tempPtr->dumpID, tempPtr->level, tempPtr->parent, ds);
2821 compactDateString(&tempPtr->incTime, ds, 50);
2822 printf(" %16s %s (%u)\n", ds, tempPtr->tapeName, tapedumpid);
2828 for (tempPtr = dvptr; tempPtr; tempPtr = dvptr) {
2829 dvptr = dvptr->next;
2834 com_err(whoami, code, "");
2838 /* structures for dumpInfo */
2842 struct volumeLink *nextVolume;
2843 struct budb_volumeEntry volumeEntry;
2848 struct tapeLink *nextTape;
2849 struct budb_tapeEntry tapeEntry;
2850 struct volumeLink *firstVolume;
2855 * print information about a dump and all its tapes and volumes.
2859 dumpInfo(dumpid, detailFlag)
2861 afs_int32 detailFlag;
2863 struct budb_dumpEntry dumpEntry;
2864 struct tapeLink *head = 0;
2865 struct tapeLink *tapeLinkPtr, *lastTapeLinkPtr;
2866 struct volumeLink **link, *volumeLinkPtr, *lastVolumeLinkPtr;
2869 afs_int32 last, next, dbTime;
2870 afs_int32 tapedumpid;
2879 extern struct udbHandleS udbHandle;
2882 lastTapeLinkPtr = 0;
2884 lastVolumeLinkPtr = 0;
2886 /* first get information about the dump */
2888 code = bcdb_FindDumpByID(dumpid, &dumpEntry);
2889 if (code) ERROR(code);
2891 /* For the user, the tape name is its name and initial dump id */
2892 tapedumpid = (dumpEntry.initialDumpID ? dumpEntry.initialDumpID : dumpid);
2894 /* Is this a database dump id or not */
2895 if ( strcmp(dumpEntry.name,DUMP_TAPE_NAME) == 0 )
2900 /* print out the information about the dump */
2904 printDumpEntry(&dumpEntry);
2907 printf("Dump: id %u, created: %s\n",
2908 dumpEntry.id, ctime(&dumpEntry.created));
2910 printf("Dump: id %u, level %d, volumes %d, created: %s\n",
2911 dumpEntry.id, dumpEntry.level, dumpEntry.nVolumes,
2912 ctime(&dumpEntry.created));
2915 if ( !detailFlag && (strlen(dumpEntry.tapes.tapeServer) > 0) &&
2916 (dumpEntry.flags & (BUDB_DUMP_ADSM | BUDB_DUMP_BUTA)) ) {
2917 printf("Backup Service: TSM: Server: %s\n",
2918 dumpEntry.tapes.tapeServer);
2921 /* now get the list of tapes */
2922 for (tapeNumber=dumpEntry.tapes.b; tapeNumber<=dumpEntry.tapes.maxTapes; tapeNumber++)
2924 tapeLinkPtr = (struct tapeLink *) malloc(sizeof(struct tapeLink));
2927 com_err(whoami,BC_NOMEM,"");
2931 memset(tapeLinkPtr, 0, sizeof(*tapeLinkPtr));
2932 code = bcdb_FindTapeSeq(dumpid, tapeNumber, &tapeLinkPtr->tapeEntry);
2940 /* add this tape to previous chain */
2941 if (lastTapeLinkPtr)
2943 lastTapeLinkPtr->nextTape = tapeLinkPtr;
2944 lastTapeLinkPtr = tapeLinkPtr;
2950 lastTapeLinkPtr = head;
2956 vl.budb_volumeList_len = 0;
2957 vl.budb_volumeList_val = 0;
2960 /* now get all the volumes in this dump. */
2961 code = ubik_Call_SingleServer(BUDB_GetVolumes, udbHandle.uh_client,
2964 BUDB_OP_DUMPID | BUDB_OP_TAPENAME,
2965 tapeLinkPtr->tapeEntry.name, /* tape name */
2966 dumpid, /* dumpid (not initial dumpid) */
2969 &next, /* nextindex */
2970 &dbTime, /* update time */
2975 if ( code == BUDB_ENDOFLIST ) /* 0 volumes on tape */
2983 for (i=0; i<vl.budb_volumeList_len; i++)
2985 link = &tapeLinkPtr->firstVolume;
2987 volumeLinkPtr = (struct volumeLink *) malloc(sizeof(struct volumeLink));
2990 com_err(whoami,BC_NOMEM,"");
2993 memset(volumeLinkPtr, 0, sizeof(*volumeLinkPtr));
2995 memcpy(&volumeLinkPtr->volumeEntry, &vl.budb_volumeList_val[i], sizeof(struct budb_volumeEntry));
2997 /* now insert it onto the right place */
2998 while ( (*link != 0) &&
2999 (volumeLinkPtr->volumeEntry.position > (*link)->volumeEntry.position) )
3001 link = &((*link)->nextVolume);
3004 /* now link it in */
3005 volumeLinkPtr->nextVolume = *link;
3006 *link = volumeLinkPtr;
3009 if (vl.budb_volumeList_val) free(vl.budb_volumeList_val);
3013 for (tapeLinkPtr=head; tapeLinkPtr; tapeLinkPtr=tapeLinkPtr->nextTape)
3018 printTapeEntry(&tapeLinkPtr->tapeEntry);
3020 printf("Tape: name %s (%u)\n", tapeLinkPtr->tapeEntry.name, tapedumpid);
3021 printf("nVolumes %d, ", tapeLinkPtr->tapeEntry.nVolumes);
3022 compactDateString(&tapeLinkPtr->tapeEntry.written, ds, 50);
3023 printf("created %16s", ds);
3024 if ( tapeLinkPtr->tapeEntry.expires != 0 ) {
3025 compactDateString(&tapeLinkPtr->tapeEntry.expires, ds, 50);
3026 printf(", expires %16s", ds);
3031 /* print out all the volumes */
3033 /* print header for volume listing - db dumps have no volumes */
3034 if ( (detailFlag == 0) && !dbDump )
3035 printf("%4s %16s %9s %-s\n", "Pos", "Clone time", "Nbytes", "Volume");
3037 for (volumeLinkPtr=tapeLinkPtr->firstVolume; volumeLinkPtr;
3038 volumeLinkPtr=volumeLinkPtr->nextVolume)
3042 printf("\nVolume\n");
3044 printVolumeEntry(&volumeLinkPtr->volumeEntry);
3048 compactDateString(&volumeLinkPtr->volumeEntry.clone, ds, 50),
3049 printf("%4d %s %10u %16s\n",
3050 volumeLinkPtr->volumeEntry.position,
3052 volumeLinkPtr->volumeEntry.nBytes,
3053 volumeLinkPtr->volumeEntry.name);
3059 if (code) com_err("dumpInfo", code, "; Can't get dump information");
3061 /* free all allocated structures */
3063 while ( tapeLinkPtr )
3065 volumeLinkPtr = tapeLinkPtr->firstVolume;
3066 while ( volumeLinkPtr )
3068 lastVolumeLinkPtr = volumeLinkPtr;
3069 volumeLinkPtr = volumeLinkPtr->nextVolume;
3070 free(lastVolumeLinkPtr);
3073 lastTapeLinkPtr = tapeLinkPtr;
3074 tapeLinkPtr = tapeLinkPtr->nextTape;
3075 free(lastTapeLinkPtr);
3080 compareDump(ptr1, ptr2)
3081 struct budb_dumpEntry *ptr1, *ptr2;
3083 if ( ptr1->created < ptr2->created )
3086 if ( ptr1->created > ptr2->created )
3091 afs_int32 printRecentDumps(ndumps)
3095 afs_int32 nextindex, index = 0;
3097 afs_int32 tapedumpid;
3099 struct budb_dumpEntry *dumpPtr;
3103 extern struct udbHandleS udbHandle;
3104 extern compareDump();
3106 do { /* while (nextindex != -1) */
3107 /* initialize the dump list */
3108 dl.budb_dumpList_len = 0;
3109 dl.budb_dumpList_val = 0;
3111 /* outline algorithm */
3112 code = ubik_Call (BUDB_GetDumps, udbHandle.uh_client, 0,
3123 if (code == BUDB_ENDOFLIST) return 0;
3124 com_err("dumpInfo", code, "; Can't get dump information");
3128 /* No need to sort, it's already sorted */
3130 if (dl.budb_dumpList_len && (index == 0))
3131 printf("%10s %10s %2s %-16s %2s %5s dump name\n",
3132 "dumpid", "parentid", "lv", "created", "nt", "nvols");
3134 dumpPtr = dl.budb_dumpList_val;
3135 for ( i = 1; i <= dl.budb_dumpList_len; i++ ) {
3136 compactDateString(&dumpPtr->created, ds, 50),
3137 printf("%10u %10u %-2d %16s %2d %5d %s",
3142 dumpPtr->tapes.maxTapes - dumpPtr->tapes.b + 1,
3146 if (dumpPtr->initialDumpID) /* an appended dump */
3147 printf(" (%u)", dumpPtr->initialDumpID);
3148 else if (dumpPtr->appendedDumpID) /* has appended dumps */
3149 printf(" (%u)", dumpPtr->id);
3155 if (dl.budb_dumpList_val) free(dl.budb_dumpList_val);
3157 } while (nextindex != -1);
3163 * list the dumps and contens of the dumps.
3168 bc_dumpInfoCmd(as, arock)
3169 struct cmd_syndesc *as;
3173 afs_int32 detailFlag;
3177 afs_int32 dumpInfo();
3179 if( as->parms[0].items )
3181 if(as->parms[1].items)
3183 com_err(whoami, 0, "These options are exclusive - select only one");
3186 ndumps = atoi(as->parms[0].items->data);
3189 com_err(whoami, 0, "Must provide a positive number");
3193 code = printRecentDumps(ndumps);
3195 else if (as->parms[1].items)
3197 detailFlag = (as->parms[2].items ? 1 : 0); /* 1 = detailed listing */
3198 dumpid = atoi(as->parms[1].items->data);
3199 code = dumpInfo(dumpid, detailFlag);
3203 code = printRecentDumps(10);