特定コマンドのログ・別のコマンドへの取替え

ある特定の処理がシステムで実行された場合にはそれをログしておきたい、という要件があることがあります。
たとえばオブジェクトの削除だったり、セキュリティの変更だったり、ファイルなどの保管や復元などがモニターの対象として考えられますね。
(まぁ、要するに"監視"ですが)

IBM i の場合、モニター対象になるようなイベントには通常特定のコマンドが対応しています。(オブジェクト指向のインターフェイスのおかげですね)
対象オブジェクトとアクセス方法/メソッドがわかればコマンドが特定されますので、そのコマンドをモニターすることで、あるオブジェクトに対する特定の処理をかんたんにログ/監視することができる、というわけです。

今回はそのやり方を見ていきましょう。


出口プログラム

出口プログラムという仕組みを使用します。システム上の特定のイベント(出口点)に対してユーザー作成のプログラムを呼び出す仕組みです。
以前も「データベースのオープン毎に処理を行う (Exit プログラム)」や「データベースのオープン毎に処理を CLで行う (Exit プログラム)」などで使用しています。

ごくごくカンタンに仕組みを説明すると …

コマンドが(F4キーによるプロンプト)も含めて呼ばれた時に通過する出口点というものがあります。
今回利用する出口点は QIBM_QCA_RTV_COMMAND といって、ここで待ち伏せをしていると RTVC0100 というフォーマットに則ってプログラムに情報が提供される、という構造になっています。

RTVC0100 Format

The following table shows the format of the information supplied to a retrieve command exit program. For a description of the fields in this format, see Field Descriptions.
Offset Type Field
Dec Hex
0 0 CHAR(20) Exit point name
20 14 CHAR(8) Exit point format name
28 1C CHAR(10) Command name
38 26 CHAR(10) Library name
48 30 CHAR(4) Reserved
52 34 BIN(4) Offset to original command string
56 38 BIN(4) Length of original command string
60 3C BIN(4) Offset to replacement command string
64 40 BIN(4) Length of replacement command string
68 44 BINARY(4) Offset to proxy chain
72 48 BINARY(4) Number of entries in proxy chain
    CHAR(*) Original command string
    CHAR(*) Replacement command string
Proxy commands and libraries. These fields repeat in the order listed. CHAR(10) Proxy command name
CHAR(10) Proxy command library name

モニターしたいコマンドが実行されるか、プロンプト(F4 キー)が表示されるかした時に、↑のような内容の情報の取得と、指定しておいたプログラムの呼び出しを行うことができる、というわけです。

情報を受け取ってプログラムの中で何をするかは自由です。

たとえば、受けとった情報をログファイルに書き込むとかメッセージ待ち行列に出力する、などが考えられますよね。
追加でジョブ情報などをプログラム内で取得したりすれば、組み合わせてけっこういろんなことができるものです。
ユーザーや対象オブジェクトによってログしたり/しなかったりすることも可能になります。

特定のコマンドの実行をログ

今回は、ごくシンプルに DSPFD というコマンドをモニターし、実行されるとメッセージが上がる、という仕組みをつくってみました。

出口プログラムの登録は↓のコマンドで可能です。PGMDTA はモニターしたいコマンドによって変わります。

  ADDEXITPGM EXITPNT(QIBM_QCA_RTV_COMMAND) FORMAT(RTVC0100) PGMNBR(1)     
PGM(APITEST/QCARTVEXIT) PGMDTA(*JOB *CALC 'DSPFD QSYS ')

システム側で網を張っていますので、モニターできるのは DSPFD コマンドが直接入力された場合だけに限りません。
CL プログラムから実行されても、↓のようにメニューからオプションで実行されてもこの出口プログラムでモニターすることができるんですね。

デバッガで見てみると、メニューから実行された場合でも↓のようにコマンドの全文が載っているのがわかります。

