Comment faire afficher un écran à un traitement batch ?

Les traitements 5250 sont faits pour tourner en interactif, mais pour différentes raisons vous pouvez vouloir les faire tourner en batch :

Pas de login
sécurisation pas de attn request
etc …

Voici un exemple, dans une société ou j’ai travaillé, les unités de productions pouvaient demander un mot de passe à l’astreinte, par exemple la nuit.

La solution la plus simple qu’on avait trouvée était de lui afficher un programme de changement sur son unité écran.

On lui demandait donc son profil utilisateur et son unité écran et on lançait le programme sur celle ci, il pouvait changer son mot de passe directement.

C’est cet exemple que j’ai simplifié qui est ci dessous

le DSPF source

                                                                 
     A                                      DSPSIZ(24 80 *DS3)             
     A                                      CA03(03)                       
     A                                      INDARA                         
     A          R FMT01                                                    
     A*%%TS  SD  20240527  074541  PLB         REL-V7R4M0  5770-WDS        
     A                                  4 20'Réinitialiser un mot de passe'
     A                                  7  2'Utilisateur         :'        
     A                                  8  2'Nouveau mot de passe  :'        
     A                                 22  1'F3=Exit'                      
     A            USER          10A  O  7 26                               
     A            PWD           32A  B  8 26CHECK(LC)                      
     A            DEV           10A  O  4 54                               
     A            TEXTE         30   O 14  3                               

Le CLP source

