星期一, 2月 05, 2024

Configuring the IBM i ssh, sftp, and scp clients to use public-key authentication

Configuring the IBM i ssh, sftp, and scp clients to use public-key authentication


Troubleshooting


Problem

This document provides the steps necessary to configure Public-key authentication on the IBM i OpenSSH clients to gain access to SSH servers.

Symptom


Cause


Environment


Diagnosing The Problem


Resolving The Problem

Public-key authentication allows the IBM i ssh, sftp, and scp clients to gain access to remote hosts without having to provide a password. The sftp and scp clients on the IBM i require Public-key authentication to gain access to ssh servers. Password authentication is not allowed with these two clients. The IBM i ssh client can use either Public-key authentication or Password authentication to gain access to ssh servers.

Prerequisites

5733SC1 IBM Portable Utilities for i5/OS *BASE & Option 1

57XXSS1 Option 33 (Portable Application Solutions Environment)

Assumptions

This document assumes the following:
  • The IBM i is running at V5R4 or higher of the operating system.
  • A user profile consisting of 8 characters or less to store the OpenSSH related objects needed to communicate with remote SSH servers.
  • The remote host is configured to accept and process inbound ssh connections.
Important Note: The user 'someuser' provided in the following examples is not the name of an actual IBM i profile. The commands that contain 'someuser' as part of the syntax should be replaced with the profile name that has been created to make outbound ssh connections.

Do the following to configure Public-key authentication on the IBM i:
1.Sign on a system that is running V5R4 or higher with the user profile designated for ssh-related functions.
2.On the operating system command line, run the CALL QP2TERM command to enter the PASE environment.
3.From within the PASE or Qshell environment, type the following commands:

Note: Licensed program product 57XXSS1 Option 30 (Qshell) is required to run the given commands in the Qshell environment.

a. Create a HOME directory on the IBM i to store the user's OpenSSH-related objects.
  • mkdir /home/someuser

b. Set permissions on the user's home directory.
  • chmod 755 /home/someuser



c. Create a rsa or ecdsa key pair that has no passphrase associated with it. Use the provided commands to create either a rsa or ecdsa key pair.

  • ssh-keygen -t rsa -N ""

  • ssh-keygen -t ecdsa -N ""

Note: During key generation, OpenSSH checks to see if there is a .ssh folder underneath the user's home directory. If one does not exist, the folder will be created in the user's home directory and the public/private key pair will be stored in it. The public key will have a .pub extension; for example, id_rsa.pub or id_ecdsa.pub.  The private key will be the one without the extension; for example, id_rsa or id_ecdsa.
Caution: The private keys generated by the ssh-keygen utility should be kept private.  It is very important to protect the private key from unauthorized individuals.  Anyone that can gain access to a user's private key has the potential to sign on to the SSH server where the corresponding public key has been copied.  Use IFS authorities to limit access to the private key to only the appropriate OpenSSH user.

d. Close the PASE or Qshell terminal session. Use the F3 key to exit the terminal session.
4.Change the home directory parameter in the user's profile to point to the IFS path of the home directory created in Step 3a.

CHGUSRPRF USRPRF(someuser) HOMEDIR('/home/someuser')
5.Sign off and sign back on up with the OpenSSH profile to allow the changes made to the HOMEDIR parameter to take effect. Note: Every time a user enters the PASE or Qshell environment, they will be placed into the IFS path specified in the HOMEDIR parameter in the user's profile.
6.
Send the public key that was generated in Step 3c to the SSH server administrator. The two most common methods for moving the public key to a PC are listed below:

a. Drill down to the IFS path /home/someuser/.ssh in IBM i Navigator, and right-click and select "Download" to save the file to your PC.

b. FTP the public key using binary mode into a folder on the PC.

Once the public key has been moved to the PC, you can send it to the SSH server administrator as an email attachment.

NOTE: Some SSH servers require the key to be in SecSH or Tectia format. (That is often the case if the SSH Server is running in Microsoft Windows)  If that is the case, and the SSH server administrator is unable to convert the key to the correct format themselves, you can follow technote https://www.ibm.com/support/pages/generating-openssh-public-key-and-converting-it-tectia-or-secsh-format, in particular from Step 5.
For example, to convert the id_rsa.pub key generated in step 3c to SecSH or Tectia format, you would run the following commands from within the users .ssh directory containing the keypair and then send the converted public key
ssh-keygen -e -f id_rsa.pub > id_rsa_win.pub
chmod 644 id_rsa_win.pub
7.Once the SSH server administrator has placed the public key into the appropriate location on the remote side, you can test the connection to see if Public-key authentication works.