特定のユーザーの場合のみをログするようにしています。

   /*  ADDEXITPGM EXITPNT(QIBM_QCA_RTV_COMMAND) FORMAT(RTVC0100) PGMNBR(1) +    
       PGM(APITEST/QCARTVEXIT) PGMDTA(*JOB *CALC 'DSPFD     QSYS      ')   */   
                                                                                
             PGM        PARM(&EXTINF)                                           
                                                                                
             /* 情報受け取り用変数とデータ構造*/                    
             DCL        VAR(&EXTINF) TYPE(*CHAR) LEN(2000)                      
             DCL        VAR(&EXTPOINT) TYPE(*CHAR) STG(*DEFINED) +              
                          LEN(20) DEFVAR(&EXTINF)                               
             DCL        VAR(&EXTFMT) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(8) DEFVAR(&EXTINF 21)                             
             DCL        VAR(&CMDNAME) TYPE(*CHAR) STG(*DEFINED) +               
                          LEN(10) DEFVAR(&EXTINF 29)                            
             DCL        VAR(&CMDLIB) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(10) DEFVAR(&EXTINF 39)                            
             DCL        VAR(&OFFSETB) TYPE(*UINT) STG(*DEFINED) +               
                          DEFVAR(&EXTINF 53)                                    
             DCL        VAR(&CMDLENB) TYPE(*UINT) STG(*DEFINED) +               
                          DEFVAR(&EXTINF 57)                                    
             DCL        VAR(&OFFSETRB) TYPE(*UINT) STG(*DEFINED) +              
                          DEFVAR(&EXTINF 53)                                    
             DCL        VAR(&CMDLENRB) TYPE(*UINT) STG(*DEFINED) +              
                          DEFVAR(&EXTINF 57)                                    
                                                                                
             /* 受け取りコマンド用変数*/                     
             DCL        VAR(&CMDSTR) TYPE(*CHAR) LEN(2000)                      
             DCL        VAR(&OFFSET) TYPE(*DEC) LEN(7 0)                        
             DCL        VAR(&CMDLEN) TYPE(*DEC) LEN(7 0)                        
                                                                                
             /* エラーハンドリング用変数*/                      
             DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)                          
             DCL        VAR(&MSGF) TYPE(*CHAR) LEN(10)                          
             DCL        VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&MSGDTA) TYPE(*CHAR) LEN(512)                       
             DCL        VAR(&ABENDING) TYPE(*LGL)                               
             DCL        VAR(&MSGKEY) TYPE(*CHAR) LEN(4)                         
             DCL        VAR(&MSGTYPE) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&RTNTYPE) TYPE(*CHAR) LEN(2)                        
             DCL        VAR(&PGMNAME) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&SENDER) TYPE(*CHAR) LEN(80)                        
                                                                                
             /* コマンド取替え条件用変数*/                       
             DCL        VAR(&JOBNAME) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&USER) TYPE(*CHAR) LEN(10)                          
                                                                                
             /* エラーキャッチ */                              
             MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))                 
                                                                                
             SNDPGMMSG  MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +                 
                          KEYVAR(&MSGKEY)                                       
             RCVMSG     PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +                  
                          SENDER(&SENDER)                                       
             CHGVAR     VAR(&PGMNAME) VALUE(%SST(&SENDER 56 10))                
                                                                                
             /* 使用コマンドの取り出し */                          
             IF         COND(&OFFSETRB = 0) THEN(DO)                            
             CHGVAR     VAR(&OFFSET) VALUE(&OFFSETB + 1)                        
             CHGVAR     VAR(&CMDLEN) VALUE(&CMDLENB)                            
             ENDDO                                                              
             ELSE       CMD(DO)                                                 
             CHGVAR     VAR(&OFFSET) VALUE(&OFFSETRB + 1)                       
             CHGVAR     VAR(&CMDLEN) VALUE(&CMDLENRB)                           
             ENDDO                                                              
                                                                                
             CHGVAR     VAR(&CMDSTR) VALUE(%SST(&EXTINF &OFFSET +               
                          &CMDLEN))                                             
                                                                                
             /* ログ条件とメッセージのセット*/                         
             RTVJOBA    JOB(&JOBNAME) USER(&USER)                               
             IF         COND(&USER = 'XXXXXX') THEN(DO)                       
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('コマンド' |< &CMDSTR |< +             
                          'がユーザー' |< &USER |< +                 
                          'によって、ジョブ' |< &JOBNAME |< +                
                          'で実行された。') TOMSGQ(QHST)               
             ENDDO                                                              
                                                                                
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +             
                          'は正常に終了した。') MSGTYPE(*COMP)                
             RETURN                                                             
                                                                                
 ERROR:      IF         COND(&ABENDING) THEN(RETURN)                            
             CHGVAR     VAR(&ABENDING) VALUE('1')                               
             RCVMSG     MSGTYPE(*LAST) MSGDTA(&MSGDTA) MSGID(&MSGID) +          
                          RTNTYPE(&RTNTYPE) MSGF(&MSGF) +                       
                          MSGFLIB(&MSGFLIB)                                     
             IF         COND((&RTNTYPE = '15') *OR (&RTNTYPE = +                
                          '17')) THEN(DO)                                       
             SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +                    
                          MSGDTA(&MSGDTA) MSGTYPE(*DIAG)                        
             ENDDO                                                              
 ESCAPE:     SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +           
                          'は異常終了した。') MSGTYPE(*ESCAPE)               
                                                                                
             ENDPGM    