pgm  (&dev &user)                                                
/*----------------------------------------------------------*/   
/* Ce programme permet de faire afficher un écran en batch  */   
/* vous devez acquérir l'unité écran                         */   
/* pour réinitialiser un mot de passe                        */   
/* sbmjob cmd(call initpwdr (&dev &user) job(QPADEV0001)    */   
/*----------------------------------------------------------*/   
dcl &dev  *char 10                                               
dcl &user *char 10                                               
/* Contrôles existence                      */                   
             CHKOBJ     OBJ(&DEV) OBJTYPE(*DEVD)                 
             MONMSG     MSGID(CPF9801) EXEC(DO)                  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +           
                          MSGDTA('L''unité écran doit exister') +
                          MSGTYPE(*ESCAPE)                       
             enddo 
            ALCOBJ     OBJ((&DEV *DEVD *EXCLRD)) WAIT(0)       
            monmsg CPF1002  exec(do)                           
            SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +          
                         MSGDTA('Impossible d''allouer l''unité +
                         écran doit exister') MSGTYPE(*ESCAPE) 
            enddo                                                                                            
             CHKOBJ     OBJ(&user) OBJTYPE(*usrprf)              
             MONMSG     MSGID(CPF9801) EXEC(DO)                  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Le +
                          profil utilisateur doit exister') +         
                          MSGTYPE(*ESCAPE)                            
             enddo                                                    
 /* Lancement du programme sur l'écran remote  */                      
             SBMJOB     CMD(CALL PGM(INITPWDR) PARM((&DEV) (&USER))) +
                          JOB(&DEV) JOBQ(QSYSNOMAX)  
             SNDPGMMSG  MSGID(CPF9898) MSGF(QCPFMSG) +          
                          MSGDTA('Travail, ' *bcat &dev *bcat + 
                          'démarré') MSGTYPE(*comp)                              
endpgm                                                                

Le CLP cible

pgm  (&dev &user)                                                
/*----------------------------------------------------------*/   
/* Ce programme réinitiliase  un mot  de passe              */   
/* Peut être envoyé sur un écran remote qui est sur la      */   
/* Mire                                                     */   
/*----------------------------------------------------------*/   
dcl &dev *char 10                                                
dclf initpwdr                                                    
monmsg cpf0000 exec(goto erreur)                                 
/* controles fait dans le programme appelant */                  
             OVRDSPF    FILE(INITPWDR) DEV(&DEV) OVRSCOPE(*JOB)  
          chgvar &pwd     'ici mot de passe'                     
             dountil &in03                                       
             SNDRCVF    RCDFMT(FMT01)                            
             if cond(*not &in03) then(do)                        
             if (&pwd *ne 'ici mot de passe') then(do)           
             CHGUSRPRF  USRPRF(&USER) PASSWORD(&PWD) PWDEXP(*YES)
             Chgvar &texte ('Mot de passe réinitialisé')         
             enddo                                               
             enddo                                                   
             enddo                                                   
/* fin traitement */                                                 
             DLTOVR     FILE(INITPWD) LVL(*JOB)                      
             MONMSG     MSGID(CPF0000)                               
return                                                               
erreur:                                                              
             SNDUSRMSG  MSG('Réinitialisation de mot de passe pour' +
                          *BCAT &USER *BCAT ', actuellement +        
                          impossible') MSGTYPE(*INFO)                
             MONMSG     MSGID(CPF0000)                               
endpgm                                                               

La seule spécificité est de choisir l’unité écran d’affichage, pour qu’elle soit éligible elle doit être à la mire de connexion.

La commande pour habiller

             CMD        PROMPT('Réinitialisation de PWD')           
             PARM       KWD(UNITE) TYPE(*NAME) LEN(10) MIN(1) +     
                          PROMPT('Unité écran')                     
             PARM       KWD(PROFIL) TYPE(*NAME) LEN(10) MIN(1) +    
                          PROMPT('Profil utilisateur')              

le lancement job source:

La saisie sur le job cible :

Sur un changement de mot de passe, il peut être important d’avoir un suivi ?

Bien sur, vous pouvez ajouter des contrôles …

Le job cible qui tourne en batch !

Donc pas de possibilité d’ATTN Request , et messages d’erreurs dans QSYSOPR

Remarques :

Vous pouvez choisir l’utilisateur de lancement, mais QSECOFR est interdit dans un SBMJOB
Simple mais efficace, mais difficilement généralisable, on doit avoir un OVRDBF par DSPF

Intéressant pour des demandes ponctuelles

Dans les CLP vous pouvez indiquer directement l’unité sur la commande SNDRCVF

, Mettre un délai sur un programme 5250

Vous avez tous des enregistrements qui restent verrouillés dans vos traitements 5250, par exemple parce que la personne à quitter son poste.

Vous voulez donc qu’au bout d’un certain temps, votre programme déverrouille l’enregistrement

Vous avez une première solution qui est système

Elle se base sur les valeurs systèmes

QINACTITV qui indique un délai en seconde
QINACTMSGQ ou vous indiquez:
Une MSGQ, un message CPI1126 sera envoyé, à vous de mettre en place un watcher qui analyse et traite ces messages
*DSCJOB votre travail sera déconnecté et le système tentera de vous reconnecter (attention ca ne déverrouille pas toutes les ressources)
*ENDJOB votre travail sera arrêté, et donc tous sera libéré avec risques de déphasage dans votre base de données.

Mais vous pouvez être plus précis en le prévoyant dans les développements que vous ciblerez comme étant sensible, voici un exemple ci-dessous .

Notre exemple commenté

Dans le DSPF, vous devrez compiler votre écran avec un paramètre WAITRCD(délai en secondes), passer ce délai le système quittera le READ en attente.

Vous pouvez également indiquer ce paramètre temporairement par la commande OVRDSPF

Dans le programme RPGLE

Vous devrez indiquer l’option maxdev(*file) sur votre écran (c’est celle qui vous permet d’avoir plusieurs travails sur le même écran, pour faire une bataille navale par exemple)

Dans votre traitement vous ne pouvez pas utiliser l’instruction EXFMT (qui est un condensé d’un write et d’un read)

Vous devez explicitement indiquer les 2 instructions

WRITE du format ;
READ du fichier ;

Pour tester et savoir si vous avez dépassé le delai, vous pouvez tester la fonction %status qui vous renverra la valeur 01331 si vous avez débordé, vous pouvez également utiliser L’infds de votre écran.

Voici un exemple ou on a mis que ce qui est indispensable, à vous d’ajouter ce qui sera nécessaire à votre cas .

L’écran DSPF

A* OPTIONS DE COMPILE pour GDDS -------------------->          
A*<COMP>WAITRCD(60)</COMP>                                      
A* ------------------------------------------------->          
A*                                                         
A                                      DSPSIZ(24 80 *DS3)      
A                                      CA03(03)                
A                                      INDARA                  
A          R FMT01                                             
A* 
A                                      INVITE                  
A                                  5  3'Votre format de saisie'
A                                  7 12'Zone 1 :'              
A            ZONE1          4   B  7 23                        
A                                 22  2'F3=Exit'               

Dans notre exemple nous utilisons GDDS qui permet de mémoriser les options de compile pour les DSPF chez vous, vous devrez le mettre dans la commande de création de votre écran

==>CRTDSPF … WAITRCD(60) …

Le RPGLE

**free                                                    
// Ecran compiler avec WAITRCD(60)                          
dcl-f TESTDSPF workstn                                    
 maxdev(*file) ;                                          
 //                                                       
   dou     *in03        ;                                 
  // remplace un EXFMT insensible au Paramètre WAITRCD    
       write FMT01   ;                                    
       read(e) TESTDSPF ;                                 
  // 11331 indique que le temps est dépassé               
  select  ;                                               
  when  (%status  = 01331) ;                              
       dsply  'temps dépassé ...' ;                       
       *in03  = *on ;                                     
  // F3 activé                                            
  when *in03   ;                                          
       dsply  'sortie demandée .' ;                       
  // Touche entrée avec  lecture des zones                                      
  other    ;                                              
       dsply  'validation ok ...' ;
  endsl    ;                       
 enddo  ;                          
//                                 
*inlr = *on ;

Conclusion :

C’est simple et efficace et certains traitements devraient avoir cette option

Vous pouvez aussi utiliser facilement cette technique pour faire du rafraichissement auto, par exemple un tableau de bord toutes les 3 minutes …

Voir également cet exemple en CLP dans un article précédent ici : https://www.gaia.fr/reaffichage-automatique-dun-dspf/

, , Gestion du suivi des tâches soumises

Une petite amélioration sur le clear avec la TR4

Je vous rappelle le principe de cette fonction qui existe depuis la TR1

C’est un job à démarrage automatique qui tourne dans QUSRWRK qui va superviser

QSYSWRK QSYS SBS 0,0 DEQW
QMRDBJNFY QSECOFR ASJ 0,0 PGM-QMRDBJNFY DEQW

Pour le lancer

QSYS/CALL QSYS/QMRDBJNFY PARM(0)

Ajouter une jobq à surveiller

CALL QSYS2.ADD_TRACKED_JOB_QUEUE(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY => ‘majobq’,
JOB_QUEUE => ‘malib’);

Supprimer une jobq à surveiller

CALL QSYS2.REMOVE_TRAcked_job_QUEUE(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY => ‘majobq’,
JOB_QUEUE => ‘malib’)


pour voir les jobq contrôlées

SELECT * FROM QSYS2.TRACKED_JOB_QUEUES

c’est le fichier QSYS/QAMRDJQL il peut être copié d’une machine à une autre !

Soumission d’un travail dans la file

SBMJOB CMD(DSPLIB LIB(QGPL)) JOB(DSPQGPL) JOBQ(GG/GGTEST)

voir le résultat

SELECT * FROM TABLE(QSYS2.TRACKED_JOB_INFO(JOB_QUEUE_LIBRARY_FILTER => ‘GG’,
JOB_QUEUE_FILTER => ‘GGTEST’))
ORDER BY INTERNAL_JOB_IDENTIFIER, ROUTING_STEP;

sortie

NODE_NAME QUALI00001 JOB_NAME JOB_USER JOB_NUMBER COMMA00001
NEPTUNE 740023/PLB/DSPQGPL DSPQGPL PLB 740023 DSPLIB LIB(QGPL)

pour clearer les logs
CALL QSYS2.CLEAR_TRACKED_JOB_INFO(IASP_NAME => ‘*SYSBAS’);

Vous pouvez avoir plus d’informations dans le fichier

Avec la TR4 vous avez des filtres supplémentaires par exemple jobq et bibliothèque

Pour clearer une jobq spécifique !

CALL QSYS2.CLEAR_TRACKED_JOB_INFO(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY_FILTER => ‘GG’,
JOB_QUEUE_FILTER => ‘GGTEST’);

des liens à connaitre

https://www.ibm.com/docs/en/i/7.4?topic=environment-managing-submitted-job-tracker

https://www.ibm.com/support/pages/submitted-job-tracker-job-qmrdbjnfy

Remarque :

il definit des programmes d’exit que vous pouvez utiliser
Change Job (QIBM_QWT_CHGJOB)
Job Notification (QIBM_QWT_JOBNOTIFY)
Submit Job (QIBM_QWT_SBMJOB)

Rien dans Navigator for i Mais gros liens avec DB2MIRROR et son interface !

, , Manager les informations d’audit avec la TR4

Vous pouvez demander un tas d’informations d’audit pour tracer les violations de sécurité par exemple

Vous devez avoir le droit *AUDIT sur votre profil pour la mettre en place, c’est valeurs système qui permettent de le paramétrer les informations souhaitées

QAUDCTL, QAUDLVL, QAUDLVL2

Pour chaque information demandée, vous allez avoir un poste qui est créé dans le journal d’audit QAUDJRN.

Pour consolider ces informations, vous devez les copiez dans des fichiers il existait jusqu’à présent 2 méthodes

1) Les fichiers modèles utilisables dans des commandes IBMi

ils sont dans QSYS et commencent par QASY

DSPJRN QAUDJRN OUTPUT(*OUTFILE)
OUTFILE(MABIB/QASYPWJ5)
JRNCODE(‘T’) ENTTYP(‘PW’)

exemple pour les types PW, c’est le fichier QASYPWJ5 que vous devez dupliquer dans votre bibliothèque
DSPJRN JRN(QAUDJRN) ENTTYP(‘PW’)
OUTPUT(OUTFILE) OUTFILFMT(TYPE5) OUTFILE(MABIB/WASYPWJ5)

La stratégie est la suivante , la première fois vous demandez la création du fichier de sortie par un crtdupobj
et après vous ajoutez à ce fichier tous les soirs à 23 h 59 les postes PW

2) Les Technology Refresh nous apportent régulièrement des services