a. On the operating system command line, run the CALL QP2TERM command to enter the PASE environment.

b. From within the PASE environment, execute the follow command:
  • ssh -T serveruid@somehost

Note: Replace serveruid with the name of the user profile that the SSH server administrator provided you with to gain access to the remote host. Replace somehost with either the IP address or host name of the remote system that you want to establish a connection with.

If this is the first time you have connected to the remote host using SSH, you will receive a message similar to the one below:
 
The authenticity of host 'somehost (x.x.x.x)' can't be established.      
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?       
 
Type yes and press the Enter key to add the server's public host key into the known_hosts file in the .ssh folder in the user's home directory. If Public-key authentication is successful, you will not be prompted for a password.

If the remote host provides shell access, use the hostname command to verify that you are truly logged in to the SSH server. The hostname command will return the name of the system on which you are actually logged in to.

c. Close the SSH connection:

Type exit and press the Enter key to disconnect the SSH connection.
8.Once Public-key authentication has been successfully configured, the sftp and scp clients can be used to transfer files securely between the IBM i and the remote SSH server.  Listed below is the syntax that can be used to initiate sftp connections to remote systems:

sftp serveruid@somehost

Note: Replace serveruid with the name of the user profile that the SSH server administrator provided you with to gain access to the remote host. Replace somehost with either the IP address or host name of the remote system that you want to establish a connection with.


Historical Number

538212497

Product Synonym

OpenSSH
5733SC1

星期四, 1月 11, 2024

An example DLQ handler rules table

Dead-letter Queue use by IBM MQ Channels

On IBM MQ for IBM i, you invoke the DLQ handler by setting the STRMQMDLQ command

STRMQMDLQ UDLMSGQ(ABC1.DEAD.LETTER.QUEUE) SRCMBR(QRULE) SRCFILE(library/QTXTSRC) MQMNAME(MY.QUEUE.MANAGER) The examples apply to the DLQ called ABC1.DEAD.LETTER.QUEUE, owned by the default queue manager. https://www.ibm.com/docs/en/ibm-mq/9.3?topic=table-example-dlq-handler-rules An example DLQ handler rules table An example dead-letter queue rules table for the runmqdlq command, containing a single control-data entry and several rules. ************************************************************************* * An example rules table for the runmqdlq command * ************************************************************************* * Control data entry * ------------------ * If no queue manager name is supplied as an explicit parameter to * runmqdlq, use the default queue manager for the machine. * If no queue name is supplied as an explicit parameter to runmqdlq, * use the DLQ defined for the local queue manager. * inputqm(' ') inputq(' ') * Rules * ----- * We include rules with ACTION (RETRY) first to try to * deliver the message to the intended destination. * If a message is placed on the DLQ because its destination * queue is full, attempt to forward the message to its * destination queue. Make 5 attempts at approximately * 60-second intervals (the default value for RETRYINT). REASON(MQRC_Q_FULL) ACTION(RETRY) RETRY(5) * If a message is placed on the DLQ because of a put inhibited * condition, attempt to forward the message to its * destination queue. Make 5 attempts at approximately * 60-second intervals (the default value for RETRYINT). REASON(MQRC_PUT_INHIBITED) ACTION(RETRY) RETRY(5) * The AAAA corporation are always sending messages with incorrect * addresses. When we find a request from the AAAA corporation, * we return it to the DLQ (DEADQ) of the reply-to queue manager * (&REPLYQM). * The AAAA DLQ handler attempts to redirect the message. MSGTYPE(MQMT_REQUEST) REPLYQM(AAAA.*) + ACTION(FWD) FWDQ(DEADQ) FWDQM(&REPLYQM) * The BBBB corporation never do things by half measures. If * the queue manager BBBB.1 is unavailable, try to * send the message to BBBB.2 DESTQM(bbbb.1) + action(fwd) fwdq(&DESTQ) fwdqm(bbbb.2) header(no) * The CCCC corporation considers itself very security * conscious, and believes that none of its messages * will ever end up on one of our DLQs. * Whenever we see a message from a CCCC queue manager on our * DLQ, we send it to a special destination in the CCCC organization * where the problem is investigated. REPLYQM(CCCC.*) + ACTION(FWD) FWDQ(ALARM) FWDQM(CCCC.SYSTEM) * Messages that are not persistent run the risk of being * lost when a queue manager terminates. If an application * is sending nonpersistent messages, it should be able * to cope with the message being lost, so we can afford to * discard the message. PERSIST(MQPER_NOT_PERSISTENT) ACTION(DISCARD) * For performance and efficiency reasons, we like to keep * the number of messages on the DLQ small. * If we receive a message that has not been processed by * an earlier rule in the table, we assume that it * requires manual intervention to resolve the problem. * Some problems are best solved at the node where the * problem was detected, and others are best solved where * the message originated. We don't have the message origin, * but we can use the REPLYQM to identify a node that has * some interest in this message. * Attempt to put the message onto a manual intervention * queue at the appropriate node. If this fails, * put the message on the manual intervention queue at * this node. REPLYQM('?*') + ACTION(FWD) FWDQ(DEADQ.MANUAL.INTERVENTION) FWDQM(&REPLYQM) ACTION(FWD) FWDQ(DEADQ.MANUAL.INTERVENTION)