実行

DSPLOG コマンドで見てみると、こんなかんじでログされていることが確認できました。


特定のコマンドの実行を阻止する

モニターするだけではなく、たとえばそのコマンドの実行を阻止したい場合もありますね。

やはりこちらも同様に出口点を使って、あるコマンドが実行された場合にその機会をとらえて別のコマンドに取り替えてしまう、ということができるようになっています。

出口点は QIBM_QCA_CHG_COMMAND といって、ここで待ち伏せをしていると CHGC0100 というフォーマットに則ってプログラムに情報が提供される、という構造になっています。

CHGC0100 Format

The following table shows the format of the information supplied to a change command exit program. For a description of the fields in this format, see Field Descriptions.
Offset Type Field
Dec Hex
0 0 CHAR(20) Exit point name
20 14 CHAR(8) Exit point format name
28 1C CHAR(10) Command name
38 26 CHAR(10) Library name
48 30 CHAR(1) Change allowed indicator
49 31 CHAR(1) Prompt indicator
50 32 CHAR(2) Reserved
52 34 BINARY(4) Offset to command string
56 38 BINARY(4) Length of command string
60 3C BINARY(4) Offset to proxy chain
64 41 BINARY(4) Number of entries in proxy chain
    CHAR(*) Command string
Proxy commands and libraries. These fields repeat in the order listed. CHAR(10) Proxy command name
CHAR(10) Proxy command library name

↓のコマンドで登録します。PGMDTA はモニターしたいコマンドによって変わります。

  ADDEXITPGM EXITPNT(QIBM_QCA_CHG_COMMAND) FORMAT(CHGC0100) PGMNBR(1) 
  PGM(APITEST/QCACHGEXIT) PGMDTA(*JOB *CALC 'DSPPFM QSYS ')