Ces services permettent de lire directement ces informations par SQL.
c’est les fonctions tables qui se trouve dans SYSTOOLS et qui s’écrivent SYSTOOLS.AUDIT_JOURNAL_XX()
XX étant le type
exemple pour les types PW
SYSTOOLS.AUDIT_JOURNAL_PW()

voici un exemple

select A.VIOLATION_TYPE_DETAIL, A.AUDIT_USER_NAME , A.REMOTE_ADDRESS, A.ENTRY_TIMESTAMP
from table (SYSTOOLS.AUDIT_JOURNAL_PW(STARTING_TIMESTAMP => current timestamp – 1 days)) A
order by A.ENTRY_TIMESTAMP desc

La stratégie est la suivante , la première fois vous demandez la création du fichier de sortie par un create table as()
et après vous ajoutez à ce fichier tous les soirs à 23 h 59 les postes PW par un insert

3) Avec la TR (4 pour V7R5 et 10 pour V7R4)

elle vous apporte une solution intégrée pour créer votre DATAMART
https://www.ibm.com/support/pages/ibm-i-75-tr4-enhancements
Vous avez 2 services

QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART procedure
QSYS2.AUDIT_JOURNAL_DATA_MART_INFO view

https://www.ibm.com/support/pages/node/7148888

