Wednesday, April 22, 2026

Checking the Electronic Service Agent

The Electronic Service Agent, ESA, is part of the IBM i operating system. Its purpose is to monitor your system's health, detect potential hardware failures, and report issues directly to IBM Support. If it encounters problems it can "call home" to report the issue to IBM.

To be able to "call home" it must be able to connect to IBM. How can we test that it can?

We can either use:

  • VFYSRVAGT the Verify Service Agent command
  • ELECTRONIC_SERVICE_AGENT_INFO SQL View that shows the same information

I am going to explain both of these below.

To check the connection using the command I would enter the following on a command line:

VFYSRVAGT TYPE(*DETAIL)

When the command finishes the following is displayed at the bottom of the screen:

Connectivity verification operation completed successfully.

When I position the cursor on the message and I press either the Help key or F1 the message details are displayed:

                        Additional Message Information

Message ID . . . . . . : CPIEF89       Severity . . . . . . . : 00
Message type . . . . . : Completion
Date sent  . . . . . . : DD/DD/DD      Time sent  . . . . . . : TT:TT:TT

Message . . . . :   Connectivity verification operation completed
  successfully.
Cause . . . . . :   Communications has successfully occurred with IBM.
  Detailed result is output under /QIBM/UserData/OS400/ServiceAgent.
Recovery  . . . :   None.

I would use ACS's "Integrate File System" viewer, but I can use SQL to get the same information without having to leave ACS's "Run SQL Scripts".

I would use the IFS_OBJECT_STATISTICS SQL Table function to return a list of objects in the IFS directory /QIBM/UserData/OS400/ServiceAgent:

01  SELECT SYSTOOLS.IFS_PATH(PATH_NAME,'FILE NAME') AS "File name",
02         OBJECT_CHANGE_TIMESTAMP
03    FROM TABLE(QSYS2.IFS_OBJECT_STATISTICS(
04                START_PATH_NAME => '/QIBM/UserData/OS400/ServiceAgent',
05                SUBTREE_DIRECTORIES => 'NO',
06                OBJECT_TYPE_LIST => '*STMF')) 
07   WHERE DATE(OBJECT_CHANGE_TIMESTAMP) = CURRENT_DATE

Line 1: As I know the IFS path name I only need to return the file name. I am using IFS_PATH Scalar function to extract just the file name for me.

Line 2: The object change timestamp will allow me to find the files in the IFS that have been changed today.

Lines 3 – 6: IFS_OBJECT_STATISTICS is only going to return a list of the objects that are in the IFS path name given, no information from any subdirectories, and only information about stream files.

Line 7: I only want to return any objects that have been changed today.

My results are:

File name                    OBJECT_CHANGE_TIMESTAMP
--------------------------   -----------------------
VerifyConnectionResult.txt   DDDD-DD-DD TT:TT:TT
VerifyConnectionResult.xml   DDDD-DD-DD TT:TT:TT

Again I am going to use SQL to show the contents of these files, using the IFS_READ Table function. First I am going to read the Text file:

01  SELECT LINE
02  FROM TABLE(QSYS2.IFS_READ('/QIBM/UserData/OS400/ServiceAgent/VerifyConnectionResult.txt'))

Which returns the following long line of information, I have to show it one two lines below:

LINE
-------------------------------------------------------------------------------------------
ESA status:Activated
Connectivity to IBM: DirectLANConnect
   ResultByIP   |ResultByHostname|      Server Type        |           Hostname           |
success         |success         |Edge_Problem_Report_1    |esupport.ibm.com              |
success         |success         |Edge_Problem_Report_1    |esupport.ibm.com              |


--------------------------------------------------------
                  IP Address                  |Port
192.123.6.31                                  |443
170.456.123.55                                |443

I can use the following statement to read the XML file:

01  SELECT LINE
02  FROM TABLE(QSYS2.IFS_READ('/QIBM/UserData/OS400/ServiceAgent/VerifyConnectionResult.xml'))

This returns:

LINE
-----------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<esa-status>Activated<esa-connection>
<path-type>DirectLANConnect</path-type>
</esa-connection>
<ibm-server>
<statusByIp>success</statusByIp>
<statusByHostname>success</statusByHostname>
<type>Edge_Problem_Report_1</type>
<name>esupport.ibm.com</name>
<ip>192.123.6.31</ip>
<port>443</port>
</ibm-server>
<ibm-server>
<statusByIp>success</statusByIp>
<statusByHostname>success</statusByHostname>
<type>Edge_Problem_Report_1</type>
<name>esupport.ibm.com</name>
<ip>170.456.123.55</ip>
<port>443</port>
</ibm-server>
</esa-status>

Using the command and its output I can get to the information about the ESA, but it is not simple. Using the SQL View makes it simple.

I can use the following statement to get the same results:

01  SELECT * FROM QSYS2.ELECTRONIC_SERVICE_AGENT_INFO

Which returns:

                               PROXY_HOST  PROXY   PROXY   RESULT_BY_  RESULT_BY_
ESA_STATUS ESA_CONNECTION      _OR_IP      _PORT   _ID     IP_ADDRESS  HOSTNAME     SERVER_TYPE
---------- ------------------  ----------  ------  ------  ----------  -----------  ---------------------
ACTIVATED  DIRECT LAN CONNECT  <NULL>      <NULL>  <NULL>  SUCCESS     SUCCESS      EDGE PROBLEM REPORT 1
ACTIVATED  DIRECT LAN CONNECT  <NULL>      <NULL>  <NULL>  SUCCESS     SUCCESS      EDGE PROBLEM REPORT 1


                                     SERVER
