$!++ $! Title: CLUSTER_SHUTDOWN.COM $! $! Author: Barry J. Gillin $! Gillin Consulting Company $! 8140 Calvin Avenue $! Reseda, CA 91335 $! (818) 885-0573 $! $! Creation Date: 25-Feb-1991 $! $! Functional Description: $! This command procedure is designed to allow $! someone who is responsible for the management $! of a VAXcluster an easy method of shutting down $! the entire VAXcluster. The prompts within the $! command procedure mimic the questions posed $! during a normal system shutdown, except this $! command procedure will shut down the entire $! cluster. $! $! Assumptions: $! This command procedure assumes that the standard $! DCL shutdown command procedure will accept the $! seven arguments which are normally asked as $! questions during and interactive shutdown. VMS $! versions through 5.4-1 do allow this, but if the $! shutdown command procedure gets changed in the $! future, there is no gaurantee that this procedure $! will continue to work. $!-- $ $! $! Set up the symbol "say" and check to make sure that we have the $! required privileges. $! $ say = "write sys$output" $ say f$fao("!/!/!_CLUSTER_SHUT -- Perform an Orderly Cluster Shutdown") $ privs = "CMKRNL, EXQUOTA, LOG_IO, NETMBX, OPER, SECURITY, " - + "SYSNAM, SYSPRV, TMPMBX, WORLD" $ saved_privs = f$setprv(privs) $! $! If we don't have the required privileges, give a message and get out $! $ if .not. f$privilege(privs) $ then $ say "" $ say "%CLUSTER_SHUT-F-NOPRIV, The following privileges are required:" $ say "-CLUSTER_SHUT-F-NOPRIV, ''privs'" $ exit $ endif $! $! On any control/y or error, clean up and get out. $! $ on control_y then goto CANCELLED $ on error then goto CANCELLED $! $! Ask for the number of minutes until shutdown. No check is made to $! determine whether the logical name SHUTDOWN$MINIMUM_MINUTES is defined $! but if it is, and the answer given is less than the minimum specified $! through the logical name, the shutdown will not function correctly. $! $ pmpt = "How many minutes until final shutdown [0]: " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p1 $ if p1 .eqs. "" then p1 = 0 $! $! Ask for and input the reason for cluster shutdown. $! $ pmpt = "Reason for shutdown [Standalone]: " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p2 $ if p2 .eqs. "" then p2 = "Standalone" $! $! Ask for and input whether or not to spin down the disks. $! $ pmpt = "Do you want to spin down the disk volumes [NO]? " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p3 $ if p3 .eqs. "" then p3 = "NO" $! $! Check on the site specific command procedure. $! $ pmpt = "Do you want to invoke the site-specific shutdown procedure [YES]? " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p4 $ if p4 .eqs. "" then p4 = "YES" $! $! Check for automatica reboot $! $ pmpt = "Should an automatic system reboot be performed [NO]? " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p6 $ if p6 .eqs. "" then p6 = "NO" $! $! When will the system come back up? $! $ when = "later" $ if p6 then when = "shortly via automatic reboot" $ pmpt = "When will the system be rebooted [''when']: " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p5 $ if p5 .eqs. "" then p5 = when $! $! Allow the reboot check and the save feedback options to be used. $! The cluster shutdown option will be supplied by default, and $! it would not be compatible with the remove_node option. $! $ p7_flags = "/REBOOT_CHECK/SAVE_FEEDBACK/" $ say f$fao("Shutdown options (enter as a comma-separated list):") $ say f$fao(" !20Check existence of basic system files") $ say f$fao(" !20Save AUTOGEN feedback information from this boot") $ say "" $ pmpt = "Shutdown options [NONE]: " $ read/end_of_file=CANCELLED/prompt="''pmpt'" sys$command p7 $ if p7 .eqs. "" then p7 = "NONE" $! $! parse the options that have been supplied for the shutdown. We $! start by assuming that no options have been provided. $! $ REBOOT_CHECK = 0 $ SAVE_FEEDBACK = 0 $ tp7 = f$edit(p7,"COLLAPSE,UPCASE") + "," $ p7_loop: $ t1 = f$element(0,",",tp7) $ if t1 .eqs. "" then goto p7_loopend $! $! If the option was specified, we will set the appropriate flag for $! later use. $! $ if t1 .eqs. f$extract(0,f$length(t1),"REBOOT_CHECK") $ then $ REBOOT_CHECK = 1 $ else $ if t1 .eqs. f$extract(0,f$length(t1),"SAVE_FEEDBACK") $ then $ SAVE_FEEDBACK = 1 $ endif $ endif $! $! Remove this option from the list specified, and see if there are $! any more to be processed $! $ tp7 = tp7 - "''t1'," $ goto p7_loop $ p7_loopend: $! $! Create a temporary command procedure which will be invoked by a $! detached process on each node. The command procedure will invoke $! the standard shutdown command procedure on each node. $! $ open/write shut_command sys$common:[sysmgr]shut_det.com $ write shut_command "$ @sys$system:shutdown -" $ write shut_command "''p1' - " $ write shut_command """''p2'"" - " $ write shut_command "''p3' - " $ write shut_command "''p4' - " $ write shut_command """''p5'"" - " $ write shut_command "''p6' - " $! $! The last argument, (shutdown options) has to be built based on the $! symbols that we have previously set up. We always add in the $! cluster_shutdown option. $! $ shutdown_option = "CLUSTER_SHUTDOWN" $ if REBOOT_CHECK $ then $ shutdown_option = shutdown_option + ",REBOOT_CHECK" $ endif $ if SAVE_FEEDBACK $ then $ shutdown_option = shutdown_option + ",SAVE_FEEDBACK" $ endif $ write shut_command """''shutdown_option'""" $! $! Close off the temporary command procedure. $! $ write shut_command "$ EXIT" $ close shut_command $! $! Using the SYSMAN utility "DO" command, create detached processes on $! each node of the VAXcluster to actually perform the shutdown. $! $ run sys$system:sysman set environment/cluster do run/detach sys$system:loginout.exe - /input=sys$common:[sysmgr]shut_det.com - /output=sys$specific:[sysmgr]shut_det.log - /maximum=1024 - /priority=8 - /privileges=(cmk,exq,log,net,oper,sec,sysn,sysp,tmpm,wor) - /process_name="Shutdown" exit $! $! Clean up any leftover garbage and exit. $! $ purge sys$common:[sysmgr]shut_det.com $ exit $! $! Here on control/y or error. Clean up and exit. $! $ CANCELLED: $ saved_privs = f$setprv(saved_privs) $ exit $!Copy software to system area (see figure 7 for programs) $copy Image_backup.com sys$manager: $copy backup_subs.com sys$manager: $copy update_tape_index.com sys$manager: $copy read_jnl.exe sys$system: $Init/density=6250 mua0: 1000 $Init/density=6250 mua0: 1001 etc $!Tape ID file format: $! Character*4 ! format is ####[-####] where #### is four digit tape ID and $!or ! the - is optional delimiter indicating a range of tapes $! Character*9 $! Create tapeid list (volumes) for first job $Create sys$manager:tapes1.txt 1000 1001 1002 1003 1004 1005 $ $! Create tapeid list (volumes) for second job $Create sys$manager:tapes2.txt 2000-2004 $ $!Disk file format: $! Character*3 State ! either BEG or END reflecting state of backup $! Character*(*) Disk ! the disk to be backed up $! $!Create a file of disks to be backed up (omit the colon :) $Create sys$manager:backup_disks.txt BEG$1$DUA0 BEG$1$DUA1 BEG$1$DUA2 BEG$1$DUA3 BEG$1$DUA4 $ $! $! Submit first backup using MUA0: and MUA1: tapes 1000-1005 $! $submit sys$manager:Image_backup/para=- ("N","sys$manager:tapes1.txt","sys$manager:backup_disks.txt","0","1") $! $! Submit second backup using MUA2: and MUA3: tapes 2000-2005 $! $submit sys$manager:Image_backup/para=- ("N","sys$manager:tapes2.txt","sys$manger:backup_disks.txt","2","3") Sample tape index file format: Integer*2 Tapeid ! 4 digit by convention (keyed) Byte Volume ! 1, 2, 3 etc Character*1 BackupType ! Image, incremental etc Byte DiskUnit ! Disk unit number not whole name Character*50 Directory ! A resonable directory length (keyed) Real*8 DateTime ! Date/time of backup Sample user index file format: Character*30 UserName ! User requesting backup (keyed) Integer*2 TapeId ! link to tape index file Real*8 Expiration_Date ! when tape can be returned to pool Character*80 Description ! what was put on the tape $!+ $! IMAGE_BACKUP.COM $! $! Jan 5, 1990 Kevin F. Homan $! $! P1 - restart value y n t f etc $! P2 - Filename containing Tape ids (volumes) $! P3 - Filename containing Disk ids (volumes) $! P4 - 1st drive $! P5 - 2nd drive $! P6 - 3rd drive $! P7 - 4th drive $! Modification history: $! When Who What $!- $ Set nover $ GoSub SETUP $ If P1 .eqs. "" Then P1 = "N" $ @Sys$manager:backup_subs INIT 'P1 'P4 'P5 'P6 'P7 $ If Finished Then GoTo EXIT $ Request/to=tapes "Beginning Image backup of ''CurrentDisk' on ''CurrentTape'" $ On Severe_Error Then Goto ERR_EXIT $! command = "Backup/Rewind/Image/Journal=''JourFile'/Record"- $ command = "Backup/Rewind/Image/Journal=''JourFile'"- + "/Density=6250/Ignore=Interlock"- + "/Modified/Since=Backup "- + CurrentDisk + ":/label=(''VolumeList') "+ OutputSpec $ set verify $ 'command $ set noverify $! $! $! $LOOP: $ On Warning Then Goto ERR_EXIT $ @Sys$manager:backup_subs PROCESS 'P1 'P4 'P5 'P6 'P7 $ If Finished Then GoTo EXIT $ Request/to=tapes "Beginning Image backup of ''CurrentDisk' on ''CurrentTape'" $ On Severe_Error Then Goto ERR_EXIT $! command = "Backup/NoRewind/Image/Journal=''JourFile'/Record"- $ command = "Backup/Rewind/Image/Journal=''JourFile'"- + "/Density=6250/Ignore=Interlock"- + "/Modified/Since=Backup "- + CurrentDisk + ":/label=(''VolumeList') "+ OutputSpec $ set verify $ 'command $ set noverify $ GoTo LOOP $! $!+ $! Exit Routines $!- $ERR_EXIT: $ ErrStat = $Status $ set noon $ Deas sys$output $ If ErrStat .eq. %X48084 $ Then $ Request/to=tapes "''Node' Image backup canceled." $ Else $ Request/to=tapes/Reply "''Node' Image backup terminated abnormally. Please acknowledge." $ EndIf $ Goto COMMON_EXIT $! $! $EXIT: $ set noon $ Request/to=tapes "''Node' Image Backup C O M P L E T E D, Last tape ==> ''CurrentVolume'" $ Write sys$output " " $ Write sys$output "Image B A C K U P C O M P L E T E D' $ Write sys$output " " $ Write sys$output "First tape processed ==> ''StartVolume'" $ Write sys$output "Last tape processed ==> ''CurrentVolume'" $ Write sys$output " " $! $COMMON_EXIT: $ set noon $ exit ErrStat $! $!+ $! Setup $!- $SETUP: $ Set Noon $ ErrStat = 1 $ Node = f$getsyi("NODENAME") ! get my node $ Tapes = P4 + P5 + P6 + P7 $ Set Process/name="''Node' Incr''Tapes'" $ On Warning Then Goto ERR_EXIT $ Finished == "FALSE" $ Set default sys$manager $ LockFileName == "Sys$manager:image_lock.dat" $ DskFileName == P3 $ DskTableName == "Sys$manager:image_dsk_table.dat" $ VolFileName == P2 $ if f$search(LockFileName) .eqs. "" then create 'LockFileName $ HHMMSS = f$cvtime(,,"HOUR") + f$cvtime(,,"MINUTE") + f$cvtime(,,"SECOND") $ UniqueId = f$cvtime(,,"MONTH") + f$cvtime(,,"DAY") + "_" + HHMMSS $ JourFile = "SYS$MANAGER:IMAGE_''Node'" + UniqueId + ".BJL" $ EntryNumber = F$Getqui("DISPLAY_JOB","ENTRY_NUMBER",,"THIS_JOB") $!+ $! Submit job to update the tape index now to insure it runs even if this $! job bombs. It will know when to start by use of the SYNCHRONIZE command. $!- $ Submit/Restart Sys$manager:update_tape_index/para=('EntryNumber,'JourFile,I) $ Show Time $ If p1 $ then $ Request/to=tapes "Beginning restart of IMAGE backup" $ else $ Request/to=tapes "Beginning IMAGE backup" $ EndIf $ Return ************************************************************ * $!+ $! backup_subs.com $!- $! $! Jan 5, 1990 Kevin F. Homan $! Science Applications International Corporation $! Inputs: $! Parameters: $! P1 - Function (INIT or PROCESS) $! P2 - restart value y n t f etc $! P3 - 1st drive $! P4 - 2nd drive $! P5 - 3rd drive $! P6 - 4th drive $! Symbols: (global) $! VerifyBackupSubs - Defined if set verify to be defined $! $! Outputs: $! Symbols: (global) $! Finished - True when processing complete $! CurrentDisk - Contains the current disk about to be backed up $! this is also used as the save set name $! CurrentVolume - Contains the current tape volume (ID) about to be $! backup up generally used for information $! CurrentTape - Contains the current tape drive being used $! generally used for information purposes $! OutputSpec - Contains complete output specification $! VolumeList - This is the list of tape volumes (tape IDs) $! StartVolume - First volume (Tape ID) to be used, informational. $! $! Input and Output used only by this procedure: $! CurDskRec - Current disk record symbol holding current disk $! record $! CurVolRec - Contains the current tape volume record this is $! - a tape ID or range of tape IDs $! LowRange/HighRange $! - Pointers used when a range of tape drives are $! specified these are reused and must be global $! NoMoreVolumes - Indicates there are no volumes left in the $! Tape ID volume file. Reused must be global $! SecondTime - Defined after first pass through disk file is $! complete $! SetupFlag - Defined on first call to keep init from being called $! twice $! TapeList - List of tape drives to be used for backup $! this is reused by this routine and must be global $!+ $! Modification history: $! When Who What $!- $ If F$Type(VerifyBackupSubs) .eqs. "" ! globally defined for verify $ Then $ Save_Verify = 'F$Verify(0) $ Else $ Save_Verify = 'F$Verify(1) $ EndIf $ On Error Then Goto ERR_EXIT $ If P1 .eqs. "INIT" $ Then $ Gosub SETUP $ Gosub INIT_VOLUME_LIST $ GoSub DISPLAY_TIME_STAMP $ Gosub DEFINE_OUTPUT_SPEC $ Else If P1 .eqs. "PROCESS" $ Then $ GoSub GET_CURRENT_VOLUME $ GoSub GET_VOLUME_LIST $ GoSub INDICATE_COMPLETE $ GoSub DEFINE_OUTPUT_SPEC $ Else $ Write Sys$output "P1 must be INIT or PROCESS" $ Exit 4 ! Give funny error $ EndIf $ EndIf $ Save_Verify = 'F$Verify(Save_Verify) $ Exit $! $!+ $! Exit Routines $!- $ERR_EXIT: $ ErrStat = $Status $ set noon $ Save_Verify = 'F$Verify(Save_Verify) $ Exit ErrStat $! $! $EXIT: $!+ $! Close files when we are done $!- $ set noon $ If "''F$logical("LockFile")'" .Nes. "" Then Close LockFile $ If "''F$logical("Dskfile")'" .Nes. "" Then Close Dskfile $ If "''F$logical("Volumefile")'" .Nes. "" Then Close Volumefile $ Save_Verify = 'F$Verify(Save_Verify) $ Finished == "TRUE" $ Exit $! $!+ $! Setup $!- $SETUP: $ Set noon $ If F$Type(SetupFlag) .nes. "" $ Then $ Write sys$output "Error init specified twice" $ Exit 4 ! don't call setup twice $ EndIf $ SetupFlag == "ON" $ ErrStat = 1 $ On Warning Then Goto ERR_EXIT $! $! Use LockFile to prevent overwrite of dsk_table $! $! Interesting problem here, if you start on node1 and it locks the lockfile $! then you start on node2 it can't lock the file. Now if you kill node1 $! the file is no longer locked, so if you resubmit on node1 without specifing $! restart the file isn't locked and a new separate backup begins. $! THE MORAL IS ALWAYS SPECIFY RESTART WHEN RESTARTING. $! $ If p2 $ then $ open/read/write/error=CONTINUE_SETUP LockFile 'LockFileName $ else $ open/read/write/error=CONTINUE_SETUP LockFile 'LockFileName $ copy 'DskFileName 'DskTableName $ EndIf $! $! $CONTINUE_SETUP: $!+ $! Open all needed files $!- $ Open/Read/Write/Share=Write DskFile 'DskTableName $ Open/Read/Write/Share=Read VolumeFile 'VolFileName $ Read/End_Of_File=EXIT VolumeFile CurVolRec $! Make CurVolRec global $ CurVolRec == CurVolRec $ GoSub GET_NEXT_DISK $ if P4 .eqs. "" $ then $ TapeList == ",MUA" + P3 + ":" $ else $ if P5 .eqs. "" $ then $ TapeList == ",MUA" + P3 + ":" +",MUA" + P4 + ":" $ else $ if P6 .eqs. "" $ then $ TapeList == ",MUA" + P3 + ":" + ",MUA" + P4 + ":" + ",MUA" + P5 + ":" $ else $ TapeList == ",MUA" + P3 + ":" + ",MUA" + P4 + ":" + ",MUA" + P5 + ":" + ",MUA" + P6 + ":" $ endif $ endif $ endif $ TapeStart = 0 $ CurrentTape == "MUA" + P3 + ":" $ Return $INIT_VOLUME_LIST: $ VolumeCount = 1 $ lowrange == 0 $ highrange == 0 $ NoMoreVolumes == "FALSE" $ VolumeList == F$Extract(0,4,CurVolRec) $ StartVolume == VolumeList $ Char = F$Extract(4,1,CurVolRec) $ If (Char .eqs. "-") $ Then $ LowRange == F$Integer(F$Extract(0,4,CurVolRec)) $ HighRange == F$Integer(F$Extract(5,4,CurVolRec)) $ Gosub RANGELOOP $ EndIf $ if VolumeCount .eq. 11 Then Return $ Read/End_Of_File=EXIT_INIT_VOLUME_LIST VolumeFile CurVolRec $ GoSub VOLUME_LIST_LOOP $ Return $! $! $! $GET_VOLUME_LIST: $ VolumeCount = 11 $ Index = 0 $GET_VOLUME_LOOP: $ If CurrentVolume .eqs. F$Extract(Index,4,VolumeList) Then GoTo FOUNDIT $ VolumeCount = VolumeCount - 1 $ If VolumeCount .lt. 0 $ Then $ Write Sys$output "Bad CurrentVolume" $ Write Sys$output CurrentVolume $ GoTo ERR_EXIT $ EndIf $ Index = Index + 5 $ GoTo GET_VOLUME_LOOP $FOUNDIT: $ If VolumeCount .eq. 11 Then Return $ VolumeList == F$Extract(Index,F$Length(VolumeList),VolumeList) $ GoSub VOLUME_LIST_LOOP $ Return $! $! $! $VOLUME_LIST_LOOP: $!+ $! This routine determines whether a range of tapes have been entered $! If so it tries to get the next volume from any existing range (GETRANGE) $! if no range has been entered or if the end of a range has been entered $! then another tape ID (or tape ID range) is read from the tape volume file. $!- $ Char = F$Extract(4,1,CurVolRec) $ If (Char .eqs. "-") $ Then $ GoSub GETRANGE $ Else $ VolumeList == VolumeList + "," + F$Extract(0,4,CurVolRec) $ VolumeCount = VolumeCount + 1 $ EndIf $ if VolumeCount .eq. 11 $ Then $ If LowRange .eq. HighRange Then Read/End_Of_File=EXIT_INIT_VOLUME_LIST VolumeFile CurVolRec $ Return $ EndIf $ Read/End_Of_File=EXIT_INIT_VOLUME_LIST VolumeFile CurVolRec $ GoTo VOLUME_LIST_LOOP $EXIT_INIT_VOLUME_LIST: $ VolumeList == VolumeList + ",NOMORE" $ NoMoreVolumes == "TRUE" $ Return $! $! $! $! $GETRANGE: $ if LowRange .ne. HighRange Then GoTo RangeLoop $ If NoMoreVolumes Then Return $ LowRange == F$Integer(F$Extract(0,4,CurVolRec)) $ HighRange == F$Integer(F$Extract(5,4,CurVolRec)) $ VolumeList == VolumeList + "," + F$String(LowRange) $ VolumeCount = VolumeCount + 1 $RANGELOOP: $ LowRange == LowRange + 1 $ VolumeCount = VolumeCount + 1 $ VolumeList == VolumeList + "," + F$String(LowRange) $ If LowRange .eq. HighRange Then Return $ If VolumeCount .eq. 11 Then Return $ GoTo RANGELOOP $ return $! $! $! $GET_CURRENT_VOLUME: $!+ $! This routine figures out which tape device is current and which tape id $! is current. This is used for building the backup command's output $! specification. $! $! Note if you change the device from 'MUA' here you also have to change $! the offsets assigned to TapeStart $!- $ CurrentTape == "MUA" + P3 + ":" $ TapeStart = 0 $ if f$getdvi( CurrentTape, "MNT" ) Then goto FOUNDMNT $ CurrentTape == "MUA" + P4 + ":" $ TapeStart = 6 $ if f$getdvi( CurrentTape, "MNT" ) Then goto FOUNDMNT $ CurrentTape == "MUA" + P5 + ":" $ TapeStart = 12 $ if f$getdvi( CurrentTape, "MNT" ) Then goto FOUNDMNT $ CurrentTape == "MUA" + P6 + ":" $ TapeStart = 18 $ if f$getdvi( CurrentTape, "MNT" ) Then goto FOUNDMNT $FOUNDMNT: $ CurrentVolume == f$getdvi( CurrentTape, "VOLNAM" ) $ return $! $! $! $! $DEFINE_OUTPUT_SPEC: $ TempList1 = F$Extract(0,TapeStart,TapeList) $ TempStart = TapeStart + 6 $ TempList2 = F$Extract(TempStart,F$Length(TapeList),TapeList) $ OutputSpec == CurrentTape + CurrentDisk + "/Saveset" + TempList2 + TempList1 $ return $!+ $! Rewrite the the dsk just read with "END" indicating it has be $! backed up. Then read the next disk in. $!- $INDICATE_COMPLETE: $ CurDskRec == "END" + F$Extract(3,F$length(CurDskRec),CurDskRec) $ Write/Update DskFile CurDskRec $ GoSub GET_NEXT_DISK $!fall through to display time stamp $DISPLAY_TIME_STAMP: $ TimeStamp = F$time() $ CurrentDisk == F$Edit(F$Extract(3,F$Length(CurDskRec),CurDskRec),"trim") $ Write Sys$Output " " $ Write Sys$Output "''CurrentDisk' at ''TimeStamp'" $ Write Sys$Output " " $ Return $! $!+ $! Read the next disk to backed up into CurDskRec $! $! USE LOCK TO INSURE WE DON'T BACKUP SAME DISK CONCURRENTLY WITH ANOTHER JOB $!- $GET_NEXT_DISK: $ Read/End_Of_File=CHECK_IF_FIRST_PASS/Err=READ_ERR DskFile CurDskRec $! Make CurVolRec global $ CurDskRec == CurDskRec $ State = F$Extract(0,3,CurDskRec) $ If (State .eqs. "BEG") Then Return $ GoTo GET_NEXT_DISK $! $! $READ_ERR: $!+ $!HERE TO CHECK IF ERROR STATUS IS "Record Locked" ( %X182AA ) IF SO, SOME $!OTHER JOB IS ALREADY BACKING UP THAT DISK $!- $ ErrStat = $STATUS $ If ErrStat .eq. %X182AA $ Then $ Read/NoLock/End_Of_File=CHECK_IF_FIRST_PASS DskFile CurDskRec $ GoTo GET_NEXT_DISK $ EndIf $ Goto ERR_EXIT $! $! $! $CHECK_IF_FIRST_PASS: $ If F$Type(SecondTime) .nes. "" Then GoTo EXIT $ SecondTime == 1 $ If "''F$logical("Dskfile")'" .Nes. "" Then Close Dskfile $!+ $! READ THROUGH DISK FILE AGAIN TO MAKE SURE WE DIDN'T MISS ANY DISKS $!- $ Open/Read/Write/Share=Write DskFile 'DskTableName $ GoTo GET_NEXT_DISK ************************************************************ * $!+ $! upadate_tape_index.com $!- $!+ $! UPDATE_TAPE_INDEX.COM $! Procedure to update the backup tape database $! $! P1 = Entry number of job that submitted this job, which must complete $! before this job runs. $! P2 = The backup journal file name $!- $!+ $! Load symbols in meaningful names $!- $ EntryNumber = P1 $ JourFile = P2 $ if EntryNumber .eqs. "" Then exit $ Set Noon $ Synchronize/Entry='EntryNumber $ If JourFile .eqs. "" Then exit $ If (f$search(JourFile) .eqs. "") then exit $ uj :==$read_jnl $ uj 'JourFile $ Exit *********************************************************** C+ C Jour.inc C- Structure /JourStructure/ Integer*4 Inblk Integer*4 Inbyte Integer*4 Count Integer*2 StrucLev Character*512 Buffer End Structure Structure /BufStruc/ Union Map Character*512 Char End Map Map Byte Byte(512) End Map Map Integer*2 Word(256) End Map End Union End Structure Parameter bjl$b_size = 1 Parameter bjl$b_type = 2 Parameter bjl$b_data = 3 Parameter bjl$w_data = 2 Parameter bjl$c_struc_len = 2 Parameter bjl$c_volume_len = 14 Parameter bjl$c_dir_len = 255 Parameter bjl$c_file_len = 133 Parameter bjl$k_struclev = 0 Parameter bjl$k_ssname = 1 Parameter bjl$k_volume = 2 Parameter bjl$k_directory = 3 Parameter bjl$k_file = 4 Parameter bjl$k_level1 = 257 Parameter bjl$k_level2 = 258 *********************************************** C+ C read_jnl.for C- Options /EXTEND_SOURCE Program Read_jnl C+ C Version: V01.000 C Date: 22-FEB-1991 C C Kevin F. Homan C Science Applications, Inc. C 800 Oak Ridge Turnpike C Oak Ridge, TN 37830 C (615) 482-9031 C C This program will read a backup journal much in the same way as C backup/list/jour will and is based on the the backup version of the C same. The program will read through the journal file byte by byte C decompressing the data as it goes. The algorithm that does the decompression C is buried within the ReadRecord and ReadByte routines. Little effort C here in deciphering the algorithm, it is based on the backup program C that does the same. As each record is unpacked its 'TYPE' is examined C and the data is displayed appropriately. C C The program is meant to be used for a template for maintaining an C indexed file of backups. C- Include 'Jour.inc' Record /JourStructure/ Jour Integer*4 SSDate(2),ReadBlock,OpenBlock Integer*4 Status,ReadRecord,Len,BufFlags Integer*2 VolNum Byte SSNameLen,DirLen,VolumeLen,BufSize External SS$_ENDOFFILE,SS$_ABORT,OpenBlock Character*80 FileName Character*41 SSName Character*14 Volume Character*257 dir Character*23 DateTime Logical Change Record /BufStruc/ Buf Equivalence (SSDate,SSName) Equivalence (VolNum,Volume(13:14)) Parameter InpLun=1 Call Lib$Get_Foreign(FileName,'File name: ',Len) Open ( Unit=Inplun, File=FileName ( 1:Len), 1 Form='UnFormatted', 2 Status='Old') Jour.Inblk = 1 Type *,' ' Type *,' ' Type *,' ' Read(InpLun) Jour.Buffer Status = ReadRecord(Jour,Buf,InpLun) Do While (Status) Goto (1000,2000,3000,4000,5000),Buf.Byte(bjl$b_type)+1 C Type struc, store for later 1000 GoTo 20000 C Type ssname, store for later use 2000 SSNameLen = Buf.Byte(bjl$b_size) - 1 SSName = Buf.Char(bjl$b_data:bjl$b_data+SSNameLen) Change = .True. GoTo 20000 C volume store for later 3000 VolumeLen = Buf.Byte(bjl$b_size) - 1 Volume = Buf.Char(bjl$b_data:bjl$b_data+VolumeLen) Change = .True. GoTo 20000 C directory store for later 4000 DirLen = Buf.Byte(bjl$b_size) - 1 Dir = Buf.Char(bjl$b_data:bjl$b_data+DirLen - 1) C Type *,Dir(1:DirLen) GoTo 20000 C file, ok display if first file of new saveset or volume then display C all the information. Otherwise just display filespec. 5000 If (Change) Then Call sys$asctim(,DateTime,SSDate,) Type *,'Save set '//SSName(9:SSNameLen)//' created on '//DateTime Type *,'Volume number ',VolNum,', volume label '//Volume(1:12) Change = .False. EndIf BufSize = Buf.Byte(bjl$b_size) If (Jour.StrucLev .gt. bjl$k_level1) Then BufSize = BufSize - 4 BufFlags = Buf.Byte(BufSize+1) Else BufFlags = 0 EndIf Buf.Char(DirLen+3:DirLen+3+BufSize -1) = Buf.Char(bjl$b_data:BufSize) Buf.Char(1:1) = '[' Buf.Char(2:DirLen+2) = Dir Buf.Char(DirLen+2:DirLen+2) = ']' If (Buf.Char(1:DirLen+2+BufSize) .ne. ' ') - Type *,' '//Buf.Char(1:DirLen+2+BufSize) GoTo 20000 C Get the next logical record 20000 Status = ReadRecord(Jour,Buf,InpLun) EndDo If (.not. Status) then if (status .eq. %loc(ss$_endoffile)) then type *,'End of file read' else Call lib$STOP(%Val(Status)) end if end if Close (InpLun) end Options /EXTEND_SOURCE Function ReadRecord(Jour,Buf,InpLun) C+ C This routine reads a logical backup record from the backup journal file C- Include 'Jour.inc' Record /JourStructure/ Jour Integer*4 ReadRecord,ReadByte,I,Tmp1,Tmp2,Tmp3,Lib$Locc,InpLun Byte LeadingNulls External SS$_NORMAL,SS$_ABORT,Lib$Locc Record /BufStruc/ Buf Record /BufStruc/ Dir C C Standard buffer format |size|type|data| C C size is determined by XORing previous size C ReadRecord = ReadByte(Jour,Buf.Char(1:1),InpLun) If (.not. ReadRecord) Return Jour.Count = Jour.Count .xor. Buf.Byte(1) Do While (Jour.Count .eq. 0) ReadRecord = ReadByte(Jour,Buf.Char(1:1),InpLun) If (.not. ReadRecord) Return Jour.Count = Jour.Count .xor. Buf.Byte(1) EndDo Buf.Byte(1) = Jour.Count If (Jour.Inbyte+Jour.Count .ge. 512) Then Do I = 2,Jour.Count+1 ReadRecord = ReadByte(Jour,Buf.Char(I:I),InpLun) If (.not. ReadRecord) Return EndDo Else Buf.Char(2:) = Jour.Buffer(Jour.Inbyte+1:Jour.Inbyte+Jour.Count) Jour.Inbyte = Jour.Inbyte + Jour.Count EndIf ReadRecord = %Loc(SS$_ABORT) Goto (1000,2000,3000,4000,5000),Buf.Byte(bjl$b_type) + 1 C bjl$k_struclev = 0 1000 If (Buf.Byte(bjl$b_size) .ne. Bjl$c_struc_len + 1) Then type *,'Error Struclev Invalid Size' type *, Jour.Inblk Return Else If ((Buf.Word(bjl$w_data) .ne. Bjl$k_level1) .and. - (Buf.Word(bjl$w_data) .ne. 0) .and. - (Buf.Word(bjl$w_data) .ne. Bjl$k_level2)) Then Type *, 'Error Invalid Level' Return EndIf EndIf Jour.StrucLev = Buf.Word(bjl$b_size) ReadRecord = %Loc(SS$_NORMAL) Return C bjl$k_ssname = 1 C C Contains creation date followed by saveset name must be large enough C to hold creation date (quad) plus size ie 9 C 2000 If ((Buf.Byte(bjl$b_size).lt.9).or.(Buf.Byte(bjl$b_size).gt.41)) Return ReadRecord = %Loc(SS$_NORMAL) Return C bjl$k_volume = 2 3000 If (Buf.Byte(bjl$b_size) .ne. bjl$c_volume_len+1) Return ReadRecord = %Loc(SS$_NORMAL) Return C bjl$k_directory = 3 4000 LeadingNulls = Buf.Byte(bjl$b_data) If ((Buf.Byte(bjl$b_size) .lt. 2) .or. - (Buf.Byte(bjl$b_size)+LeadingNulls .gt. bjl$c_dir_len)) Return C Compute XOR of bytes in record and bytes of context to develop true value Do I = bjl$b_data+1,Jour.Count+1 Buf.Byte(I) = Buf.Byte(I) .xor. Dir.Byte(I+LeadingNulls-Bjl$b_data) EndDo C Move non-null data in record to appropriate offset If (LeadingNulls .ne. 1) - Buf.Char(bjl$b_data+LeadingNulls:bjl$b_data+LeadingNulls+Jour.Count-1)= - Buf.Char(bjl$b_data+1:bjl$b_data+Jour.Count-1) C Establish leading portion of directory string from context value If (LeadingNulls .ne. 0) - Buf.Char(bjl$b_data:bjl$b_data+LeadingNulls-1)= - Dir.Char(1:LeadingNulls) C Establish trailing portion of directory string from context value Tmp1 = bjl$c_dir_len - LeadingNulls + Jour.Count - 1 Tmp2 = LeadingNulls + Jour.Count - 1 Tmp3 = bjl$b_data + Tmp2 - 1 Buf.Char(Tmp3:Tmp3 + Tmp1) = Dir.Char(Tmp2:Tmp2 + Tmp1) C Save true value for next directory record Dir.Char(1:bjl$c_dir_len) = Buf.Char(bjl$b_data:bjl$b_data+bjl$c_dir_len) C Strip trailing zeroes Buf.Byte(bjl$b_size) = bjl$c_dir_len ! Assume no null Tmp1 = Lib$Locc(Char(0),Buf.Char(bjl$b_data:bjl$c_dir_len)) If (Tmp1 .ne. 0) Buf.byte(bjl$b_size) = Tmp1 ReadRecord = %Loc(SS$_NORMAL) Return C bjl$k_file = 4 5000 If (Buf.Byte(bjl$b_size) .gt. bjl$c_file_len+1) Return ReadRecord = %Loc(SS$_NORMAL) Return End Function ReadByte(Jour,Buffer,InpLun) C+ C Returns a byte from the input stream C- Include 'Jour.inc' Record /JourStructure/ Jour Character*1 Buffer Integer*4 ReadByte,InpLun External SS$_ABORT,SS$_NORMAL,SS$_ENDOFFILE ReadByte = %Loc(SS$_NORMAL) Jour.Inbyte = Jour.Inbyte + 1 If (Jour.Inbyte .ge. 513) Then Jour.Inbyte = 1 Jour.Inblk = Jour.Inblk + 1 Read (InpLun,End=100) Jour.Buffer EndIf Buffer = Jour.Buffer(Jour.Inbyte:Jour.Inbyte) Return 100 ReadByte = %loc(ss$_endoffile) End