ces 2 services vont faire ce que vous faisiez par SQL ou commande ibmi 2

Création du datamart le fichier résultat s’appelera AUDIT_JOURNAL_XX dans la bibliothèque que vous aurez choisi

Création de la table

CALL QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART(JOURNAL_ENTRY_TYPE => ‘PW’,
DATA_MART_LIBRARY => ‘MALIB’,
STARTING_TIMESTAMP => CURRENT DATE – 1 MONTH,
ENDING_TIMESTAMP => CURRENT TIMESTAMP);

Alimentation quotidienne de la table

CALL QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART(JOURNAL_ENTRY_TYPE => ‘PW’,
DATA_MART_LIBRARY => ‘MALIB’,
STARTING_TIMESTAMP => ‘*CONTINUE’,
ENDING_TIMESTAMP => CURRENT TIMESTAMP,
DATA_MART_ACTION => ‘ADD’
);

Vous avez une vue qui vous permet de suivre vos mises à jour de datamart

SELECT DATA_MART_LIBRARY, DATA_MART_TABLE, JOURNAL_ENTRY_TYPE, BUILD_END, FAILURE_DETAIL
FROM QSYS2.AUDIT_JOURNAL_DATA_MART_INFO
WHERE JOURNAL_ENTRY_TYPE = ‘PW’ AND DATA_MART_LIBRARY = ‘MALIB’ ;