↓を組み合わせることでパラメータを変更不可にしたりすることができます。
http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/topic/rbam6/secpr.htm

     /*  ADDEXITPGM EXITPNT(QIBM_QCA_CHG_COMMAND) FORMAT(CHGC0100) PGMNBR(1) +  
      *  PGM(APITEST/QCACHGEXIT) PGMDTA(*JOB *CALC 'DSPPFM    QSYS      ')   */    
                                                                                
             PGM        PARM(&EXTINF &NEWSTR &NEWLEN)                           
                                                                                
             /* 情報受け取り用変数とデータ構造*/                                               
             DCL        VAR(&EXTINF) TYPE(*CHAR) LEN(2000)                      
             DCL        VAR(&EXTPOINT) TYPE(*CHAR) STG(*DEFINED) +              
                          LEN(20) DEFVAR(&EXTINF)                               
             DCL        VAR(&EXTFMT) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(8) DEFVAR(&EXTINF 21)                             
             DCL        VAR(&CMDNAME) TYPE(*CHAR) STG(*DEFINED) +               
                          LEN(10) DEFVAR(&EXTINF 29)                            
             DCL        VAR(&CMDLIB) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(10) DEFVAR(&EXTINF 39)                            
             DCL        VAR(&CHG) TYPE(*CHAR) STG(*DEFINED) LEN(1) +            
                          DEFVAR(&EXTINF 49)                                    
             DCL        VAR(&PROMPT) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(1) DEFVAR(&EXTINF 50)                             
             DCL        VAR(&FILLER) TYPE(*CHAR) STG(*DEFINED) +                
                          LEN(2) DEFVAR(&EXTINF 51)                             
             DCL        VAR(&OFFSETB) TYPE(*UINT) STG(*DEFINED) +               
                          DEFVAR(&EXTINF 53)                                    
             DCL        VAR(&CMDLENB) TYPE(*UINT) STG(*DEFINED) +               
                          DEFVAR(&EXTINF 57)                                    
                                                                                
             /* 受け取りコマンド用変数*/                                                   
             DCL        VAR(&CMDSTR) TYPE(*CHAR) LEN(2000)                      
             DCL        VAR(&OFFSET) TYPE(*DEC) LEN(7 0)                        
             DCL        VAR(&CMDLEN) TYPE(*DEC) LEN(7 0)                        
                                                                                
             /* 取替えコマンド用変数*/                                                    
             DCL        VAR(&NEWSTR) TYPE(*CHAR) LEN(2000)                      
             DCL        VAR(&NEWLEN) TYPE(*UINT)                                
                                                                                
             /* エラーハンドリング用変数*/                                                  
             DCL        VAR(&MSGID) TYPE(*CHAR) LEN(7)                          
             DCL        VAR(&MSGF) TYPE(*CHAR) LEN(10)                          
             DCL        VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&MSGDTA) TYPE(*CHAR) LEN(512)                       
             DCL        VAR(&ABENDING) TYPE(*LGL)                               
             DCL        VAR(&MSGKEY) TYPE(*CHAR) LEN(4)                         
             DCL        VAR(&MSGTYPE) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&RTNTYPE) TYPE(*CHAR) LEN(2)                        
             DCL        VAR(&PGMNAME) TYPE(*CHAR) LEN(10)                       
             DCL        VAR(&SENDER) TYPE(*CHAR) LEN(80)                        
                                                                                
             /* コマンド取替え条件用変数*/                                                  
             DCL        VAR(&USER) TYPE(*CHAR) LEN(10)                          
                                                                                
             /* エラーキャッチ */                                                      
             MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))                 
                                                                                
             SNDPGMMSG  MSG(' ') TOPGMQ(*SAME) MSGTYPE(*INFO) +                 
                          KEYVAR(&MSGKEY)                                       
             RCVMSG     PGMQ(*SAME) MSGTYPE(*INFO) RMV(*YES) +                  
                          SENDER(&SENDER)                                       
             CHGVAR     VAR(&PGMNAME) VALUE(%SST(&SENDER 56 10))                
                                                                                
                                                                                
             /* 使用コマンドの取り出し */                                                  
             CHGVAR     VAR(&OFFSET) VALUE(&OFFSETB + 1)                        
             CHGVAR     VAR(&CMDLEN) VALUE(&CMDLENB)                            
             CHGVAR     VAR(&CMDSTR) VALUE(%SST(&EXTINF &OFFSET +               
                          &CMDLEN))                                             
                                                                                
             /* コマンド取替え条件と取替えコマンドのセット*/                                         
             RTVJOBA    USER(&USER)                                             
         /*  IF         COND(&USER = 'XXXXXX') THEN(DO)  */                     
             IF         COND(%SST(&CMDSTR 13 17) = +                            
                          'SAMPLE/DEPARTMENT') THEN(DO)                         
             CHGVAR     VAR(&NEWSTR) VALUE('ENDJOB ?*JOB(*) ??OPTION(*IMMED)')      
             CHGVAR     VAR(&NEWLEN) VALUE(32)                                  
             ENDDO                                                              
                                                                                
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +                       
                          'は正常に終了した。') MSGTYPE(*COMP)                           
             RETURN                                                             
                                                                                
 ERROR:      IF         COND(&ABENDING) THEN(RETURN)                            
             CHGVAR     VAR(&ABENDING) VALUE('1')                               
             RCVMSG     MSGTYPE(*LAST) MSGDTA(&MSGDTA) MSGID(&MSGID) +          
                          RTNTYPE(&RTNTYPE) MSGF(&MSGF) +                       
                          MSGFLIB(&MSGFLIB)                                     
             IF         COND((&RTNTYPE = '15') *OR (&RTNTYPE = +                
                          '17')) THEN(DO)                                       
             SNDPGMMSG  MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +                    
                          MSGDTA(&MSGDTA) MSGTYPE(*DIAG)                        
             ENDDO                                                              
 ESCAPE:     SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +                          
                          MSGDTA('プログラム' || &PGMNAME |< +                       
                          'は異常終了した。') MSGTYPE(*ESCAPE)                          
                                                                                
             ENDPGM                                                             

