/*------------------------------------------------------------------+ | Copyright (c) 2001 by SAS Institute Inc., Cary N.C. 27513 USA | |-------------------------------------------------------------------| | | | NAME: HOTFIX | | DATE: 04apr2001 | | AUTHOR: David Caudle | | SUPPORT: David Caudle | | PURPOSE: Installs a hotfix. | | USAGE: | | HOTFIX fn > ( | | -------- | | NOTES: | | 1) BACKUP will create backup copies of anything that | | is replaced on the SAS system disk; NOBACKUP is the | | default | | | | 2) Prerequisite modules: | | VMARC | | | | 3) fn should be of the form rrPPxxCM where: | | rr = release (eg, 81 = 8.1) | | PP = product (eg, BA = SAS/BASE) | | xx = number assigned to hotfix | | | | 4) global variables: | | | | globals.infile = fn ft fm of the VMARC file | | globals.product = product associated with hotfix | | (the LOADLIB name of the product) | | globals.sasfm = fm of the sas disk | | globals.sas.space = blksiz blksleft blkstot of sasfm | | globals.sas.need = estimated number of blocks needed | | globals.sas.sfs = boolean if sas disk is sfs or not | | globals.sas.updt = boolean if a file on the sas disk | | has been updated (only needed if | | BACKUP option specified) | | globals.workfm = fm of the work disk | | globals.work.space =blksiz blksleft blkstot of workfm | | globals.work.need = estimated number of blocks needed | | globals.work.sfs = boolean if work disk is sfs or not | | globals.opts. = value for option | | globals.cmstype = initial user setting of CMSTYPE | | globals.error = bad return code if encountered | | globals.warn = 4 if warning, 0 if not | | globals.estring = error message with which to exit | | globals.max = size in bytes of largest file extracted | | globals.file.0 = number of files associated w/hotfix | | globals.file.1 = fn ft fm bi bo of first file | | globals.file.2 = fn ft fm bi bo of 2nd file | | etc.. (bi=bytes in; bo=bytes out) | | globals.backup.0 = number of backup files | | globals.backup.1 = fn ft fm of first backup file | | globals.backup.2 = fn ft fm of 2nd backup file | | etc.. | | globals.trace = boolean for existence of trace file | | globals.support = list of valid filetypes for input | | | | 5) HOTFIX TRACE file contains the output of all | | the LOADLIB copies executed during the exec | | | | 6) DBC - in the summer of 2001 I added code to check on | | the available space of the sas disk and work disk. | | This revealed a problem in the CMS File System Record | | Manager: while Q LIMITS showed enough space avail | | in an SFS filepool to copy a particular-sized member | | from one loadlib to another, a DMS107S occurred. | | It appeared more than just appending the one member | | to the new loadlib was happening. This problem was | | opened with IBM TS under 568411201; it turned out | | to be DMSRCM and DMSERD problems. Rather than an | | APAR, it was marked "fixed in next" release and | | marked IBM-internal PITS 2T0K537. This was in August | | of 2001. So any customers using SFS directories will | | not see any benefit from my space-checking until they | | get the fix from IBM; it will simply be as if they | | were still running the old HOTFIX exec without space | | checking. Note this space-checking sat in my playpen | | until Jan 2002 when necessary updates were needed to | | the exec (MESSAGE file and bundle hotfix processing) | | | | 7) DBC - note - Future hot fixes may require the | | addition of code to process MODULE files | | | | HISTORY: | | date who action | | _______ ___ _________________________________________ | | 04apr01 DBC Initial coding | | 13jun01 DBC Added datetime stamp in audit file | | 18jun01 DBC Added catalog and bundle processing | | 11jan02 DBC Added: | | -MESSAGE file processing | | -BASE hotfix bundle processing | | -code to check for available space (see | | note '6' above) | | -globals.sas.updt | | 14jan02 DBC Special processing for base common files | | 15may02 DBC STAT hotfix bundle processing | | 17jun02 RJB Added BB product code for Base hot fixes | | 02jul02 DBC Added SQ product for DB2 VM Server | | 15jul04 RJB Added BC product code for Base hot fixes | | | +------------------------------------------------------------------*/ trace o parse arg parms '(' options /*-------------------------------------------------------------------+ | Initialization | +-------------------------------------------------------------------*/ 'QUERY CMSTYPE (STACK LIFO' pull . . globals.cmstype . /* user setting of cmstype */ 'SET CMSTYPE HT' globals.error = 0 globals.warn = 0 globals.trace = 0 globals.sas.updt = 0 globals.infile = checkfn(parms) globals.product = getprod(globals.infile) call checkopts options 'STATE VMARC MODULE *' if rc ^= 0 then exitrc(28, 'VMARC MODULE not found. You may download this executable', ||' at http://www.vm.ibm.com/download') 'SET CMSTYPE RT' say 'CMS Hotfix Installation running...'; say; /*-------------------------------------------------------------------+ | SAS system disk? check for validity | +-------------------------------------------------------------------*/ say 'Enter disk mode for which the SAS system minidisk is accessed:' parse pull usasfm . globals.sasfm = checkfm('s',usasfm); /*-------------------------------------------------------------------+ | Work disk? check for validity | +-------------------------------------------------------------------*/ say 'Enter disk mode for work area:' say ' NOTE: You can use the SAS system minidisk as the work area' say ' but ONLY if you have not specified the BACKUP option' parse pull uworkfm . globals.workfm = checkfm('w',uworkfm); 'SET CMSTYPE HT' 'ERASE HOTFIX TRACE' globals.workfm /* start w/clean trace file */ 'SET CMSTYPE RT' /*-------------------------------------------------------------------+ | extract the files | +-------------------------------------------------------------------*/ globals.file.0 = 0 /* number of files extracted */ globals.support = 'LOADLIB SASHELP AUDIT TXT MESSAGE' /* if anything gets added to the above line, then processing */ /* files code block (below) needs updating as well */ say; say 'Extracting files...' 'MAKEBUF' 'VMARC UNPACK' globals.infile '= =' globals.workfm '(REP TRACE LIFO' if rc ^= 0 then call seterror rc, 'Error' rc 'from VMARC attempting to unpack', globals.infile else call postvmarc; /* process file info stored in the stack */ 'DROPBUF' if globals.error ^= 0 then exitrc() /*-------------------------------------------------------------------+ | 'nuf space? we only need to check for additional space needed on | | the work disk if the backup option is spec'd | +-------------------------------------------------------------------*/ globals.sas.space = spacea(globals.sasfm) /*---say 'SAS have:' globals.sas.space say 'SAS need:' globals.sas.need---*/ parse var globals.sas.space . avail tot globals.sas.sfs . if globals.sas.need > avail | tot = 0 then call continue 's' if globals.workfm ^= globals.sasfm then do globals.work.space = spacea(globals.workfm) /*--say 'WORK have:' globals.work.space say 'WORK need:' globals.work.need---*/ parse var globals.work.space . avail tot globals.work.sfs . if globals.work.need > avail | tot = 0 then call continue 'w' end else do globals.work.space = globals.sas.space globals.work.sfs = globals.sas.sfs end /*-------------------------------------------------------------------+ | process each file | | note: make sure all filetypes are listed in globals.support | +-------------------------------------------------------------------*/ say; say 'Processing files...' do i = 1 to globals.file.0 parse var globals.file.i fn ft fm bi bo . bfield = length(globals.max) say ' ' left(fn,8) left(ft,8) left(fm,2) ' (bytes in='||, right(bi,bfield) ' bytes out='||right(bo,bfield)||')' select when ft = 'LOADLIB' then call ploadlib globals.file.i when ft = 'SASHELP' | ft = 'MESSAGE' then call pwx globals.file.i when ft = 'AUDIT' then call paudit globals.file.i when ft = 'TXT' then call ptext globals.file.i otherwise exitrc(12, 'Unsupported filetype' ft 'in input file') end end /*-------------------------------------------------------------------+ | Termination | +-------------------------------------------------------------------*/ if globals.opts.backup then call makebkup parse var globals.infile fn . if globals.trace = 1 then do 'COPY HOTFIX TRACE' globals.workfm fn '=' globals.sasfm '(REP' if rc ^= 0 then call setwarn 'RC' rc 'from COPYFILE moving the TRACE file from '||, 'the work disk to the SAS system disk' else 'ERASE HOTFIX TRACE' globals.workfm end say; say 'CMS Hotfix Installation complete.' 'SET CMSTYPE' globals.cmstype exitrc() /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: postvmarc() | | PURPOSE: post process of VMARC unpack information in the stack | | sets globals on estimated space needed | | NOTES: | | at this point, all files have been successfully | | unpacked from the VMARC file to either the sas or | | work disk without encountering a disk full. because | | each file is processed one at a time and then | | discarded or backed up, the amount of extra space | | that is needed is: | | | | A) if the install is directly to the sas disk: | | on the sas disk, we need room for: | | 1) an extra copy of each executable which will | | be treated as the number of bytes the loadlib | | will grow by (the actual number of growth | | bytes will be smaller but this errs on the | | side of caution | | 2) an extra block for an extended audit file | | 3) extra room for the trace file | | | | | | B) if the install is to a work disk first without | | creating backups, then on the sas disk we need the | | same as described in (A) plus: | | 1) room for an extra copy of the largest non- | | loadlib file (each one of these files is copied | | from the work disk to the sas disk with a | | COPYFILE (REPLACE | | | | (note that we don't need this in the case of | | (A) because the non-loadlib files are already | | there via the VMARC extraction with replace) | | | | and on the work disk we simply need: | | 1) extra room for the trace file | | | | C) if the install creates backups, then on the sas | | disk we need the same as described in (A) plus: | | 1) room for the larger of the following: | | an extra copy of the largest non-loadlib | | file or an extra copy of the vmarc input file | | | | and on the work disk we need room for: | | 1) copy of the current audit file + 1 block | | 2) extra copy of the largest file | | (loadlib or non-loadlib) | | 3) the larger of the following: | | yet another copy of the largest file or an | | extra copy of the vmarc input file | | | +------------------------------------------------------------------*/ postvmarc: procedure expose globals. globals.max = 0 globals.work.need = 0 /* how much space we need on work */ globals.sas.need = 0 /* how much space we need on sas */ parse var globals.sas.space sblksiz sblklft sblktot . parse var globals.work.space wblksiz wblklft wblktot . maxnl = 0 /* largest non-loadlib file */ vmarcsize = 0 /* running total of size of the vmarc file */ ldlbcopys = 0 /* running total of the number of loadlib */ /* copies that will be performed; a good */ /* estimate of the size of the trace file */ /* is 4k * (trunc(ldlbcopys / 5) + 1) */ /* so a good err-to-side-of-caution is: */ /* 4k * (trunc(ldlbcopys / 4) + 1) */ globals.file.0 = queued() do i = globals.file.0 to 1 by -1 pull fn ft fm '.' . . bytesin ',' . . bytesout . vmarcsize = vmarcsize + bytesin if find(globals.support,ft) = 0 then call seterror 12, 'Unsupported filetype' ft 'in input file' globals.file.i = fn ft fm bytesin bytesout if bytesout > globals.max then globals.max = bytesout /* the following computation of how much space will be needed */ /* errs on the side of caution */ if ft = 'LOADLIB' then do globals.sas.need = globals.sas.need + trunc(bytesout/sblksiz) +2 ldlbcopys = ldlbcopys + 1 if globals.opts.backup = 1 then ldlbcopys = ldlbcopys + 1 end else if bytesout > maxnl then maxnl = bytesout end /* note we do NOT need to add the size for a 2nd copy of EACH */ /* file in the case of backups, because as each file is backed */ /* up (under a backup name) from the sas disk, the existing */ /* file on the work disk is copied to the sas disk, then */ /* erased, and then the backup copy is renamed. the only */ /* window of possible limitation is the brief period of time */ /* that the largest file has both its original and backup */ /* copies existing on the work disk simultaneously until the */ /* (new vmarc) copy is erased and only the backup copy remains */ /* (to later be vmarc'd with all the other backup files) */ /* space for audit file */ globals.sas.need = globals.sas.need + 2 if globals.opts.backup = 1 then do 'MAKEBUF' 'SET CMSTYPE HT' 'LISTFILE HOTFIX AUDIT' globals.sasfm '(ALLOC LIFO' if rc = 0 then parse pull . . . . . . blocks . else blocks = 0 'SET CMSTYPE RT' 'DROPBUF' blocks = format(blocks * (sblksiz/wblksiz),,0) globals.work.need = globals.work.need + blocks + 2 end /* space for new trace file; again, err on side of caution */ globals.sas.need = globals.sas.need +, (4096 * (trunc(ldlbcopys / 4) + 1)) / sblksiz globals.work.need = globals.work.need +, (4096 * (trunc(ldlbcopys / 4) + 1)) / wblksiz if globals.opts.backup = 1 then do /* additional sas disk space... */ estimate = trunc((vmarcsize / sblksiz) * 1.03) + 1 maxnl = trunc(maxnl / sblksiz) + 1 if estimate > maxnl then globals.sas.need = globals.sas.need + estimate else globals.sas.need = globals.sas.need + maxnl /* for estimate of additional space needed on the work disk */ /* if the backup option is being used, choose the larger of: */ /* 1) current vmarc size + largest extracted file */ /* 2) twice the size of the largest extracted file */ estimate = trunc((vmarcsize / wblksiz) * 1.03) + 1 maxblks = trunc(globals.max / wblksiz) + 1 if estimate > maxblks then globals.work.need = globals.work.need + estimate + maxblks else globals.work.need = globals.work.need + (2 * maxblks) end else if globals.sasfm ^= globals.workfm then do maxnl = trunc(maxnl / sblksiz) + 1 globals.sas.need = globals.sas.need + maxnl end return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: spacea(fm) | | PURPOSE: returns a string of 4 numbers for a disk or sfs dir: | | blksize blksleft blkstotal type | | type = 0 for disk and 1 for sfs | | NOTES: if for some reason we cannot get these numbers, | | blkstotal is set to zero | +------------------------------------------------------------------*/ spacea: procedure expose globals. parse arg fm 'MAKEBUF' 'QUERY DISK' fm '(LIFO' parse pull label 7 vdev . . . . blksize . . blksleft blkstotal . 'DROPBUF' if datatype(vdev,'X') = 0 then do /* sfs directory */ type = 1 blksize = 4096 'MAKEBUF' 'QUERY ACCESSED' fm '(LIFO' parse pull . . . . directory . 'DROPBUF' parse var directory fpool ':' fspace '.' 'MAKEBUF' 'QUERY LIMITS FOR' fspace fpool||': (LIFO' if rc = 0 then do parse pull . . blkstotal committed '-' . '%' threshold '%' /*---say 'total blocks left =' blkstotal - committed---*/ blksleft = trunc((blkstotal - committed) * (threshold / 100)) /*---say 'threshold blocks left =' blksleft---*/ end else blkstotal = 0 /* unable to get the info */ 'DROPBUF' end else type = 0 return blksize blksleft blkstotal type /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: continue('s' | 'w') | | PURPOSE: prompts the user to continue if there is not enough | | estimated space on the sas 's' or work 'w' disk | | NOTES: | +------------------------------------------------------------------*/ continue: procedure expose globals. parse arg type if type = 's' then do disk = 'SAS' mode = globals.sasfm need = globals.sas.need sfs = globals.sas.sfs parse var globals.sas.space blksz have total . end; else do disk = 'work' mode = globals.workfm need = globals.work.need sfs = globals.work.sfs parse var globals.work.space blksz have total . end; say; say 'The estimated number of blocks needed on the' disk 'disk'||, ' is' need 'blocks'; say if total = 0 then say 'HOTFIX was unable to determine exactly how '||, 'much space is available on that disk' else do say 'Currently there are' have 'blocks available on that disk' if sfs = 1 then say 'before your current THRESHOLD for the filepool is exceeded' end if type = 's' then do say; say 'It is STRONGLY recommended that you NOT continue'; say end say; say 'If you stop the exec now, no processing of files on '||, 'the SAS disk has occurred,' say 'and you can safely reinvoke HOTFIX'||, ' after obtaining more space for the' disk 'disk:'; say if sfs = 1 then do 'MAKEBUF' 'QUERY ACCESSED' mode '(LIFO' parse pull . . . . directory . 'DROPBUF' parse var directory fpool ':' fspace '.' 'Q ACCESSED' mode 'QUERY LIMITS FOR' fspace fpool||':' end else 'Q DISK' mode say; say 'Continue? (Yes | No)' pull resp . if abbrev('YES',resp,1) = 0 then exitrc(40, 'Terminating exec due to user request') return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: makebkup() | | PURPOSE: creates the VMARC backup file | | NOTES: | +------------------------------------------------------------------*/ makebkup: procedure expose globals. parse var globals.infile fn . say; say 'Creating backup file...' backfile = fn 'BACKUP' globals.workfm 'SET CMSTYPE HT' 'ERASE' backfile do i = 1 to globals.backup.0 'VMARC PACK' globals.backup.i backfile '(NOTRACE APPEND' if rc ^= 0 then exitrc(28, 'RC' rc 'from VMARC creating backup file while '||, 'processing' globals.backup.i) 'ERASE' globals.backup.i end if globals.workfm ^= globals.sasfm then do 'COPY' backfile fn 'BACKUP' globals.sasfm '(REP' if rc ^= 0 then exitrc(28, 'RC' rc 'from COPYFILE moving backup file from '||, 'the work disk to the SAS system disk') end 'ERASE' backfile 'SET CMSTYPE RT' return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: ptext(fn ft fm) | | PURPOSE: process the TXT file within the VMARC file | | NOTES: just a NOP for now | +------------------------------------------------------------------*/ ptext: procedure expose globals. parse arg fn ft fm . /* fm = globals.workfm */ return /* ft = TXT */ /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: paudit(fn ft fm) | | PURPOSE: process the AUDIT file within the VMARC file | | NOTES: | +------------------------------------------------------------------*/ paudit: procedure expose globals. parse arg fn ft fm . /* fm = globals.workfm */ 'SET CMSTYPE HT' /* ft = AUDIT */ status = state('HOTFIX' ft globals.sasfm) parse var status chk . if chk = 'NOT' then do /* assume this is the first time this */ /* customer has applied a hotfix */ newfile.1 = '*******************' newfile.2 = 'HOTFIX AUDIT file - DO NOT ERASE or MODIFY' newfile.3 = '*******************' 'EXECIO 3 DISKW HOTFIX' ft globals.sasfm '1 V (STEM NEWFILE. FINIS' if rc ^= 0 then exitrc(28, 'RC' rc 'from EXECIO creating'||, ' HOTFIX' ft globals.sasfm) end timestamp = date() time() 'EXECIO 1 DISKW HOTFIX' ft globals.sasfm '0 V (VAR TIMESTAMP FINIS' if globals.opts.backup then do globals.backup.0 = globals.backup.0 + 1 backupnum = globals.backup.0 'COPY HOTFIX' ft globals.sasfm 'HOTFIX' ft fm '(REP OLDD' if rc ^= 0 then exitrc(28, 'RC' rc 'from COPYFILE 1 making '||, 'backup of HOTFIX' ft globals.sasfm) globals.backup.backupnum = 'HOTFIX' ft fm end 'COPY' fn ft fm 'HOTFIX' ft globals.sasfm '(APPEND' if rc ^= 0 then call setwarn 28, 'RC' rc 'from COPYFILE appending '||, fn ft 'to the HOTFIX' ft 'file on the SAS system disk' else 'ERASE' fn ft fm /* erase 81ba01cm audit a, eg */ 'SET CMSTYPE RT' return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: pwx(fn ft fm bytesin bytesout) | | PURPOSE: process CATALOGs and MESSAGE files from the hotfix | | VMARC file | | NOTES: | +------------------------------------------------------------------*/ pwx: procedure expose globals. parse arg fn ft fm . /* fm = globals.workfm */ samedisk = 0 /* ft = SASHELP or MESSAGE */ if globals.workfm = globals.sasfm then samedisk = 1 'SET CMSTYPE HT' if globals.opts.backup then do globals.backup.0 = globals.backup.0 + 1 backupnum = globals.backup.0 'COPY' fn ft globals.sasfm fn 'HFBACKUP' fm '(REP OLDD' if rc ^= 0 then exitrc(28, 'RC' rc 'from COPYFILE 1 making '||, 'backup of' fn ft globals.sasfm) globals.backup.backupnum = fn ft fm end if ^samedisk then do 'COPY' fn ft fm fn ft globals.sasfm '(REP OLDD' if rc ^= 0 then exitrc(28, 'RC' rc 'from COPYFILE moving '||, 'catalog' fn ft 'to the SAS system disk') else do globals.sas.updt = 1 'ERASE' fn ft fm end end if globals.opts.backup then do 'COPY' fn 'HFBACKUP' fm fn ft fm '(REP OLDD' if rc ^= 0 then call setwarn 'RC' rc 'from COPYFILE 2 making '||, 'backup of' fn ft globals.sasfm else 'ERASE' fn 'HFBACKUP' fm end 'SET CMSTYPE RT' return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: ploadlib(fn ft fm bytesin bytesout) | | PURPOSE: process LOADLIB executable from hotfix VMARC file | | NOTES: | +------------------------------------------------------------------*/ ploadlib: procedure expose globals. parse arg fn ft fm . /* fm = globals.workfm */ 'SET CMSTYPE HT' /* ft = LOADLIB */ bundle = 0 basecom = 0 if substr(fn,3,1) = 'B' then do bundle = 1 globals.product = 'SABBASE' /* TEMPORARY change to process */ end /* the bundle */ else if globals.product = 'SASCONNE' |, globals.product = 'SASSHARE' then do if basecom(fn) then do /* only call basecom() if this is */ basecom = 1 /* connect or share; if the file */ saveprod = globals.product /* belongs in SASBASE, temporary */ globals.product = 'SASBASE' /* change the product to process */ end /* the base 'common' file */ end if globals.opts.backup then do 'ERASE HFBACKUP' ft fm globals.backup.0 = globals.backup.0 + 1 backupnum = globals.backup.0 'MAKEBUF' queue 'SELECT' queue fn queue '' 'LOADLIB COPY' globals.product ft globals.sasfm ' '||, 'HFBACKUP' ft fm '(MODIFY DISK' lrc = rc 'DROPBUF' if lrc ^= 0 then exitrc(28, 'RC' lrc 'from LOADLIB COPY making '||, 'backup of' fn 'from' globals.product 'LOADLIB') status = state('LOADLIB LISTING *') parse var status chk . llfm . if chk = 'NOT' then call setwarn 'LOADLIB LISTING was not produced during backup of ', || fn ft fm 'COPY LOADLIB LISTING' llfm 'HOTFIX TRACE' fm '(APP' if rc ^= 0 then call setwarn 'RC' rc 'from COPYFILE creating '||, 'trace file of backup process for' fn ft fm else globals.trace = 1 globals.backup.backupnum = fn ft fm end 'MAKEBUF' queue 'SELECT' queue fn||'(R)' queue '' 'LOADLIB COPY' fn ft fm globals.product ft globals.sasfm '(REPLACE DISK' lrc = rc 'DROPBUF' /* note: in testing it was discovered that if the user has write auth | to the directory globals.sasfm but not write auth to the existing | files within globals.sasfm, an open code error of 24 will occur | on the loadlib, but a system rc of 28 will be issued--lesson: | a rc of 28 at this point may indicate the user does not have | write authoriy on the files within the directory globals.sasfm | 14nov01 dbc */ if lrc ^= 0 then exitrc(28, 'RC' lrc 'from LOADLIB COPY moving '||, 'hotfix executable' fn 'to' globals.product ft globals.sasfm) globals.sas.updt = 1 status = state('LOADLIB LISTING *') parse var status chk . llfm . if chk = 'NOT' then call setwarn 'LOADLIB LISTING was not produced during copy of ', || fn 'to' globals.product ft globals.sasfm parse var status . . llfm . 'COPY LOADLIB LISTING' llfm 'HOTFIX TRACE' fm '(APP' if rc ^= 0 then call setwarn 'RC' rc 'from COPYFILE creating '||, 'trace file for creation of' fn 'in' globals.product ft else do globals.trace = 1 'ERASE LOADLIB LISTING' llfm end 'SET CMSTYPE RT' if globals.opts.backup then do 'COPY' 'HFBACKUP' ft fm fn ft fm '(REP OLDD' if rc ^= 0 then exitrc(28, 'RC' rc 'from COPYFILE creating '||, 'backup of hotfix executable' fn 'on the work disk') else 'ERASE HFBACKUP' ft fm end else 'ERASE' fn ft fm if bundle = 1 then globals.product = 'SASBASE' /* reset TEMP change before exit */ else if basecom = 1 then globals.product = saveprod /* reset TEMP change before exit */ return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: basecom(fn) | | PURPOSE: check 'fn' against a list of known modules that exist | | in SASBASE that are common among two products | | NOTES: returns (1) if the file is common (0) if not | | the list is not exhaustive and should be maintained | | as new share and/or connect hotfixes mandate | +------------------------------------------------------------------*/ basecom: procedure parse arg fn . commons = ' ' ||, 'SASCSRV ' ||, 'SASIOINV ' ||, 'SASIORMT ' ||, 'SASI6RMT ' ||, 'SASLOADD ' ||, 'SASSHRAE ' ||, 'SASWXXC ' ||, 'SASXDMR ' ||, 'SASYHERI ' ||, 'SASYHRE6 ' ||, 'SASYHRE7 ' ||, 'SASYHROP ' ||, 'SASYHRSQ ' ||, 'SASYHRSV ' ||, 'SASYHRWT ' ||, ' ' if find(commons,fn) = 0 then return 0 else return 1 /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: state(fn ft fm) | | PURPOSE: returns the string: fn ft fm recfm lrecl | | NOTES: fm can be * on input | | if bad return code from LISTFILE, the string 'NOT | | FOUND' is returned | | calling routine should set cmstype HT if appropriate | +------------------------------------------------------------------*/ state: procedure parse arg fn ft fm . 'MAKEBUF' 'LISTFILE' fn ft fm '(FORMAT STACK FIFO' if rc ^= 0 then do 'DROPBUF' return 'NOT FOUND' end pull fn ft fm recfm lrecl . 'DROPBUF' return fn ft fm recfm lrecl /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: checkfn(filename) | | PURPOSE: checks user-supplied filename for the input file | | and returns fn ft fm | | NOTES: | +------------------------------------------------------------------*/ checkfn: procedure expose globals. parse arg fn ft fm other . if other ^= '' then exitrc(24, 'Extraneous input parameter' other) if fn = '' then exitrc(24, 'No parameters supplied') if ft = '' then ft = 'VMARC' else if bitor(ft,' ',' ') ^= 'VMARC' then exitrc(24, 'File type must be VMARC') if fm = '' then fm = '*' status = state(fn ft fm) parse var status chk . . recfm lrecl . if chk = 'NOT' then exitrc(28, 'File "'||fn ft fm ||'" does not exist') if lrecl ^= 80 then exitrc(12, 'File "'||fn ft fm ||'" does not have a record length of 80') return bitor(fn,' ',' ') 'VMARC' bitor(fm,' ',' ') /* all upcase*/ /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: checkfm(type,filemode) | | PURPOSE: checks user-supplied filemode for R/W status and | | returns validated filemode | | NOTES: type is 's' for sas system disk and 'w' for work area | +------------------------------------------------------------------*/ checkfm: procedure expose globals. parse arg type,fm 'SET CMSTYPE HT' if fm = '' then exitrc(36, 'You must specify a filemode') 'MAKEBUF' 'Q DISK' fm '(STACK LIFO' if rc = 0 then do pull . . qfm acc . . blksz . if qfm = 'NOT' then call seterror 36, 'Filemode' fm 'is not accessed' else if acc ^= 'R/W' then call seterror 36, 'Filemode' fm 'is not accessed R/W' else if globals.opts.backup = 1 & type = 'w' & qfm = globals.sasfm then call seterror 36, 'Since BACKUP is specified, the work area must '||, 'be a different disk than the SAS system disk' else if type = 's' then do /* a quick sanity check to insure it really is the SAS disk */ status = state(globals.product 'LOADLIB' fm) parse var status chk . if chk = 'NOT' then call seterror 36, 'Filemode' fm 'is not the SAS System disk' globals.sas.space = blksz /* gets more info later in spacea() */ end else if type = 'w' then globals.work.space = blksz /* gets more info later in spacea() */ end else call seterror 36, 'Filemode' fm 'is invalid' 'DROPBUF' if globals.error ^= 0 then exitrc() 'SET CMSTYPE RT' return qfm /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: checkopts(options) | | PURPOSE: parses option list | | NOTES: currently only one: BACKUP|NOBACKUP | +------------------------------------------------------------------*/ checkopts: procedure expose globals. parse arg list globals.opts.backup = 0 /* default */ args = words(list) optindex = 1 do while optindex <= args uopt = word(list,optindex) opt = bitor(uopt,' ',' ') /* uppercase */ if abbrev('BACKUP',opt,1) then do globals.opts.backup = 1 globals.backup.0 = 0 /* the number of backup files */ end else if abbrev('NOBACKUP',opt,3) then globals.opts.backup = 0 else exitrc(24, 'Unknown option' uopt) optindex = optindex + 1 end return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: getprod(filename) | | PURPOSE: retrieves SAS product name from the hotfix file name | | NOTES: filename is already in format 'fn ft fm' | +------------------------------------------------------------------*/ getprod: procedure expose globals. parse arg fn . pr = substr(fn,3,2) select when pr = 'AF' then product = 'SASAF' when pr = 'BA' then product = 'SASBASE' when pr = 'BB' then product = 'SASBASE' when pr = 'BC' then product = 'SASBASE' when pr = 'BX' then product = 'SASBASE' when pr = 'CT' then product = 'SASCONNE' when pr = 'ET' then product = 'SASETS' when pr = 'FS' then product = 'SASFSP' when pr = 'GI' then product = 'SASGIS' when pr = 'GR' then product = 'SASGRAPH' when pr = 'LB' then product = 'SASLAB' when pr = 'ML' then product = 'SASIML' when pr = 'MX' then product = 'SASMDDB' when pr = 'OR' then product = 'SASOR' when pr = 'QC' then product = 'SASQC' when pr = 'SH' then product = 'SASSHARE' when pr = 'SQ' then product = 'SASACCSQ' when pr = 'ST' then product = 'SASSTAT' when pr = 'SX' then product = 'SASSTAT' when pr = 'WB' then product = 'SASINTRN' otherwise exitrc(12, 'Unsupported product key' pr 'in input file') end return product /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: setwarn(string) | | PURPOSE: called for any warnings encountered | | NOTES: | +------------------------------------------------------------------*/ setwarn: procedure expose globals. parse arg string globals.warn = 4 say 'WARNING:' string return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: seterror(rc, string) | | PURPOSE: set error before exiting exec | | NOTES: | +------------------------------------------------------------------*/ seterror: procedure expose globals. parse arg ret,string globals.error = ret globals.estring = string return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: exitrc(rc, string) | | PURPOSE: exit exec | | NOTES: if called with no parms, then it is assumed seterror | | has been called prior or we are exiting cleanly | +------------------------------------------------------------------*/ exitrc: procedure expose globals. parse arg ret,string 'SET CMSTYPE RT' if ret = '' then do ret = globals.error string = globals.estring end if ret ^= 0 then say string if ret = 24 then do say; say 'Usage:'; say 'HOTFIX fn > <( >' end else if ret = 0 & globals.warn = 4 then ret = 4 if ret ^= 0 & globals.opts.backup = 1 then do parse var globals.infile fn . if globals.sas.updt = 1 then do say; say 'IMPORTANT: IF THE HOTFIX EXEC IS TO BE RERUN ON' fn say 'WITH THE BACKUP OPTION SPECIFIED, THEN THE SAS DISK' say 'MUST FIRST BE RESTORED TO OBTAIN PROPER BACKUP FILES' end else do say; say 'NO REPLACEMENT OF FILES TOOK PLACE ON THE SAS DISK' say 'The HOTFIX exec can be rerun on' fn 'without a restore of the' say 'SAS system disk regardless of the setting of the BACKUP option' end end 'SET CMSTYPE' globals.cmstype exit ret