Let’s ‘C’ a little more (about *SRVPGM management)

The program we created last time shows how to read through a message file looking for a particular text string, but we generally do not search and change system messages (we only have the snd_error_msg() function which uses the system delivered messages) so perhaps now would be a good time to look at how we can provide a function to send messages we have defined.

To add this functionality we are going to need to change one of the service programs, this adds a new wrinkle because as we explained before, when a program is bound with a service program the system assigns a signature to that link which identifies the entities that are exported from the service program. Change the exports and the signature created for the service program will change. 

In our current situation it would not be a major task to go out and re-bind the service program with the latest signature as we would just have to recompile the one program we have created so far and all will be good. However in a normal application that would be an unlikely situation, there could be hundreds of programs and not all of them will rely on the service program you are changing. (Spoiler!!!  Later we will develop a program to help identify what needs re-compiling, but for now…) You would still want to leave any current programs untouched ( remember they are not going to use the new functionality in the Service Program until you add it to them ) but still have a way for new programs that use the new functionality to run against the same *SRVPGM object. Thankfully IBM i has a way to that!

When we built the service program we used a file to store the current exports for the service program
CRTSRCPF FILE(OSLIB/QSRVSRC) RCDLEN(150) TEXT(‘Service Program Exports’)
and after we compiled the module we told the system to log the exports to that file 
RTVBNDSRC MODULE(OSLIB/MSGFUNC) SRCFILE(OSLIB/QSRVSRC).
At the moment this file has the following content.
STRPGMEXP PGMLVL(*CURRENT)
/********************************************************************/
/*   *MODULE      MSGFUNC      OSLIB        03/13/18  12:02:21      */
/********************************************************************/
  EXPORT SYMBOL("snd_error_msg")
ENDPGMEXP  
As you can see we only have the one export which is the function which is used to send error messages. In this post we are going to add a new function to the service program that will allow a message to be sent from our programs which links to our own message file, we will call that function snd_msg().  Let’s create the message file first and add a simple message description to that file, we will make it very simple as it about the process not the content. We will also create the message queue, it should have been created previously to receive error messages but oops!
CRTMSGF MSGF(OSPGM/OSMSGF) TEXT(‘OS message file’)
ADDMSGD MSGID(MSG0001) MSGF(OSPGM/OSMSGF)  MSG(‘We have just called API &1’)
SECLVL(‘Just a simple message stating we have called an API.’) FMT((*CHAR 10))
CRTMSGQ MSGQ(OSPGM/OSMSGQ) TEXT(‘OS Message Queue’)
The message will be used to signal a call to an API, not very interesting I know….

We could add the message to the program that we created previously but we want to show how we can have a service program which has been updated still function with a program that was created using a different signature. So lets develop a new program which will do just that. Here another very simple program plus command that we can use to list all of the objects in a library of a specific type, it will use both of the service programs we have developed so far to list the objects and send out the new message.
Here is the command we will use:
/*===================================================================*/
/*  COMMAND name :  LSTOBJBYTP                                       */
/*  Author name..:  Chris Hird                                       */
/*  Date created :  March 2018                                       */
/*                                                                   */
/*                                                                   */
/*  Purpose......:  List objects of certain type in library          */
/*  CPP..........:  LSTOBJBYTP                                       */
/*  Revision log.:                                                   */
/*  Date     Author    Revision                                      */
/*                                                                   */
/*  @Copyright Chris Hird 2018                                       */
/*===================================================================*/
             CMD        PROMPT('List Objects of Type')
             PARM       KWD(LIB) TYPE(*CHAR) LEN(10) MIN(1) PROMPT('Library Name')
             PARM       KWD(TYPE) TYPE(*CHAR) LEN(10) MIN(1) PROMPT('Object Type')                                                                                                        
Here is the CPP for that command :
//
// Copyright (c) 2018 Chris Hird
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// Disclaimer :
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

#include <H/MSGFUNC>                        // Message headers
#include <H/SPCFUNC>                        // User Space functions
#include <H/COMMON>                         // common header file
#include <quslobj.h>                        // list Objects
#include <qusdltus.h>                       // delete user space


