Something I mentioned on twitter a little while ago, I had a problem where someone had changed a source member but not recompiled it. As it happens they took out a line of code that the customer was quite keen on, so when 10+ years later the next poor sap came to amend the program and recompile it you can imagine they got both barrels.
Now there are a number of packages out there to help you catch this, he says after watching a webinar this week for just such a package, but I wondered how easy it would be to knock something together myself. I will quickly add this is one of my customers who has a long love affair with V5R4 so unfortunately I cannot avail myself of SQL services, if you’re not familiar then I suggest googling QSYS2.PROGRAM_INFO as an example of a time-saver.
I’ll start off with a quick and dirty solution, in my case the customer has one program library and one source library that I’m interested in, they also don’t use many bound modules/service programs. So I could cut a few corners via:
DSPOBJD OBJ(PGMLIB/*ALL) OBJTYPE(*PGM) DETAIL(*BASIC) OUTPUT(*OUTFILE) OUTFILE(QTEMP/DSPOBJDSRV)
DSPFD FILE(SRCLIB/*ALL) TYPE(*MBRLIST) OUTPUT(*OUTFILE) FILEATR(*PF) OUTFILE(QTEMP/DSPFD)
select a.odobnm, a.odcdat, b.mlchgd
from qtemp.dspobjdsrv as a
join qtemp.dspfd as b
on a.odobnm = b.mlname
and a.ODOBAT = b.mlseu
I now have a list of each program, creation date, and the source member last change date. I need to do a bit of additional work since the two dates are in different formats (MDY for my dspobjd, YMD for my dspfd) but I can then see if source change date > program creation date.
Now this is all very well and good if you have a one-to-one relationship between your source and programs, but what if your source is in one lib and the program could be in one of three libraries? Or vice versa, the above won’t help as much. You could do a *ALL/*ALL and then add some more conditions to your join to try and match everything up.
But what if you use bound modules / service programs? Creation date may not be what you are interested in since you UPDPGM rather than CRTPGM.
My solution to this is to use the API QBNLPGMI to return information on all the modules in my program. For those not familiar this gives you information on each module within the program, including (importantly for my needs), source file/library/member along with module creation date/time and source file updated date/time.
Below is an example of a very rough loop to go through every module in my program, I’ve used the IBM supplied Create/Change/Retrieve user space commands as opposed to QUSCRTUS / QUSRTVUS just because I find it easier to read.
CHGVAR VAR(&PGMNAME) VALUE(&P#PGM *CAT &P#LIB)
CHGVAR VAR(&USRSPACE) VALUE(‘DXSTEST ‘ *CAT ‘MNISLIB ‘)
CALL PGM(QBNLPGMI) PARM(&USRSPACE ‘PGML0100′ &PGMNAME X’00000000’)
CHGVAR VAR(&STARTPOS) VALUE(1)
CHGVAR VAR(&DATALEN) VALUE(140)
RTVUSRSPC USRSPC(MNISLIB/DXSTEST) STRPOS(&STARTPOS) LENGTH(&DATALEN) RCVDTA(&HEADER)
CHGVAR VAR(&LST_OFFSET) VALUE(%BIN(&HEADER 125 4))
CHGVAR VAR(&LST_NBR) VALUE(%BIN(&HEADER 133 4))
CHGVAR VAR(&LST_LEN) VALUE(%BIN(&HEADER 137 4))
CHGVAR VAR(&LST_POS) VALUE(&LST_OFFSET + 1)
CHGVAR VAR(&LST_COUNT) VALUE(0)
LST_LOOP: IF COND(&LST_COUNT *EQ &LST_NBR) THEN(GOTO CMDLBL(LST_END))
RTVUSRSPC USRSPC(MNISLIB/DXSTEST) STRPOS(&LST_POS) LENGTH(&LST_LEN) RCVDTA(&RECEIVER)
CHGVAR VAR(&LST_COUNT) VALUE(&LST_COUNT + 1)
CHGVAR VAR(&LST_POS) VALUE(&LST_POS + &LST_LEN)
So, the API call will dump all the information into my pre-created user-space (CRTUSRSPC command not shown here.) Read the header (first RTVUSRSPC command) then set variables to allow us to read each entry in turn. Swap out QBNLPGMI for QBNLSPGM and you can do the same for service programs.
I am passing in a program name & library, so you could use the DSPOBJD from earlier as a driver to loop through each program you found. You could then write the information out to a file or compare on the go if you have the DSPFD from earlier to hand.
As you can probably tell I’m still fine tuning how this is going to run before I let it loose on the customers, perhaps I’ll come back later and discuss how many instances I found of problems and what I care to do with them. If you have any suggestions why not add them below, you might have a much better idea than I which I can steal 😉