/*------------------------------------------------------------------+ | 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.workfm = fm of the work disk | | 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 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 | | | | 5) HOTFIX TRACE file contains the output of all | | the LOADLIB copies executed during the exec | | | | 6) DBC - note - Need to incorporate code to handle | | a hotfix that includes bundles (SABBASE). such | | a hotfix would include both SASBASE and SABBASE | | loadlibs | | | | 7) DBC - note - Need to add checks for disk space | | before proceeding with any replaces | | | | 8) DBC - note - Add global variable that marks the | | 'point of no return' after the first loadlib is | | updated and BACKUP is specified. After this point | | if the exec is to be rerun, the system disk will | | have to be restored to pick up proper backup copies | | | | 9) DBC - note - Future hot fixes may require the | | addition of code to process MODULE or MESSAGE files | | | | HISTORY: | | date who action | | _______ ___ _________________________________________ | | 04apr01 DBC Initial coding | | 13jun01 DBC Added datetime stamp in audit file | | 18jun01 DBC Added catalog and bundle processing | | | +------------------------------------------------------------------*/ 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.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 */ 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() /*-------------------------------------------------------------------+ | process each file | +-------------------------------------------------------------------*/ 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' then call pcatalog 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: uppercase(string) | | PURPOSE: uppercases string | | NOTES: | +------------------------------------------------------------------*/ uppercase: procedure parse arg string string = bitor(string,copies(' ',length(string))) return string /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: postvmarc() | | PURPOSE: post process of VMARC unpack information in the stack | | NOTES: | +------------------------------------------------------------------*/ postvmarc: procedure expose globals. globals.max = 0 globals.file.0 = queued() do i = globals.file.0 to 1 by -1 pull fn '.' . . bytesin ',' . . bytesout . globals.file.i = fn bytesin bytesout if bytesout > globals.max then globals.max = bytesout end return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | SUBROUTINE: makebkup() | | PURPOSE: creates the VMARC backup file | | NOTES: | +------------------------------------------------------------------*/ makebkup: procedure expose globals. parse var globals.infile fn . 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: pcatalog(fn ft fm bytesin bytesout) | | PURPOSE: process CATALOGs from hotfix VMARC file | | NOTES: | +------------------------------------------------------------------*/ pcatalog: procedure expose globals. parse arg fn ft fm . /* fm = globals.workfm */ samedisk = 0 /* ft = SASHELP */ 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 call setwarn 'RC' rc 'from COPYFILE moving '||, 'catalog' fn ft 'to the SAS system disk' else 'ERASE' fn ft fm 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 if substr(fn,3,1) = 'B' then do bundle = 1 globals.product = 'SABBASE' /* TEMPORARY change to process */ end /* the bundle */ if globals.opts.backup then do 'ERASE HFBACKUP' ft fm globals.backup.0 = globals.backup.0 + 1 backupnum = globals.backup.0 queue 'SELECT' queue fn queue '' 'LOADLIB COPY' globals.product ft globals.sasfm ' '||, 'HFBACKUP' ft fm '(MODIFY DISK' if rc ^= 0 then exitrc(28, 'RC' rc '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 queue 'SELECT' queue fn||'(R)' queue '' 'LOADLIB COPY' fn ft fm globals.product ft globals.sasfm '(REPLACE DISK' if rc ^= 0 then exitrc(28, 'RC' rc 'from LOADLIB COPY moving '||, 'hotfix executable' fn 'to' globals.product ft globals.sasfm) 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' 'ERASE HFBACKUP' ft fm end else 'ERASE' fn ft fm if bundle = 1 then globals.product = 'SASBASE' /* reset TEMP change before exit */ return /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | 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 | +------------------------------------------------------------------*/ 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 uppercase(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 uppercase(fn) 'VMARC' uppercase(fm) /* /////////////////////////////////////////////////////////////// */ /*------------------------------------------------------------------+ | 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 . 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' end 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 = uppercase(uopt) 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 = '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 = 'ST' 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 'SET CMSTYPE' globals.cmstype exit ret