星期五, 12月 22, 2023

2023-12-22 Get top cpu usage percentage job (TOPCPUPCT)

2023-12-22 Get top cpu usage percentage job (TOPCPUPCT)

File  : QRPGLESRC
Member: TOPCPUPCT
Type  : RPGLE

     **
     **  Program . . : TopCpuPct
     **  Description : Finds CPU Top  and notifies caller
     **  Author  . . : Vengoal Chang
     **  Published . : AS400 ePaper
     **  Date  . . . : November 21, 2023
     **
     **
     **  Program summary
     **  ---------------
     **
     **  Work management APIs:
     **    QGYOLJOB      Open list of jobs     Lists jobs on the system based on
     **                                        the specified selection criteria.
     **
     **                                        Optionally a sort order for the
     **                                        returned jobs can be specified -
     **                                        in this case the processor unit
     **                                        time percentage in descending
     **                                        order - listing the jobs having
     **                                        the highest CPU usage first.
     **
     **                                        The CPU processor time is measured
     **                                        for an interval of 10 seconds in
     **                                        this example.
     **
     **                                        The QGYOLJOB API is found in the
     **                                        QGY library as are all other open
     **                                        list APIs.
     **
     **    QWVRCSTK      Retrieve Call Stack   Lists the program call stack for
     **                                        the specified job or thread.
     **                                        The current invocation level is
     **                                        returned first.
     **
     **  Message handling API:
     **    QMHSNDM       Send message          Sends a message to the specified
     **                                        non-program message queue - here
     **                                        an informational message is sent
     **                                        to the current user running this
     **                                        program.
     **
     **  Open list APIs:
     **    QGYGTLE       Get list entries      To retrieve open lists entries
     **                                        from an already open list the
     **                                        QGYGTLE (Get List Entries) API
     **                                        is available.
     **
     **    QGYCLST       Close list            This API closes the previously
     **                                        opened list identified by the
     **                                        request handle parameter.
     **                                        Storage allocated is freed.
     **
     **  MI builtins:
     **    _MEMMOVE      Copy memory           Copies a string from one pointer
     **                                        specified location to another.
     **
     **  Unix Type - Signal APIs:
     **    Sleep                               Suspends program processing for
     **                                        the specified number of seconds.
     **
     **
     **  Sequence of events:
     **    1. The act jobs processor time limit percentage is retrieved
     **
     **    2. The list jobs API input parameters are initialized
     **
     **    3. The open list of jobs API is called to reset the job
     **       statistics.
     **
     **    4. Program is suspended for some seconds
     **
     **    5. The open list of jobs API is called to list the interactive
     **       jobs on the system returning the most CPU intensive jobs
     **       for the elapsed period first.
     **
     **    6. For each top cpu percent job a message is sent to the
     **       message queue.
     **
     **    7. The job list resources are cleaned up.
     **
     **    8. The program will loop 1 to 7, until manual job.
     **
     **  Programmer's notes:
     **
     **    As mentioned above library QGY must be in the job library list
     **    to succesfully run this program.
     **
     **    To retrieve another job's call stack *JOBCTL special authority is
     **    required.
     **
     **
     **  Compile options:
     **
     **    CrtRpgMod Module( TOPCPUPCT )  DbgView( *LIST )
     **
     **    CrtPgm    Pgm( TOPCPUPCT )
     **              Module( TOPCPUPCT )
     **
     **  Usage sample:
     **    Get top first CPU% job per 60 secs with following:
     **     SBMJOB
     **       CMD(TOPCPUPCT TOPCOUNT(001) INTERVAL(00060)
     **                     TOMSGQ(*SYSOPR))
     **       Job(TOPPCTPCT)
     **
     **    Get top 5     CPU% job per 60 secs with following:
     **     SBMJOB
     **       CMD(TOPCPUPCT TOPCOUNT(005) INTERVAL(00060)
     **                     TOMSGQ(*SYSOPR))
     **       Job(TOPPCTPCT)
     **
     **-- Control spec:  -----------------------------------------------------**
     H Option( *SrcStmt )  DecEdit( *JobRun )  BndDir( 'QC2LE' )
     H DftActGrp(*NO)
     **-- System information:  -----------------------------------------------**
     D PgmSts         SDs
     D  PsPgmNam         *Proc
     D  PsSts                         5a   Overlay( PgmSts:  11 )
     D  PsCurJob                     10a   Overlay( PgmSts: 244 )
     D  PsUsrPrf                     10a   Overlay( PgmSts: 254 )
     D  PsJobNbr                      6a   Overlay( PgmSts: 264 )
     D  PsCurUsr                     10a   Overlay( PgmSts: 358 )
     **-- API error data structure:  -----------------------------------------**
     D ApiError        Ds
     D  AeBytPrv                     10i 0 Inz( %Size( ApiError ))
     D  AeBytAvl                     10i 0
     D  AeExcpId                      7a
     D                                1a
     D  AeExcpDta                   128a
     **-- API parameters:  ---------------------------------------------------**
     D JlRtnRcdNbr     s             10i 0 Inz( 1 )
     D JlNbrFldRtn     s             10i 0 Inz( %Elem( JlKeyFld ))
     D JlKeyFld        s             10i 0 Dim( 3 )
     **-- Job information:
     D JlJobInf        Ds           512
     D  JbJobId                      26a
     D   JbJobUsd                    10a   Overlay( JbJobId: 1 )
     D   JbUsrUsd                    10a   Overlay( JbJobId: *Next )
     D   JbNbrUsd                     6a   Overlay( JbJobId: *Next )
     D  JbActSts                      4a
     D  JbJobTyp                      1a
     D  JbJobSubTyp                   1a
     D  JbDtaLen                     10i 0
     D                                4a
     D  JbDta                       256a
     **-- Key information:
     D JlKeyInf        Ds
     D  KiFldNbrRtn                  10i 0
     D  KiKeyInf                     20a   Dim( %Elem( JlKeyFld ))
     D   KiFldInfLen                 10i 0 Overlay( KiKeyInf :  1 )
     D   KiKeyFld                    10i 0 Overlay( KiKeyInf :  5 )
     D   KiDtaTyp                     1a   Overlay( KiKeyInf :  9 )
     D                                3a   Overlay( KiKeyInf : 10 )
     D   KiDtaLen                    10i 0 Overlay( KiKeyInf : 13 )
     D   KiDtaOfs                    10i 0 Overlay( KiKeyInf : 17 )
     **-- Sort information:
     D JlSrtInf        Ds
     D  SiNbrKeys                    10i 0 Inz( 1 )
     D  SiSrtInf                     12a   Dim( 10 )
     D   SiKeyFldOfs                 10i 0 Overlay( SiSrtInf :  1 )
     D   SiKeyFldLen                 10i 0 Overlay( SiSrtInf :  5 )
     D   SiKeyFldTyp                  5i 0 Overlay( SiSrtInf :  9 )
     D   SiSrtOrd                     1a   Overlay( SiSrtInf : 11 )
     D   SiRsv                        1a   Overlay( SiSrtInf : 12 )
     **-- List information:
     D JlLstInf        Ds
     D  LiRcdNbrTot                  10i 0
     D  LiRcdNbrRtn                  10i 0
     D  LiHandle                      4a
     D  LiRcdLen                     10i 0
     D  LiInfSts                      1a
     D  LiDts                        13a
     D  LiLstSts                      1a
     D                                1a
     D  LiInfLen                     10i 0
     D  LiRcd1                       10i 0
     D                               40a
     **-- Selection information:
     D JlSltInf        Ds
     D  SiJobNam                     10a   Inz( '*ALL' )
     D  SiUsrNam                     10a   Inz( '*ALL' )
     D  SiJobNbr                      6a   Inz( '*ALL' )
     D  SiJobTyp                      1a   Inz( '*' )
     D                                1a
     D  SiOfsPriSts                  10i 0 Inz( 60 )
     D  SiNbrPriSts                  10i 0 Inz( 0 )
     D  SiOfsActSts                  10i 0 Inz( 70 )
     D  SiNbrActSts                  10i 0 Inz( 0 )
     D  SiOfsJbqSts                  10i 0 Inz( 78 )
     D  SiNbrJbqSts                  10i 0 Inz( 0 )
     D  SiOfsJbqNam                  10i 0 Inz( 88 )
     D  SiNbrJbqNam                  10i 0 Inz( 0 )
     **
     D  SiPriSts                     10a   Dim( 1 )
     D  SiActSts                      4a   Dim( 2 )
     D  SiJbqSts                     10a   Dim( 1 )
     D  SiJbqNam                     20a   Dim( 1 )
     **-- Job information key fields:
     D JbKeyDta        Ds
     D  JbPrcUniTim                  20u 0
     D  JbPrcUniPct                   9b 1
     D  JbPrcUniTimE                 20u 0
     **-- General return data:
     D JlGenDta        Ds
     D  GdBytRtn                     10i 0
     D  GdBytAvl                     10i 0
     D  GdElpTim                     20u 0
     D                               16a
     **-- MatRmd parameters:  ------------------------------------------------**
     D MatRscMgDt      Ds
     D  RdBytPrv                     10i 0 Inz( %Size( MatRscMgDt ))
     D  RdBytAvl                     10i 0
     D  RdTimDay                      8a
     D  RdData
     D   RdPrcTimIpl                 20u 0 Overlay( RdData: 1 )
     D   RdPrcTimScWl                20u 0 Overlay( RdData: *Next )
     D   RdPrcTimDb                  20u 0 Overlay( RdData: *Next )
     D   RdPrcTimDbTh                 5u 0 Overlay( RdData: *Next )
     D   RdPrcTimDbLm                 5u 0 Overlay( RdData: *Next )
     D   RdRsv1                      10u 0 Inz( x'00' )
     D                                     Overlay( RdData: *Next )
     D   RdPrcTimInt                 20u 0 Overlay( RdData: *Next )
     D   RdPrcTimIntT                 4b 1 Overlay( RdData: *Next )
     D   RdPrcTimIntL                 4b 1 Overlay( RdData: *Next )
     D   RdRsv2                      10u 0 Inz( x'00' )
     D                                     Overlay( RdData: *Next )
     **
     D MatCtlDta       Ds
     D  CdSltOpt                      1a   Inz( x'01' )
     D  CdRsv                         7a   Inz( *Allx'00' )
     **-- Global variables:  -------------------------------------------------**
     D Ix              s              5i 0
     D Count           s              5i 0
     D Interval        s             10u 0
     D TopCount        s              3S 0
     D PgmNam          s             10a
     D MsgDta          s            256a   Varying
     D MsgKey          s              4a
     D SysTime         s               z   inz(*sys)
     **-- API constants:  ----------------------------------------------------**
     D JOB_RESET_STAT  c                   '1'
     D JOB_KEEP_STAT   c                   '0'
     **-- Open list of jobs:  ------------------------------------------------**
     D LstJobs         Pr                  ExtPgm( 'QGYOLJOB' )
     D  LjRcvVar                  65535a          Options( *VarSize )
     D  LjRcvVarLen                  10i 0 Const
     D  LjFmtNam                      8a   Const
     D  LjRcvVarDfn               65535a          Options( *VarSize )
     D  LjRcvDfnLen                  10i 0 Const
     D  LjLstInf                     80a
     D  LjNbrRcdRtn                  10i 0 Const
     D  LjSrtInf                   1024a   Const  Options( *VarSize )
     D  LjJobSltInf                1024a   Const  Options( *VarSize )
     D  LjJobSltLen                  10i 0 Const
     D  LjNbrFldRtn                  10i 0 Const
     D  LjKeyFldRtn                  10i 0 Const  Options( *VarSize )  Dim( 32 )
     D  LjError                    1024a          Options( *VarSize )
     **
     D  LjJobSltFmt                   8a   Const  Options( *NoPass )
     **
     D  LjResStc                      1a   Const  Options( *NoPass )
     D  LjGenRtnDta                  32a          Options( *NoPass: *VarSize )
     D  LjGenRtnDtaLn                10i 0 Const  Options( *NoPass )
     **-- Get list entry:  ---------------------------------------------------**
     D GetLstEnt       Pr                  ExtPgm( 'QGYGTLE' )
     D  GlRcvVar                  65535a          Options( *VarSize )
     D  GlRcvVarLen                  10i 0 Const
     D  GlHandle                      4a   Const
     D  GlLstInf                     80a
     D  GlNbrRcdRtn                  10i 0 Const
     D  GlRtnRcdNbr                  10i 0 Const
     D  GlError                    1024a          Options( *VarSize )
     **-- Close list:  -------------------------------------------------------**
     D CloseLst        Pr                  ExtPgm( 'QGYCLST' )
     D  ClHandle                      4a   Const
     D  ClError                    1024a          Options( *VarSize )
     **-- Send message:  -----------------------------------------------------**
     D SndMsg          Pr                  ExtPgm( 'QMHSNDM' )
     D  SmMsgId                       7a   Const
     D  SmMsgFq                      20a   Const
     D  SmMsgDta                    512a   Const Options( *VarSize )
     D  SmMsgDtaLen                  10i 0 Const
     D  SmMsgTyp                     10a   Const
     D  SmMsgQq                    1000a   Const Options( *VarSize )
     D  SmMsgQnbr                    10i 0 Const
     D  SmMsgQrpy                    20a   Const
     D  SmMsgKey                      4a
     D  SmError                      10i 0 Const
     **
     D  SmCcsId                      10i 0 Const Options( *NoPass )
     **-- Copy memory:  ------------------------------------------------------**
     D memcpy          Pr              *   ExtProc( '_MEMMOVE' )
     D  outmem                         *   Value
     D  inpmem                         *   Value
     D  memsiz                       10u 0 Value
     **-- Delay job:  --------------------------------------------------------**
     D sleep           Pr            10i 0 ExtProc( 'sleep' )
     D  seconds                      10u 0 Value
     **-- Get top stack entry:  ----------------------------------------------**
     D GetTopStkE      Pr            20a
     D  GtJobId                      26a   Const
     **-- Materialize resource management data:  -----------------------------**
     D MatRmd          Pr                  ExtProc( '_MATRMD' )
     D  Rcv                                Like( MatRscMgDt )
     D  Ctl                                Like( MatCtlDta )
     **
     **-- Mainline:  ---------------------------------------------------------**
     **
     C     *Entry        Plist
     C                   Parm                    TopCount_p        3
     C                   Parm                    Interval_p        5
     C                   Parm                    ToMsgQ           10
     **
     C                   Eval      TopCount    = %Int(TopCount_p)
     C                   Eval      Interval    = %Int(Interval_p)

     C                   Select
     C                   When      ToMsgQ      =  '*SYSOPR'
     C                   Eval      ToMsgQ      =  'QSYSOPR'
     C                   When      ToMsgQ      =  '*CURUSR'
     C                   Eval      ToMsgQ      =  PsCurUsr
     C                   EndSl
     **
     **-- Job information return fields:
     C                   Eval      JlKeyFld(1) = 312
     C                   Eval      JlKeyFld(2) = 314
     C                   Eval      JlKeyFld(3) = 315
     **
     **-- Sort field specification:
     C                   Eval      SiNbrKeys      = 1
     C                   Eval      SiKeyFldOfs(1) = 49
     C                   Eval      SiKeyFldLen(1) = 4
     C                   Eval      SiKeyFldTyp(1) = 0
     C                   Eval      SiSrtOrd(1)    = '2'
     C                   Eval      SiRsv(1)       = x'00'
     **
     **-- Initialize job CPU measurement:
     **-- NOTE: Statistics only reset if return records are requested
     **
     C                   DoW       1 = 1
     C                   CallP     LstJobs( JlJobInf
     C                                    : %Size( JlJobInf )
     C                                    : 'OLJB0300'
     C                                    : JlKeyInf
     C                                    : %Size( JlKeyInf )
     C                                    : JlLstInf
     C                                    : 1
     C                                    : JlSrtInf
     C                                    : JlSltInf
     C                                    : %Size( JlSltInf )
     C                                    : JlNbrFldRtn
     C                                    : JlKeyFld
     C                                    : ApiError
     C                                    : 'OLJS0100'
     C                                    : JOB_RESET_STAT
     C                                    : JlGenDta
     C                                    : %Size( JlGenDta )
     C                                    )
     **
     **-- Wait 10 seconds:
     C                   CallP     sleep( Interval )
     **
     **-- Retrieve job list:
     C                   CallP     LstJobs( JlJobInf
     C                                    : %Size( JlJobInf )
     C                                    : 'OLJB0300'
     C                                    : JlKeyInf
     C                                    : %Size( JlKeyInf )
     C                                    : JlLstInf
     C                                    : 1
     C                                    : JlSrtInf
     C                                    : JlSltInf
     C                                    : %Size( JlSltInf )
     C                                    : JlNbrFldRtn
     C                                    : JlKeyFld
     C                                    : ApiError
     C                                    : 'OLJS0100'
     C                                    : JOB_KEEP_STAT
     C                                    : JlGenDta
     C                                    : %Size( JlGenDta )
     C                                    )
     **
     C                   If        AeBytAvl    =  *Zero
     **
     C                   Eval      Count       = 0
     C                   DoW       LiLstSts    <> '2'           Or
     C                             LiRcdNbrTot >  JlRtnRcdNbr
     **
     C                   ExSr      GetCpuDta
     C                   ExSr      ChkCpuPct
     **
     C*                  ExSr      SndCmpMsg
     **
     C                   If        Count      >= TopCount
     C                   Leave
     C                   EndIf
     **
     C                   Eval      JlRtnRcdNbr = JlRtnRcdNbr + 1
     **
     C                   CallP     GetLstEnt( JlJobInf
     C                                      : %Size( JlJobInf )
     C                                      : LiHandle
     C                                      : JlLstInf
     C                                      : 1
     C                                      : JlRtnRcdNbr
     C                                      : ApiError
     C                                      )
     **
     C                   If        AeBytAvl    > *Zero
     C                   Leave
     C                   EndIf
     **
     C                   EndDo
     **
     C                   CallP     CloseLst( LiHandle
     C                                     : ApiError
     C                                     )
     **
     C                   EndIf
     **
     C                   EndDo
     **
     C                   Eval      *InLr       = *On
     **
     C                   Return
     **
     **-- Get CPU data:  -----------------------------------------------------**
     C     GetCpuDta     BegSr
     **
     C                   Clear                   JbKeyDta
     **
     C                   For       Ix = 1  To KiFldNbrRtn
     **
     C                   Select
     C                   When      KiKeyFld(Ix) = 312
     C                   CallP     memcpy( %Addr( JbPrcUniTim )
     C                                   : %Addr( JlJobInf ) +
     C                                     KiDtaOfs(Ix)
     C                                   : KiDtaLen(Ix)
     C                                   )
     **
     C                   When      KiKeyFld(Ix) = 314
     C                   CallP     memcpy( %Addr( JbPrcUniPct )
     C                                   : %Addr( JlJobInf ) +
     C                                     KiDtaOfs(Ix)
     C                                   : KiDtaLen(Ix)
     C                                   )
     **
     C                   When      KiKeyFld(Ix) = 315
     C                   CallP     memcpy( %Addr( JbPrcUniTimE )
     C                                   : %Addr( JlJobInf ) +
     C                                     KiDtaOfs(Ix)
     C                                   : KiDtaLen(Ix)
     C                                   )
     C                   EndSl
     C                   EndFor
     **
     C                   EndSr
     **-- Check CPU percent:  ------------------------------------------------**
     C     ChkCpuPct     BegSr
     **
     C                   Eval      Count = Count + 1
     C                   Eval      PgmNam      = GetTopStkE( JbJobId )
     **
     C                   Eval      SysTime     = %Timestamp()
     C                   Eval      MsgDta      =
     C                                '{ "CPUPCTMSG": { '                     +
     C                                     '"QDATETIME" : "'                  +
     C                                       %Char(%Timestamp():*ISO) + '", ' +
     C                                     '"JobNam" : "'                     +
     C                                       %Trim(JbJobUsd) + '", '          +
     C                                     '"JobUsr" : "'                     +
     C                                       %Trim(JbUsrUsd) + '", '          +
     C                                     '"JobNbr" : "'                     +
     C                                       %Trim(JbNbrUsd) + '", '          +
     C                                     '"CpuPct" : "'                     +
     C                                       %Char( JbPrcUniPct ) + '", '     +
     C                                     '"PgmNam" : "'                     +
     C                                       %Trim( PgmNam ) + '"  '          +
     C                                '}  }'
     **
     C                   CallP(e)  SndMsg( *Blanks
     C                                   : *Blanks
     C                                   : MsgDta
     C                                   : %Len( MsgDta )
     C                                   : '*INFO'
     C                                   : ToMsgQ   + '*LIBL'
     C                                   : 1
     C                                   : *Blanks
     C                                   : MsgKey
     C                                   : 0
     C                                   )
     **
     C                   EndSr
     **-- Get top stack entry:  ----------------------------------------------**
     P GetTopStkE      B                   Export
     D                 Pi            20a
     D  GtJobId                      26a   Const
     **-- API parameters:
     D CsRcvVar        Ds
     D  CsBytRtn                     10i 0
     D  CsBytAvl                     10i 0
     D  CsNbrStkE                    10i 0
     D  CsOfsStkE                    10i 0
     D  CsNbrEntRtn                  10i 0
     D  CsThrId                       8a
     D  CsInfSts                      1a
     D  CsCalStk                  32767a
     **
     D CsCalStkE       Ds                  Based( pCalStkE )
     D  CsStkEntLen                  10i 0
     D  CsOfsStmIds                  10i 0
     D  CsNbrStmIds                  10i 0
     D  CsOfsPrcNam                  10i 0
     D  CsLenPrcNam                  10i 0
     D  CsRqsLvl                     10i 0
     D  CsPgmNam                     10a
     D  CsPgmLib                     10a
     D  CsMiInst                     10i 0
     D  CsModNam                     10a
     D  CsModLib                     10a
     D  CsCtlBdy                      1a
     D  CsRsv                         3a
     D  CsActGrpNbr                  10u 0
     D  CsActGrpNam                  10a
     D  CsAddInf                   4096a
     **
     D  CsStmIds                     10a   Dim( 16 )
     D  CsPrcNam                    512a
     **
     D CsJobId         Ds
     D  JiJobId                      26a
     D   JiJobNam                    10a   Overlay( JiJobId: 1 )
     D   JiUsrNam                    10a   Overlay( JiJobId: *Next )
     D   JiJobNbr                     6a   Overlay( JiJobId: *Next )
     D  JiIntId                      16a
     D  JiRsv                         2a   Inz( *Allx'00' )
     D  JiThrInd                     10i 0 Inz( 2 )
     D  JiThrId                       8a   Inz( *Allx'00' )
     **-- Retrieve call stack:
     D RtvCalStk       Pr                  ExtPgm( 'QWVRCSTK' )
     D  RcRcvVar                  32767a
     D  RcRcvVarLen                  10i 0 Const
     D  RcRcvInfFmt                   8a   Const
     D  RcJobId                      56a   Const
     D  RcJobIdFmt                    8a   Const
     D  RcError                   32767a          Options( *VarSize )
     **
     D EntNbr          s              5u 0
     **-- Get stack entries:  ------------------------------------------------**
     **
     C                   Eval      JiJobId     =  GtJobId
     **
     C                   CallP     RtvCalStk( CsRcvVar
     C                                      : %Size( CsRcvVar )
     C                                      : 'CSTK0100'
     C                                      : CsJobId
     C                                      : 'JIDF0100'
     C                                      : ApiError
     C                                      )
     **
     C                   If        AeBytAvl    = *Zero
     C                   Eval      pCalStkE    = %Addr( CsRcvVar ) + CsOfsStkE
     **
     C                   For       EntNbr = 1  to CsNbrEntRtn
     **
     C                   If        EntNbr      = 1
     **
     C                   Eval      CsStmIds    = *Blanks
     C                   Eval      CsPrcNam    = *Blanks
     **
     C                   If        CsOfsStmIds > *Zero
     C                   CallP     MemCpy( %Addr( CsStmIds )
     C                                   : %Addr( CsCalStkE ) +
     C                                     CsOfsStmIds
     C                                   : CsNbrStmIds * %Size( CsStmIds )
     C                                   )
     C                   EndIf
     **
     C                   If        CsOfsPrcNam > *Zero
     C                   CallP     MemCpy( %Addr( CsPrcNam )
     C                                   : %Addr( CsCalStkE ) +
     C                                     CsOfsPrcNam
     C                                   : CsLenPrcNam
     C                                   )
     C                   EndIf
     **
     C                   Leave
     C                   EndIf
     **
     C                   If        EntNbr      < CsNbrEntRtn
     C                   Eval      pCalStkE    = PCalStkE    + CsStkEntLen
     C                   EndIf
     C                   EndFor
     **
     C                   Return    CsPgmNam + CsPgmLib
     **
     C                   Else
     C                   Return    *Blanks
     C                   EndIf
     **
     P GetTopStkE      E