int main(int argc, char **argv) {
int i = 0;                                  // counter
char Obj_SPC[20] = "QUSLOBJ   QTEMP     ";  // user space
char Lib_Info[20] = "*ALL      ";           // library info
char *List_Section;
Qus_OBJL0100_t *Entry_List;                 // USRSPC Pointer
Qus_Generic_Header_0100_t *space;           // USRSPC Pointer
Os_EC_t Error_Code = {0};                   // Error Code Struct

Error_Code.EC.Bytes_Provided = _ERR_REC;
// copy the library name into place
memcpy(&Lib_Info[10],argv[2],10);
// get a pointer to a user space
if(Get_Spc_Ptr(Obj_SPC,&space,_4MB) != 1) {
   exit(-1);
   }
// send out message about the API we are going to call
snd_msg("MSG0001","QUSLOBJ   ",10);
// call the API
QUSLOBJ(Obj_SPC,
        "OBJL0100",
        Lib_Info,
        argv[2],
        &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   exit(-1);
   }
// set up the pointers to the information returned.
List_Section = (char *)space;
List_Section = List_Section + space->Offset_List_Data;
Entry_List = (Qus_OBJL0100_t *)List_Section;
// Check that some objects were returned if yes create audit date   
if(space->Number_List_Entries < 1) {
   printf("Nothing returned\n");
   exit(0);
   }
for(i = 0; i < space->Number_List_Entries; i++) {
   printf("Object %.10s type %.10s\n",Entry_List->Object_Name_Used,Entry_List->Object_Type_Used);
   }
snd_msg("MSG0001","QUSDLTUS  ",10);
QUSDLTUS(Obj_SPC,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   exit(-1);
   }
exit(0);
}  
At this point we can compile the module and even though there will be a warning issued because the snd_msg() function is not described anywhere the module will be created. However trying to compile as a program will fail because the function is not found in any of the Service programs (bound via the binding directory entries) we have so far. So let’s fix up the service program now and add the snd_msg() function to the relevant header.

Add this code to the bottom of the QCSRC/MSGFUNC member. Note:  Always add updates to the bottom of the file, this is discussed in the IBM documentation with relation to service programs.
// function snd_msg()
// Purpose: Place a message in the message queue.
// @parms
//      string MsgID
//      string Msg_Data
//      int Msg_Dta_Len
// returns void

void snd_msg(char * MsgID,
             char * Msg_Data,
             int Msg_Dta_Len) {
char Msg_Type[10] = "*INFO     ";           // msg type  
char Call_Stack[10] = {"*EXT      "};       // call stack entry  
char QRpy_Q[20] = {' '};                    // reply queue  
char Msg_Key[4] = {' '};                    // msg key  
Os_EC_t Error_Code = {0};                   // error code struct  

Error_Code.EC.Bytes_Provided = _ERR_REC;
// send the message to the message queue
QMHSNDM(MsgID,
        _DFT_MSGF,
        Msg_Data,
        Msg_Dta_Len,
        Msg_Type,
        _DFT_MSGQ,
        1,
        QRpy_Q,
        Msg_Key,
        &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   }
// add a diag message to the program message queue   
QMHSNDPM(MsgID,
         _DFT_MSGF,
         Msg_Data,
         Msg_Dta_Len,
         "*DIAG     ",
         "*         ",
         0,
         Msg_Key,
         &Error_Code);
if(Error_Code.EC.Bytes_Available > 0) {
   snd_error_msg(Error_Code);
   }
return;
}    
Replace the H/MSGFUNC header file content with the following. We only add a few lines but it’s easier to show the complete listing.
#ifndef MSGFUNC_h
   #define MSGFUNC_h
   #include <H/COMMON>                      // common header
   #include <qmhsndm.h>                     // snd msg
   #include <qmhsndpm.h>                    // snd program msg

   //the default message queue
   #define _DFT_MSGQ "OSMSGQ    *LIBL     "
   #define _DFT_MSGF "OSMSGF    OSPGM     "

   // function declarations
   void snd_error_msg(Os_EC_t);
   void snd_msg(char *,char *,int);
   #endif    