DSPPFM コマンドが SAMPLE/DEPARTMENT テーブルについて実行された場合、

ENDJOB コマンドで強制的にそのコマンドを実行したジョブを終了させる、ということを行うようにしてみました。

F4 キーを押すと、ジョブの指定を自ジョブ(*)以外には変えられないようになった状態でプロンプトが表示されるようにしています。

RPG 版

RPG でも書いてみました。

こちらでももちろんパラメータの変更不可化は可能です。

こちらでは新たな実験として、プロンプトで呼ばれたか実行キーで呼ばれたかで挙動を変えてみました。

Parm.ECAPCMD がプロンプトで呼び出されたかどうかを示すフラグになっています。
Parm.ECAPCMD = '0'はプロンプトされずに実行された場合で↑と同様にそのまま ENDJOB されてしまうようになっています。
Parm.ECAPCMD = '1'はプロンプトされた場合で、適当なコマンドに置き換えてしまって実行できないようになっています。

      *  ADDEXITPGM EXITPNT(QIBM_QCA_CHG_COMMAND) FORMAT(CHGC0100) PGMNBR(1)                        
      *  PGM(APITEST/QCACHGEXIT)  PGMDTA(*JOB *CALC 'DSPPFM    QSYS      ')                           
                                                                                                    
     H DFTACTGRP(*NO)                                                                               
      *                                                                                             
     D QCACHGEXIT      PR                                                                           
     D  Parm                                                                                        
     D                                     options(*varsize)                                        
     D                                     likeds(ECAC0100)                                         
     D  rplcmd                     1024a   options(*varsize)                                        
     D  len                          10i 0                                                          
      *                                                                                             
     D QCACHGEXIT      PI                                                                           
     D  Parm                                                                                        
     D                                     options(*varsize)                                        
     D                                     likeds(ECAC0100)                                         
     D  rplcmd                     1024a   options(*varsize)                                        
     D  len                          10i 0                                                          
      *                                                                                             
      /copy qsysinc/qrpglesrc,ecachcmd                                                              
      *                                                                                             
     D PSDS           SDS                  Qualified                                                
     D   jobUsr                      10a   OVERLAY(PSDS:254)                                        
     D   crrUsr                      10a   OVERLAY(PSDS:358)                                        
      *                                                                                             
     D msg             S             52a                                                            
     D pcmdstr         S               *                                                            
     D cmdstr          S           1024a   based(pcmdstr)                                           
      *                                                                                             
     D RUNCMD          PR                  EXTPGM('QCMDEXC')                                        
     D  cmd                        1024a   CONST                                                    
     D                                     Options(*varsize)                                        
     D  len                          15p 5 CONST                                                    
      *                                                                                             
      /free                                                                                         
          *inLR = *on ;                                                                             
                                                                                                    
          pcmdstr =  %addr(Parm) + Parm.ECACMDSO ;                                                  
                                                                                                    
          if %trimR(%subst(cmdstr : 13 : 17)) = 'SAMPLE/DEPARTMENT' ;                               
             if Parm.ECAPCMD = '0' ;                                                                
                rplcmd = 'ENDJOB JOB(*) OPTION(*IMMED)' ;                                           
             // Prompting only                                                                      
             else ;                                                                                 
                rplcmd = 'XXXXX' ;                                                                  
             endif ;                                                                                
                len = %len(rplcmd) ;                                                                
          endif ;                                                                                   
                                                                                                    
          return ;                                                                                  
                                                                                                    
      /end-free                                             

[Top Pageに戻る]

Ads by TOK2