SERVER_HOSTNAME   SERVER_IP_ADDRESS  _PORT
----------------  -----------------  ------
ESUPPORT.IBM.COM  192.123.6.31          443
ESUPPORT.IBM.COM  170.456.123.55        443

The above is for a connected situation.

If ESA is not connected to IBM then ESA_CONNECTION would be null, and RESULT_BY_IP_ADDRESS and RESULT_BY_HOSTNAME would both contain "FAILURE".

If ESA_CONNECTION contains "INACTIVE" ESA has not been activated.

What if I wanted to monitor if the ESA loses connection to IBM. That is what this RPG program will do. I am going to break it into parts to make it easier for me to explain. Let me start with the global definitions:

01  **free
02  ctl-opt main(Main) option(*srcstmt) dftactgrp(*no) actgrp(*caller) ;

03  dcl-ds Data qualified dim(*auto : 10) ;
04    ServerIP char(45) ;
05  end-ds ;

Line 2: My favorite RPG control options.

Lines 3 – 5: This data structure array that will contain the IBM server's IP address that I will retrieve from ELECTRONIC_SERVICE_AGENT_INFO. As the DIM contains "*AUTO" it is an auto-extending array, that can contain any number of elements from zero to ten.

And now into the Main procedure:

06  dcl-proc Main ;
07    dcl-pr sleep int(10) extproc('sleep') ;
08      *n uns(10) value ;
09    end-pr ;

10    dcl-ds Single likeds(Data) ;

11    dcl-s MessageLength uns(5) ;
12    dcl-s MessageText char(512) inz ;
13    dcl-s Seconds uns(10) inz(10) ;

14    dow (*on) ;
15      GetData() ;

16      if (%elem(Data) > 0) ;
17          for-each Single in Data ;
18            MessageText = 'Connection to ' + %trimr(Single.ServerIP) +
19                         ' has been lost' ;
20            MessageLength = %size(MessageText) ;

21            exec sql CALL QSYS2.SEND_MESSAGE(
22                            MESSAGE_ID => 'CPF9898',
23                            MESSAGE_LENGTH => :MessageLength,
24                            MESSAGE_TEXT => :MessageText,
25                            MESSAGE_FILE_LIBRARY => 'QSYS',
26                            MESSAGE_FILE => 'QCPFMSG') ;
27          endfor ;
28      endif ;

29      sleep(Seconds) ;
30    enddo ;
31  end-proc ;

Line 6: Start of the Main procedure.

Lines 7 – 9: The procedure prototype to call the sleep C procedure. This will "sleep" the program for the number of seconds passed to it.

Line 10: I am defining this data structure to be the same as Data using the LIKEDS keyword, it will not be an data structure array.

Lines 11 – 13: Defining various variables to be used in this procedure.

Line 14: I have a never ending Do group starting here, that ends on line 30.

Line 15: This is the subprocedure that gets the data from ELECTRONIC_SERVICE_AGENT_INFO. I will describe what it does later.

Line 16: As the array is an auto-extending array if it has more than zero elements it retrieved data from the View.

Line 17: The best way to "read" all the elements of an auto-extending array is to use FOR-EACH, as it will read all the elements of the array. In this case it is reading each element from the data structure array Data into the data structure Single.

Lines 18 and 19: Make a message, including the IP address of the IBM server, that will be sent.

Line 20: Calculate the length of string within the message text.

Lines 21 – 26: I am using the SEND_MESSAGE SQL procedure to send the message I generated to the system operator message queue. I use the message CPF9898 as it contains no text, just a parameter that will be message text.

Line 29: As I have sent messages for all of the ESA servers that are not connected I can now sleep, before I repeat. I have the number of seconds as ten, which IMHO, is ideal for testing, but too short for production.

Lastly I have the subprocedure that gets the data from the ELECTRONIC_SERVICE_AGENT_INFO View:

32  dcl-proc GetData ;
33    dcl-s Elements uns(3) inz(%elem(Data : *max)) ;

34    %elem(Data) = 0 ;

35    exec sql DECLARE C0 CURSOR FOR
36               SELECT SERVER_IP_ADDRESS
37                 FROM QSYS2.ELECTRONIC_SERVICE_AGENT_INFO
38                WHERE ESA_CONNECTION IS NULL
39                  FOR READ ONLY ;

40    exec sql OPEN C0 ;

41    exec sql FETCH C0 FOR :Elements ROWS INTO :Data ;

42  on-exit ;
43    exec sql CLOSE C0 ;
44  end-proc ;

Line 33: This variable contains the maximum number of elements the array Data could contain.

Line 34: By setting the number of elements in the array to zero this in effect initializes it.

Lines 35 – 39: A cursor is defined to return the server IP address column from ELECTRONIC_SERVICE_AGENT_INFO for any rows where the ESA connection is null, or "down".

Line 40: The cursor is opened.

Line 41: Any eligible rows are fetched into the data structure array.

Lines 42 and 43: I am using an ON-EXIT section to ensure the cursor is closed, no matter how this subprocedure ends.

After compiling this program, I would submit it to run in a subsystem as a "never ending job". And it will alert any time an ESA connection goes "down".

 

 

This article was written for IBM i 7.6, and should work for some earlier releases too.

No comments:

Post a Comment

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.