you can see we have added a new include to allow the program message to be sent, added the message file definition plus the function call definition for snd_msg().

Before we can build the new service program object in OSPGM library we will need to remove it. Then compile the module before retrieving the binder source and add it to the QSRVSRC member for the MSGFUNC.
DLTOBJ OBJ(OSPGM/MSGFUNC) OBJTYPE(*SRVPGM)
CRTCMOD MODULE(OSLIB/MSGFUNC) SRCFILE(OSLIB/QCSRC) SRCMBR(MSGFUNC) DBGVIEW(*SOURCE) REPLACE(*YES) TGTRLS(V7R1M0)
RTVBNDSRC MODULE(OSLIB/MSGFUNC) SRCFILE(OSLIB/QSRVSRC) MBROPT(*ADD)
The content of the member in QSRVSRC will now look different.
STRPGMEXP PGMLVL(*CURRENT)                                                                                                                
/********************************************************************/                                                                    
/*   *MODULE      MSGFUNC      OSLIB        03/13/18  12:02:21      */                                                                    
/********************************************************************/                                                                    
  EXPORT SYMBOL("snd_error_msg")                                                                                                          
ENDPGMEXP                                                                                                                                 
STRPGMEXP PGMLVL(*CURRENT)                                                                                                                
/********************************************************************/                                                                    
/*   *MODULE      MSGFUNC      OSLIB        03/26/18  14:38:31      */                                                                    
/********************************************************************/                                                                    
  EXPORT SYMBOL("snd_msg")                                                                                                                
  EXPORT SYMBOL("snd_error_msg")                                                                                                          
ENDPGMEXP          
This needs to be updated to show that we need 2 keys in the service program, I would have thought IBM could do a better job with the RTVBNDSRC request to automatically update the information, but as it is we have to do it manually. So we just need to update the PGMLVL for the old service program to have *PREVIOUS instead of *CURRENT so it now looks like the following.
STRPGMEXP PGMLVL(*PREVIOUS)                                                                                                                
/********************************************************************/                                                                    
/*   *MODULE      MSGFUNC      OSLIB        03/13/18  12:02:21      */                                                                    
/********************************************************************/                                                                    
  EXPORT SYMBOL("snd_error_msg")                                                                                                          
ENDPGMEXP                                                                                                                                 
STRPGMEXP PGMLVL(*CURRENT)                                                                                                                
/********************************************************************/                                                                    
/*   *MODULE      MSGFUNC      OSLIB        03/26/18  14:38:31      */                                                                    
/********************************************************************/                                                                    
  EXPORT SYMBOL("snd_msg")                                                                                                                
  EXPORT SYMBOL("snd_error_msg")                                                                                                          
ENDPGMEXP          
You should now have a new Service Program in the library, the important connection will be the signatures in the service program and the ones in the programs which call its functions. If the signatures do not match (either one) then the signature violation message will be sent. here are the signatures in the new Service Program which can be found by issuing the following command
DSPSRVPGM SRVPGM(OSPGM/MSGFUNC) DETAIL(*SIGNATURE)
                     Display Service Program Information                       
                                                                Display 1 of 1 
Service program  . . . . . . . . . . . . :   MSGFUNC                           
  Library  . . . . . . . . . . . . . . . :     OSPGM                           
Owner  . . . . . . . . . . . . . . . . . :   CHRISH                            
Service program attribute  . . . . . . . :   CLE                               
Detail . . . . . . . . . . . . . . . . . :   *SIGNATURE                        
                                                                               
                                 Signatures:                                   
                                                                               
