On 17/02/2008, at 2:17 PM, M. Lazarus wrote:
A software vendor installs a *SRVPGM to be utilized by clients'
software.
Scenario:
Signature is hardcoded.
Version 1 has two exports.
Version 2 with 3 exports gets shipped and installed.
If the manual signature has not changed, the new version will not
throw an error if the calling programs have not been rebound. This
may be a desired behavior, if the new version handles it
properly.
It is reasonable to expect that developers who are changing binder
source have some idea of what they are doing. If you employ idiots
then you get what you deserve. As others have pointed out a decent
comment block explaining what to do may help. An example from my own
source:
/* Restrictions . . . . . : Ensure you understand ILE signatures
before */
/* modifying this source. All new exports
MUST */
/* go at the end of the export
block. */
/
*
*/
Also, a proper test harness would include test programs specifically
compiled to use previous signatures.
But the oversight may allow undesirable results if an
export was dropped.
This made me curious--always a dangerous thing. I discovered that you
can remove a public export and either use a manual signature or use a
generated signature with or without *PRV signature support and the
removed export is still callable by programs that use the service
program.
Right now I'll bet many of you are saying "What the f...?"
I performed various tests but I'll only document the most obvious one
here. The service program ran in ACTGRP(*CALLER) and the calling
program ran in ACTGRP(*NEW) and used *LIBL to locate the service
program.
Module TEST_BIND1 contains 3 procedures tagged with EXPORT:
proc1 returns 1
proc2 returns 2
proc3 returns 3
Procedures are coded in that physical order in the source.
Binder source TEST_BINDZ exports all three procedures with fixed
signature:
STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('MY_SIG')
EXPORT SYMBOL(PROC1)
EXPORT SYMBOL(PROC2)
EXPORT SYMBOL(PROC3)
ENDPGMEXP
CRTSRVPGM SRVPGM(TEST_BINDZ) MODULE(TEST_BIND1) SRCFILE(TEMP)
Service program . . . . . . . . . . . . : TEST_BINDZ
Library . . . . . . . . . . . . . . . : TEMP
Procedure Exports:
Procedure
Name ARGOP
PROC1
*NO
PROC2
*NO
PROC3
*NO
Signatures:
MY_SIG
Trivial program TEST_BINDY:
D proc1 PR 5I 0
D parm1 10 CONST OPTIONS(*NOPASS)
D proc2 PR 5I 0
D parm1 10 CONST OPTIONS(*NOPASS)
D result S 5I 0
C EVAL result = proc1
C result DSPLY
C EVAL result = proc2
C result DSPLY
C SETON LR
C RETURN
crtpgm test_bindy bndsrvpgm(test_bindz)
CALL TEST_BINDY gives:
2 > call test_bindy
DSPLY 1
DSPLY 2
Now, remove export for proc2 from binder source TEST_BINDZ
STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('MY_SIG')
EXPORT SYMBOL(PROC1)
/* EXPORT SYMBOL(PROC2) */
EXPORT SYMBOL(PROC3)
ENDPGMEXP
CRTSRVPGM SRVPGM(TEST_BINDZ) MODULE(TEST_BIND1) SRCFILE(TEMP)
Service program . . . . . . . . . . . . : TEST_BINDZ
Library . . . . . . . . . . . . . . . : TEMP
Procedure Exports:
Procedure
Name ARGOPT
PROC1
*NO
PROC3
*NO
Signatures:
MY_SIG
Do NOT recreate program TEST_BINDY, just invoke it.
2 > call test_bindy
DSPLY 1
DSPLY 2
Same result--even though proc2 is no longer part of the public
interface in the service program. I wondered if physically shifting
the procedure code around in the module source would have an effect
because this is a normal side-effect of code maintenance. I did not
think it would cause a problem but I moved the code for proc3 so it
was now between the code for proc1 and proc2 (i.e., the physical code
order changed from:
proc1
proc2
proc3
to
proc1
proc3
proc2
I recompiled the module, recreated the service program still
exporting only proc1 and proc3 and then WITHOUT rebuilding TEST_BINDY:
2 > call test_bindy
DSPLY 1
DSPLY 2
Same result. The trick, of course, is that the procedure code remains
in the service program (and possibly also requires EXPORT specified
but I did not test that).
Attempting to recreate the TEST_BINDY program gives:
> crtpgm test_bindy bndsrvpgm(test_bindz)
Definition not found for symbol 'PROC2'.
Program TEST_BINDY not created.
which is the expected behaviour when referencing a procedure not
listed in the public interface.
I cannot say that I would have expected this behaviour before the
experiment, nor can I recall such behaviour being documented but it
does make a lot of sense. It provides a way to remove a deprecated
procedure from the public interface without breaking existing code. I
can imagine such a scenario (indeed Java does it frequently). Imagine
a procedure that for some reason is no longer required by current
code or is replaced by another procedure (with a better name or
easier interface). This technique allows the creator to remove the
deprecated procedure from the public interface while still allowing
old code to run. The creator can modify the deprecated procedure to
output a message such as "Procedure ABC is deprecated. Change your
code to use procedure XYZ and recompile". At some future point the
actual code could be removed from the service program module.
Not quite as flexible as using *CURRENT and *PRV signatures to
replace a deprecated function (see previous appends from me showing
how this can be accomplished) with another of the same name but does
have the advantage of making the deprecated procedure impossible to
invoke in recompiled code. That has a robustness advantage.
All in all, a useful addition to the toolbox.
Regards,
Simon Coulter.
--------------------------------------------------------------------
FlyByNight Software OS/400, i5/OS Technical Specialists
http://www.flybynight.com.au/
Phone: +61 2 6657 8251 Mobile: +61 0411 091 400 /"\
Fax: +61 2 6657 8251 \ /
X
ASCII Ribbon campaign against HTML E-Mail / \
--------------------------------------------------------------------
As an Amazon Associate we earn from qualifying purchases.