Vous pourrez également manager par Navigator for i

Remarque :

Bien sur vous avez toujours aussi un onglet dans Navigator for i qui vous permet de voir sous forme de graphique les résultats , mais attention uniquement sur les postes en cours .
Si vous avez déjà une solution, c’est compliqué de revenir dessus, mais si vous n’avez rien ca vous simplifier considérablement les taches.
Attention dans tous les cas c’est à vous d’épurer les récepteurs quand vous les avez traité

, Rechercher dans l’IFS avec OMNIFIND

Vous trouvez que la recherche de fichier dans l’IFS par les services SQL n’est pas assez performante ;
Par exemple vous gérez des archives PDF sur l’IFS (toujours penser à épurer régulièrement).

Vous avez un logiciel gratuit qui s’appelle Omnifind, celui-ci permet de gérer des index textes sur l’IFS
mais aussi sur les spools si vous le désirez.

Notre besoin était sur l’IFS nous allons donc étudier ce cas au cours de cet article.

Nous avons déjà posté un autre article sur la mise en œuvre de Omnifind

Vous pouvez en savoir plus ici

https://www.ibm.com/support/pages/omnifind-ibm-i

Une fois que vous avez mis en place Omnifind et créé l’index qui va bien.

Vous retrouvez des procédures dans la collection que vous avez créée, chez nous elle s’appelle OMNIFIND.

Dans ces procédures vous en avez une qui s’appelle SEARCH qui va scanner dans vos fichiers
ceux qui contiennent une chaine de caractères.

Cette procédure renvoie un resultset ; Ce qui explique qu’en retour dans une interface ACS vous avez une liste et dans
un STRSQL juste un message de fin.

La problématique est donc de savoir comment traiter un resultset dans un programme avec, en plus, un résultat renvoyé dans un CLOB au format XML

Nous avons choisi de faire cet exercice en RPGLE.

Je vous joins un exemple ici, vous pouvez l’améliorer par exemple pour le parsage XML, ou la gestion des variables :

**free
//
// Options de compile
//
ctl-opt
   DFTACTGRP(*NO) ;
//
// Paramètre chaine à scanner
//
Dcl-Pi *N;
  p_texte char(30) ;
End-Pi;
//
// Initialisation des options de compile SQL
//
EXEC SQL
        Set Option
          Naming    = *SQL,
          Commit    = *None,
          UsrPrf    = *User,
          DynUsrPrf = *User,
          Datfmt    = *iso,
          CloSqlCsr = *EndMod;
// Zone du result set  pour la procedure SEARCH
dcl-s objtype char(10) ;
dcl-s objattr char(10) ;
dcl-s objlib  char(10) ;  // peut être nul int1
dcl-s objname char(10) ;  // peut être nul int2
dcl-s objinfo SQLTYPE(CLOB:65531)  ;
dcl-s objmod  TIMESTAMP  ;
dcl-s objtime int(5)     ;
// Déclaration de  l'adresse du result set
dcl-s RS1 SQLTYPE(RESULT_SET_LOCATOR) ;
// pour les variables à valeurs nulles
dcl-s Ind1 int(5)     ;
dcl-s Ind2 int(5)     ;
// Variables de travail
Dcl-S w_objinfo    Varchar(65531);
//
// Contrôle des paramètres
//
if %parms  = 0 ;
  dsply ('Vous devez saisir une recherche')  ;
  return ;
  *inlr = *on ;