00000087A2946D999E13C2CB45CDEFC2                                               
00000087A2946D99969999856D8495A2                                               
So now we can build our C Program and the command to front it.
CRTCMOD MODULE(OSLIB/LSTOBJBYTP) SRCFILE(OSLIB/QCSRC) SRCMBR(LSTOBJBYTP) DBGVIEW(*SOURCE) REPLACE(*YES) TGTRLS(V7R1M0)
CRTPGM PGM(OSPGM/LSTOBJBYTP) MODULE(OSLIB/LSTOBJBYTP) BNDDIR(OSLIB/OS) TGTRLS(V7R1M0)
CRTCMD CMD(OSPGM/LSTOBJBYTP) PGM(*LIBL/LSTOBJBYTP) SRCFILE(OSLIB/QCMDSRC) SRCMBR(LSTOBJBYTP) REPLACE(*YES)
At this point you should be able to run the new command and list the object in a library of your choice. you should also see a message sent to the message queue in OSPGM library. But the important thing we need to show is the signatures and how they match up with the service program which as we now know has 2 signatures.

This is the signature list for the new program that we have just created.
                         Display Program Information                          
                                                                Display 4 of 7
Program  . . . . . . . :   LSTOBJBYTP    Library  . . . . . . . :   OSPGM     
Owner  . . . . . . . . :   CHRISH                                             
Program attribute  . . :   CLE                                                
Detail . . . . . . . . :   *SRVPGM                                            
                                                                              
                                                                              
Type options, press Enter.                                                    
  5=Display                                                                   
                                                                              
     Service                                                                  
Opt  Program     Library     Activation  Signature                            
     MSGFUNC     *LIBL       *DEFER      00000087A2946D999E13C2CB45CDEFC2     
     SPCFUNC     *LIBL       *DEFER      00000000091DD45845D2200A47DBF533     
     QC2IO       QSYS        *IMMED      6E2D63C42B8D16E74EA1DD08E3AF52EF     
     QC2UTIL1    QSYS        *IMMED      CB1F803582C31AC25B4DCBDD1B6D0948     
     QC2LOCAL    QSYS        *IMMED      D8C3F2D3D6C3C1D34040404040404040     
     QLEAWI      QSYS        *IMMED      44F70FABA08585397BDF0CF195F82EC1     
and this is the is the signatures list of the program we have not recreated but was created when the service program was first created.
                          Display Program Information                          
                                                                 Display 4 of 7
 Program  . . . . . . . :   FNDMSGCNT     Library  . . . . . . . :   OSPGM     
 Owner  . . . . . . . . :   CHRISH                                             
 Program attribute  . . :   CLE                                                
 Detail . . . . . . . . :   *SRVPGM                                            
                                                                               
                                                                               
 Type options, press Enter.                                                    
   5=Display                                                                   
                                                                               
      Service                                                                  
 Opt  Program     Library     Activation  Signature                            
      MSGFUNC     *LIBL       *DEFER      00000087A2946D99969999856D8495A2     
      SPCFUNC     *LIBL       *DEFER      00000000091DD45845D2200A47DBF533     
      QC2SYS      QSYS        *IMMED      000000000000000000009485A3A2A8A2     
      QC2UTIL1    QSYS        *IMMED      CB1F803582C31AC25B4DCBDD1B6D0948     
      QC2POSIX    QSYS        *IMMED      D8C3F2D7D6E2C9E74040404040404040     
      QC2LOCAL    QSYS        *IMMED      D8C3F2D3D6C3C1D34040404040404040     
      QLEAWI      QSYS        *IMMED      44F70FABA08585397BDF0CF195F82EC1     
You will notice that the signatures stored in the programs are different for the MSGFUNC service program, but each one relates to a signature that is stored in the Service Program signature list! Both programs will function without any problems against the service program even though the earlier program has no idea of the new functionality added to the new copy of the Service program and there is no need to recompile it..

So this time we have looked at how to update a service program without having to recompile all of the programs that have been bound to it. I think this very important to understand even if you are doing this in another language that uses Service programs and hopefully we have shown above just how easy it is. It might seem like a lot of work but we have built new programs and commands as well as update the service programs and their exports.

If you have any questions or concerns let me know, I will try to expand on the content were required.

We hope to progress this further with more functionality being added into the service programs and programs, the spoiler alert above points to one feature we will explore but others will include searching through source files for the messages and how we can improve the performance with a few small changes.

Hope you find the above helpful and interesting. If you want us to cover any particular area let us know.. The source on GitHub will be updated to include the changes we have made.

Chris..