File  : QCMDSRC
Member: TOPCPUPCT
Type  : CMD

/*  ===============================================================  */
/*  = Command....... TopCpuPct                                    =  */
/*  = CPP........... TopCpuPct RPGLE                              =  */
/*  = Description... Send WRKACTJOB CPUPCT top to user            =  */
/*  =                                                             =  */
/*  =                                                             =  */
/*  = CrtCmd      Cmd( TopCpuPct )                                =  */
/*  =             Pgm( TopCpuPct  )                               =  */
/*  =             SrcFile( YourSourceFile )                       =  */
/*  ===============================================================  */
/*  = Date  : 2023/11/21                                          =  */
/*  = Author: Vengoal Chang                                       =  */
/*  ===============================================================  */
             CMD        PROMPT('Top Cpu Percent Job')

             PARM       KWD(TOPCOUNT) TYPE(*CHAR) LEN(3)             +
                          RANGE('001' '999')                         +
                          FULL(*YES)                                 +
                          PROMPT('TOP CPU JOB COUNT')

             PARM       KWD(INTERVAL) TYPE(*CHAR) LEN(5)             +
                          RANGE('00001' '99999')                     +
                          FULL(*YES)                                 +
                          PROMPT('Interval second')

             PARM       KWD(TOMSGQ) TYPE(*CHAR) LEN(10)              +
                          DFT(*SYSOPR)                               +
                          SPCVAL((*SYSOPR) (*CURUSR))                +
                          PROMPT('Message To MsgQ')


Program to capture CPU usage over time (with SQL)