What Is a Signature?
First of all, let's take a look at what service program signatures are, how they are used, and why it is necessary to manage them. Every service program has at least one signature. You can see the current signature value for a service program by using the Display Service Program (DSPSRVPGM) command. The current signature appears on the first screen of the command output. If you continue to press Enter on each screen displayed by the command, screen 9 (of 10) shows all the valid signatures, including any valid previous signatures that have been included by using binder language (more on this later).
The signature serves the same function for a service program that a level identifier (i.e., the source of level-check errors) does for a file. It provides a means for the system to tell at run time if a significant change has been made to the object that another object uses.
With file-level identifiers, the "significant change" that would cause an error is a change in the layout of the record format that a program uses. With service program signatures, the "significant change" is a change to the list of exports (i.e., externally callable procedures and/or exported data items) in the service program. The error that occurs when the file identifier changes is a level check. We have all seen them, and we all know how to fix them. The error that occurs when a service program's signature changes is a signature violation. If you have ever seen a signature violation message, you must know at least one method of fixing them—either that, or you stopped using service programs! Read on if you have ever wondered if there might be a better way to deal with them.
A common misconception about signature violations that we should get out of the way right up front is the notion that a service program's signature is impacted by a change to the parameters in one or more procedures in the service program. This is not true. You may change the parameter list to your procedures in any way you like and it will not affect the service program's signature. The signature changes only when the list of exports (typically, a list of procedures) in the service program changes or when the programmer changes it intentionally by using binder language.
For those who, like me, really like to understand why these things happen, let's look a little deeper into why signature checking is necessary. This is best explained by using an example.
Let's assume a service program, CUSTPROCS, contains procedures ValidCust, GetCustInfo, and SearchByPhone. The CUSTPROCS service program is referenced by program OEPGM01 as well as by several other programs. (Remember, the ability for a procedure to be used by many different programs is, after all, the primary purpose of putting procedures into a service program.) Refer to the picture below as we describe how the program OEPGM01 interacts with service program CUSTPROCS..
Figure 1: This image depicts the interaction between program and service program. (Click image to enlarge.)
The information about how to call each procedure in the service program is stored with the program object. At run time, this information is used to quickly locate and execute the appropriate procedure. Note that the location of each procedure is kept in a sequential list (much like an array of procedure pointers) called an export list, and the calling program only keeps the relative position in the export list (like an array index value) for each procedure to be called. There is no need for a potentially lengthy search through a list of procedure names to find the location of a given procedure; the operating system can go directly to a specific position in the list of exports to get the pointer to the procedure. The procedure may well change its location inside the service program due to maintenance of other service program procedures, but the export list will also keep track of the current location of each procedure. You may also notice that, as in this example, the physical sequence of the procedures in the service program does not necessarily need to match the sequence of the exports in the export list. In our example, ValidCust is the first procedure coded in the service program, but its address is in the third position in the export list.
Given that this is the way that programs connect with procedures in service programs, it should be apparent now that the sequence of the addresses in the export list is critical to making this work. If, for example, the location of ValidCust were not in position 3—perhaps because someone added a new procedure and placed its location in position 3 instead—then the OEPGM01 program would execute the wrong procedure unless some defense mechanism prevented it. That's why the service program's current signature value is copied along with the export list position to the program object when it is bound to the service program.
The purpose of the service program signature is to detect if the sequence of addresses in the export list has changed. If that sequence changes, then the system prevents a program such as OEPGM01 from running, therefore preventing something bad (and very hard to debug) from happening if a different procedure were to run when ValidCust was supposed to run.
The default way for service program signature values to be assigned is to let the system generate the signature(s). The system uses an algorithm for this generation based on the names of the procedures in sequence in the export list. So if a service program is re-created many times (because we continually make changes to the existing procedures) and the list of exported procedures remains the same, the signature will remain the same each time. No signature violation will occur. However, if the list of exported procedures changes (e.g., because we add a new procedure to the service program), the generated signature value will change.
At run time, the system needs only to compare the values of the stored (expected) value of the service program signature in the program object to the actual signature value (or list of signature values) in the service program. If no matching signature is found, the program will not run and fails with a signature violation. This is much like the program that fails with a file-level check because the layout of the record format has changed.
Avoiding Signature Violations
There are a few basic ways to avoid signature violations. Some of these may seem silly, but, trust me, lot of shops out there are doing what many of us might consider silly things. So I'll include all the options that I've seen, no matter how unusual. Even so, I suspect I'll learn of a few more options in the online comment forum for this article.
Avoid all possibility of a signature change in the service program. The way I've seen some shops do this is to have only one export in every service program. That is, there is only one callable procedure in each service program; therefore, there is only one position in the export list. The signature will never change unless the procedure name changes. This approach seems to work for some shops. Personally, I prefer to have the flexibility to group similar and/or dependent procedures together, either in one module or multiple modules. There is also a potential performance impact if the number of service programs becomes very large.
Avoid having the system check the signature value at run time by using procedure pointers instead of calling by procedure name. There are some excellent reasons to use procedure pointers under some circumstances. But to me, using them for the purpose of avoiding signature violations is just too much trouble.
Let the system generate only one signature value and re-generate it each time you add new procedures to the service program. The signature value will change if you add (or remove) procedures from the service program, so in that case, you must re-bind all the programs that use the service program. Re-binding causes the new current signature value of the service program to be stored in the program so that there will be a match at program run time. At the same time that the new signature value is stored, there is also an update, if necessary, to the positions in the export list of any procedures in the changed service program. This is a very common way to deal with signature changes. For many shops, this may well be the best option, especially where there is only one system in the shop (i.e., the changed service programs don't need to be distributed to many different systems). This is also a good choice for shops using automated change management systems because the system does the extra work reliably.
One of my personal pet peeves in this case is the insistence that doing this requires re-compiling every program that uses the service program. While re-compiling will do a re-bind as well, thereby solving the changed signature problem, it is far beyond what needs to be done. It not only takes more time and system resources than a simple re-bind, but it is potentially more error-prone for shops that don't have good automated change management systems in place. The wrong version of the source might be re-compiled, and/or other options (e.g., adopted authority, activation group, compile time options) may not be specified exactly the same as in the original program.
What does it take to do a faster, simpler re-bind? The Update Program (UPDPGM) command specifying the Module(*None) parameter value. You must specify *None because the command would otherwise attempt to replace one or more modules in the program, which is not needed to update the signature value the program is looking for. Since service programs may also reference one another, it may be necessary to use the UPDSRVPGM command as well. It is relatively simple to write your own program using system APIs to do this process for all impacted programs and/or service programs.
There is a caveat to using UPDPGM or UPDSRVPGM if new referenced objects (e.g., files that weren't used in the program before) are included with the changes. New referenced objects won't be picked up with the update commands. In those cases, if you still have the module object(s) necessary to create the program, you can use CRTPGM. Otherwise, you may need to re-compile in those circumstances.
The developer manages the service program's signature using binder language. A few different approaches can be used with binder language, including the following scenarios: The developer lets the system generate multiple signatures based on multiple export lists supplied by the developer in the binder language. Or the developer supplies multiple export lists and also supplies multiple hard-coded values for the signatures rather than letting the system generate the signature values. Or the developer provides one hard-coded signature value that stays the same over time and one export list that changes with each new version of the service program.
The binder language solution is another very popular approach to managing service program signatures. I don't have space to cover binder language details in this article, but because it is important to understand those details if you want to use this solution, we'll take a look at binder language syntax and reveal how to implement each of these different scenarios in a future issue of RPG Developer.
Avoid Signature Violation Errors
I hope this look inside service programs and their signatures has helped you understand the purpose of signatures and perhaps has given you some ideas on ways to manage your service program signature values to avoid signature violation errors. In the next part of this look at signatures, we'll concentrate on binder language and the various ways it can be used.