endif;
// Appel de la procédure ici de la collection OMNIFIND
// la procédure est dans la collection qui contient l'index TXT
exec sql
   Call OMNIFIND.SEARCH(:p_texte);
// Si result set créé
if SQLCODE = +466;

// Vous associez le resultset de la procédure à votre curseur 
  exec sql ASSOCIATE LOCATORS (:RS1) WITH PROCEDURE OMNIFIND.SEARCH ;
  exec sql ALLOCATE C1 CURSOR FOR RESULT SET :RS1;
  // boucle de traitement du curseur associé
  exec sql fetch c1 into :objtype   ,
                         :objattr   ,
                         :objlib:ind1 ,
                         :objname:ind2 ,
                         :objinfo   ,
                         :objmod    ,
                         :objtime   ;
  //  ici traitement du fichier
  //     récupération des données du CLOB
  Traitement() ;
  dow sqlcode = 0;
    // traitement des variables lues...
    exec sql fetch c1 into :objtype   ,
                           :objattr   ,
                           :objlib:ind1,
                           :objname:ind2,
                           :objinfo   ,
                           :objmod    ,
                           :objtime   ;
    //     récupération des données du CLOB
    Traitement() ;
  //  ici traitement du fichier
  //
  ENDDO;
  exec sql close C1;
ENDIF;
*inlr = *on;
//
//  Procédure de traitement
//
dcl-proc Traitement ;
  dcl-s w_file    char(256);
  dcl-s pos1      int(5) ;
  dcl-s pos2      int(5) ;
  // dcl-s w_text    char(50);
  w_objinfo = %subst(objinfo_data : 1 : objinfo_len) ;
  //  traiter XML ca serait mieux
  // recherche <file_path>
  pos1 = %scan('<file_path>' : w_objinfo ) ;
  // recherche </file_path>
  pos2 = %scan('</file_path>' : w_objinfo ) ;
  w_file = %subst(w_objinfo : (pos1 + 11) : (pos2 - (pos1 + 11))) ;
//
// Traitement du fichier  w_file
//
//w_text = %subst(w_file   : 1 : 50) ;
//dsply w_text ;
end-proc ; 


Les points importants à comprendre :

C’est la déclaration et l’utilisation d’un curseur basé sur un resultset – j’ai mis un commentaire dans le source.

Et la manipulation d’une variable CLOB.

Voila les points principaux à retenir :

Quand vous déclarez une dans votre programme RPGLE votre variable CLOB,

DCL-S XXX SQLTYPE(CLOB:nnnn);

En réalité, après traduction c’est déclaré comme ça :

DCL-DS XXX;
XXX_LEN UNS(10);
XXX_DATA CHAR(nnnn) CCSID(*JOBRUNMIX);
END-DS XXX;

nnnn étant la longueur de la zone
XXX_LEN contient la longueur du contenu de la zone
XXX_DATA contient la donnée

Dans la partie traitement vous pouvez facilement faire un QCMDEXC en SQL, par exemple pour faire une copie, un envoi ou une suppression

Voila un exemple concret que vous pouvez adapter, n’hésitez pas à tester Omnifind car il est gratuit et comme on met de plus en plus de chose dans l’IFS, il est de plus en plus utile !

, , , Convertir un SAVF en PF

il faut savoir qu’un fichier SAVF est un fichier PF de 528 de long.

Vous pouvez avoir besoin pour différentes raisons de convertir SAVF en PF pour manipuler plus simplement par SQL, pour contourner des restrictions d’utilisations etc ..

Vous trouverez ici 2 commandes qui vont vous permettent de réaliser cette opération : https://github.com/Plberthoin/PLB/tree/master/CPYSAVF

Copier un SAVF dans un PF

Voir le PF

Copier un PF dans un SAVF

Voir le Résultat

Pour voir ce résultat taper la commande DSPSAVF , Voir WRKSAVF si vous avez installez notre outil

Remarque :

Outil simple mais efficace qui peut rendre des services à des administrateurs