Conversion RPG FREE

Il existe de nombreuses méthodes pour convertir du code RPGLE colonné vers du RPG FREE.

La plupart des conversions se passent sans problème, mais on constate que le code RPG ou RPG IV colonné pouvait être plus permissif et permettre des choses que le FREE ne tolère pas.

Voici un exemple qu’on a rencontré récemment, on est d’accord il résulte d’une incohérence dans le développement initial,
mais jusque la ça passait à l’exécution.

En RPGIV colonné

c z-add 9999999 zone7 7 0
c z-add zone7 zone5 5 0
c eval *inlr = *on

Quand vous exécutez ce code, il n’y a pas d’erreur, on est d’accord le résultat est faux …

En RPGIV FREE

**free
Dcl-S zone5 Zoned(5:0);
Dcl-S zone7 Zoned(7:0);
ZONE7 = 9999999 ;
ZONE5 = ZONE7 ;
*inlr = *on ;

Quand vous exécutez ce code, il y a un plantage, ce qui est normal

Conclusion

On peut donc tomber sur quelques cas que le RPG III ou IV acceptait mais que le nouveau code ne permet plus.
C’est bien sûr du code de mauvaise qualité, et la réécriture peut s’avérer nécessaire surtout si on rencontre des instructions déstructurantes comme des GOTO par exemple.

, Utilisation du MD5 sur votre IBM i

Bien que le MD5 ne soit plus utilisé pour l’encryption, il est toujours utilisé pour valider l’authenticité et la conformité des fichiers.

Qu’est-ce qu’un MD5

Un md5 est une chaine de 16 caractères composée de symboles hexadécimaux. Il s’agit en réalité du nom de l’algorithme utilisé pour générer la chaine.

Comme indiqué précédemment son usage est le contrôle d’intégrité des fichier, par exemple lors du partage d’un fichier, on peut mettre à disposition le MD5 afin de contrôler que le téléchargement du fichier s’est bien passé ou que le fichier n’a pas été modifié entre temps.

Pour la suite nous aurons besoin d’un fichier, par simplicité j’utiliserai un simple .txt qui contient la phrase « This is not a test! » présent dans mon répertoire de l’ifs.

Fichier dans l’ifs/home/jl/file.txt
Contenu du fichierThis is not a test!
md5EDA20FB86FE23401A5671734E4E55A12

QSH – md5sum

La première méthode pour générer le MD5 d’un fichier est d’utiliser la commande unix md5sum via QSH :

$ /QOpenSys/pkgs/bin/md5sum /home/jl/file.txt
eda20fb86fe23401a5671734e4e55a12  /home/jl/md5.txt

La fonction retourne le hash et le chemin du fichier.

RPGLE – _cipher

Il est également possible de générer le MD5 via RPG en exploitant la procédure externe cipher.
Je ne m’épancherai pas sur son implémentation complète ici, car bien plus complexe que les deux autres méthodes présentées. De plus, passer par cette méthode, n’est plus le sens de l’histoire.

// Déclaration de la procédure
dcl-pr cipher extproc('_cipher');
	*n pointer value;
	*n pointer value;
	*n pointer value;
end-pr;

// Appel de la procédure
cipher(%ADDR(receiver) : %ADDR(controls) : %ADDR(source));

Lien vers la documentation IBM pour plus d’informations :
https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/rzatk/CIPHER.htm

SQL – hash_md5

En sql on retrouve la fonction hash_md5, qui retourne le hash d’une chaine de caractère passée en paramètre.

❗ Attention à l’encodage de votre chaine de caractères. ❗

Pour que le résultat soit cohérent entre différents systèmes il faut commencer par convertir la chaine de caractère en UTF-8 :

VALUES CAST('This is not a test!' AS VARCHAR(512) CCSID 1208); -- 1208 = UTF-8
-- Retour : This is not a test!

Le résultat est plutôt flagrant ! D’accord pas vraiment… Par contre si on regarde la valeur hexadécimale de la chaine avec et sans conversion :

VALUES HEX('This is not a test!');
-- Retour : E38889A24089A2409596A3408140A385A2A34F

VALUES HEX(CAST('This is not a test!' AS VARCHAR(512) CCSID 1208));
-- Retour : 54686973206973206E6F742061207465737421

Le hachage se fait en hexadécimal, donc le résultat ne serait pas le même sans conversion préalable.

Il suffit maintenant de hacher notre chaine de caractères :

VALUES HASH_MD5(CAST('This is not a test!' AS VARCHAR(512) CCSID 1208));
-- Retour : EDA20FB86FE23401A5671734E4E55A12

On obtient donc la même valeur que celle que l’on a obtenu précédemment (puisque que le contenu de notre fichier est strictement égale à cette chaine de caractère).

La dernière étape est de générer le MD5 directement à partir du fichier, pour cela il suffit d’utiliser la fonction GET_BLOB_FROM_FILE :

VALUES HASH_MD5(GET_BLOB_FROM_FILE('/home/jl/file.txt')) WITH CS;
-- Retour : EDA20FB86FE23401A5671734E4E55A12

Autres algorithmes de hash

Il existe d’autres algorithmes de hash qui permettent de hacher du texte et des fichiers.
Trois autres algorithmes sont généralement disponibles :

  • sha1 (qui génère une chaine de 20 de long)
  • sha256 (qui génère une chaine de 32 de long)
  • sha512 (qui génère une chaine de 64 de long)

QSH

CommandeRésultat
/qopensys/pkgs/bin/sha1sum /home/jl/file.txt10e2e89feb9287eea7a4b7b849b7a380d95c05b9 /home/jl/file.txt
/qopensys/pkgs/bin/sha256sum /home/jl/file.txtff8fb31c076b42fd63377e7ea4747f98c34291ac6e5f53cfd3940913bc9d7d37 /home/jl/file.txt
/qopensys/pkgs/bin/sha512sum /home/jl/file.txt658efb990d2765ca65adb570daa198ef6bee55e39d3a7b7fa31270c35fdf9ee523ce638dea4796ea8923a2ad428e23d23b62175b26494fa8fdca49d5e85ce502 /home/jl/file.txt

SQL

SyntaxeRésultat
VALUES HASH_SHA1(CAST(‘This is not a test!’ AS VARCHAR(512) CCSID 1208));10E2E89FEB9287EEA7A4B7B849B7A380D95C05B9
VALUES HASH_SHA256(CAST(‘This is not a test!’ AS VARCHAR(512) CCSID 1208));FF8FB31C076B42FD63377E7EA4747F98C34291AC6E5F53CFD3940913BC9D7D37
VALUES HASH_SHA512(CAST(‘This is not a test!’ AS VARCHAR(512) CCSID 1208));658EFB990D2765CA65ADB570DAA198EF6BEE55E39D3A7B7FA31270C35FDF9EE523CE638DEA4796EA8923A2AD428E23D23B62175B26494FA8FDCA49D5E85CE502

Pour plus de détails

MD5 : https://fr.wikipedia.org/wiki/MD5
md5sum : https://fr.wikipedia.org/wiki/Md5sum
Fonction sql HASH() : https://www.ibm.com/docs/en/i/7.4?topic=sf-hash-md5-hash-sha1-hash-sha256-hash-sha512
Fonction sql BLOB() : https://www.ibm.com/docs/en/i/7.4?topic=functions-get-blob-from-file
CCSID : https://www.ibm.com/docs/en/i/7.4?topic=information-ccsid-values-defined-i
cypher : https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/rzatk/CIPHER.htm
, , Utilisation du catalogue DB2

Il existe de nombreuses tables dans QSYS qui constituent le catalogue DB2,
Ces tables sont accessibles par des vues qui se trouvent dans QSYS2 de manière globale et dans les bibliothèques de vos collections SQL.

On utilise pas assez ces informations pour analyser la base de données, elles contiennent une multitude d’informations

On va faire une petit exemple:

Imaginons que nous voulons savoir ou est utilisée une zone


Nous fixerons la database par set schema , pour éviter les qualifications

exemple de manière globale
SET SCHEMA QSYS2

On va utiliser une vue qui s’appelle SYSCOLUMNS qui contient les zones de votre database

SELECT
A.SYSTEM_COLUMN_NAME,
A.SYSTEM_TABLE_NAME,
A.SYSTEM_TABLE_SCHEMA
FROM SYSCOLUMNS A
WHERE COLUMN_NAME = ‘NUMCLI’

Vous obtenez une liste de tous les fichiers (tables, vue, PF, LF) etc …

Imaginons ensuite que vous ne vouliez que les tables ou PF vous pouvez utiliser la vue SYSTABLES

SELECT a.SYSTEM_COLUMN_NAME,
A.SYSTEM_TABLE_NAME,
A.SYSTEM_TABLE_SCHEMA
FROM SYSCOLUMNS a join SYSTABLES b on A.SYSTEM_TABLE_NAME=b.SYSTEM_TABLE_NAME
and a.SYSTEM_TABLE_SCHEMA = b.SYSTEM_TABLE_SCHEMA and B.TABLE_TYPE in(‘T’ , ‘P’)
WHERE COLUMN_NAME = ‘NUMCLI’

Vous limitez ainsi votre recherche aux tables et PF

Imaginons maintenant que vous ne vouliez que les tables et PF qui ont été utilisées sur l’année flottante (13 mois), on va utiliser la vue SYSTABLESTAT

SELECT a.SYSTEM_COLUMN_NAME,
A.SYSTEM_TABLE_NAME,
A.SYSTEM_TABLE_SCHEMA
FROM SYSCOLUMNS a join SYSTABLES b on A.SYSTEM_TABLE_NAME=b.SYSTEM_TABLE_NAME
and a.SYSTEM_TABLE_SCHEMA = b.SYSTEM_TABLE_SCHEMA and B.TABLE_TYPE in( ‘T’ , ‘P’)
join SYSTABLESTAT c on A.SYSTEM_TABLE_NAME=c.SYSTEM_TABLE_NAME
and a.SYSTEM_TABLE_SCHEMA = c.SYSTEM_TABLE_SCHEMA and c.LAST_USED_TIMESTAMP >
(current date – 13 months)
WHERE COLUMN_NAME = ‘NUMCLI’

Cette exemple n’est pas parfait, mais il vous montre qu’avec le catalogue db2 et un peu de SQL vous pouvez avoir de nombreuses informations pertinentes sur cette dernière .
Vous pouvez par exemple avoir des informations statistiques sur vos colonnes par la vue SYSCOLUMNSTAT et une vue globale avec la vue SYSFILES qui permet d’avoir un bon résumé de vos fichiers

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


Voici un lien qui vous présente les vues disponibles,

https://www.ibm.com/docs/en/i/7.5?topic=views-i-catalog-tables

Contrôlez un numéro de sécu

Voici une fonction RPGLE pour contrôler un numéro de sécurité sociale.


Elle reçoit une variable caractère de 15 de long qui contient le numéro de sécu + sa clé
et renvoie un booléen indiquant si la clé calculée est différente ou égale de la clé passée.


C’est une fonction que vous pouvez inclure dans un programme de service par exemple !

Voici le code à inclure dans votre programme de service :

dcl-proc Check_Numero_Secu export;
    dcl-pi *n ind;
        numSecu char(15) const;
    end-pi;

    select;
      when %SUBST(numSecu : 6 : 2) = '2A';
        numSecu = %SUBST(numSecu : 1 : 5) + '19' + %SUBST(numSecu : 8 : 7);
      when %SUBST(numSecu : 6 : 2) = '2B';
        numSecu = %SUBST(numSecu : 1 : 5) + '18' + %SUBST(numSecu : 8 : 7);
    endsl;

    return (97 - (%DEC(%SUBST(numSecu : 1 : 13) : 13 : 0)
              - %DEC(%DEC(%SUBST(numSecu : 1 : 13) : 13 : 0) / 97 : 13 : 0) * 97)
           = %DEC(%SUBST(numSecu : 14 : 2) : 2 : 0));
end-proc;             

Voici le code à inclure dans source de liage :

EXPORT SYMBOL(« CHECK_NUMERO_SECU »)  

Voici le code a ajouter dans dans votre programme pour pouvoir utiliser votre contrôle :

dcl-PR Check_Numero_Secu ind ;        
 W_Num_Sec char(15);                  
end-pr ;                              

// dans votre code

if Check_Numero_Secu ind(‘16403323470623’) ;

  dsply ‘error’ ;

endif;

Remarque :

Vous pouvez améliorer le code en mettant votre code sous monitor pour éviter les numéros de sécu incomplets

, Créer une requête DTFX en SQL

vous voulez créer une requête d’extraction de fichier et vous avez déjà la requête voici un petit mode opératoire

d’abord choisissez le bouton depuis votre IBMi

Choisissez propriétés

indiquez lui que vous voulez faire du SQL

Vous pouvez maintenant saisir votre requête SQL

Vous pouvez aussi comme d’habitude sauvegarder et rejouer vos requêtes de transfert !

, 2 requêtes pour gérer vos récepteurs de journaux

Pour faire un peu de place il peut être important de supprimer certains récepteurs de journaux inutiles

D’abord la liste des récepteurs détachés de plus de 30 jours

SELECT JOURNAL_RECEIVER_LIBRARY, JOURNAL_RECEIVER_NAME FROM
QSYS2.JOURNAL_RECEIVER_INFO WHERE ATTACH_TIMESTAMP < current date –
30 days and DETACH_TIMESTAMP is not null

A partir de cette liste vous pouvez faire une DLTJRNRCV de ces recepteurs

Liste des journaux qui n’éffacent pas les récepteurs après détachement
SELECT JOURNAL_NAME, JOURNAL_LIBRARY FROM qsys2/JOURNAL_INFO WHERE
DELETE_RECEIVER_OPTION = ‘NO’

A partir de cette liste vous pouvez modifier le récepteur en indiquant de supprimer les recepteurs après détachement !

CHGJRN DLTRCV(*YES)

Attention :

Il ne faut pas supprimer des récepteurs de votre base de données de production
mais il y a souvent une multitude de bases de tests qui n’ont pas besoin de leurs récepteurs détachés pour permettre de faire des tests

, Outil pour gérer les outqs

Un nouvel outil dans GTOOLS

Nous avons développé une petite commande pour un client, elle facilite la gestion des remotes Outqs
par exemple pour changer une adresse IP
La commande est WRKOUTQIP

Vous pouvez
Arrêter le travail d’édition
Supprimer une outq
Changer une outq


Les sources sont ici
https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Il y a une commande, un programme et un écran

Cet outil est un peu brut, vous pouvez l’améliorer, par exemple, un 3 dupliquer pourrait intéresser tout le monde

, Amélioration du scheduler IBMi en V7R5

En une seule commande vous pouvez holder tous les travaux en suspendant le scheduler lui même, vous voyez le status dans la commande wrkjobscde, bien utile pour les installations , les machines de backups etc..

.

HLDJOBSCDE JOB(JOBSCD) ENTRYNBR(ALL)

pour libérer

HLDJOBSCDE JOB(JOBSCD) ENTRYNBR(ALL)

Vous pouvez désormais soumettre une tâche annuellement

ADDJOBSCDE

FRQ(*YEARLY)

exemple épuration de fin d’années

, , , , Verrouiller vos sources pour VSCODE

Une des difficultés, quand on développe avec VSCE sur IBMi ,
C’est que si on est 2 deux à modifier le même source, c’est le dernier qui a raison avec perte de modification du premier même s’il a sauvegardé

Voici comment on peut améliorer les choses.
On va créer un fichier base de données qui liste les sources qui sont en cours de maintenance, un peu comme un ALM.

Avec GIT on peut arriver à des mécanismes identiques, et surtout, il faut commencer à mettre vos sources dans l’IFS directement

Voila comment, vous pouvez faire pour améliorer les choses

CREATE TABLE DB_OPENLCK (FICHIER CHAR ( 10) NOT NULL WITH
DEFAULT, BIBLIO CHAR ( 10) NOT NULL WITH DEFAULT, MEMBRE CHAR ( 10)
NOT NULL WITH DEFAULT, PARTAGE CHAR ( 3) NOT NULL WITH DEFAULT,
PUSER CHAR ( 10) NOT NULL WITH DEFAULT, PDATE DATE NOT NULL WITH
DEFAULT, PTIME TIME NOT NULL WITH DEFAULT)

Pour ajouter un source à verrouiller

INSERT INTO DB_OPENLCK VALUES(‘QRPGLESRC’, ‘GDATA’, ‘AAAA’,
‘NON’, ‘PLB’, current date, current time)

Et parmi les programmes d’exit il en a un qui va nous permettre de mettre en œuvre ce contrôle
C’est le QIBM_QDB_OPEN

On va donc écrire un programme, ici en SQLRPGLE

**free
//
// ce programme permet d'éviter de travailler à 2 sur un même source
//
Dcl-Pi *N;
  DS_parm          likeds(ds_parm_t) ;
  reponse          int(10);
End-Pi;
// dsprogramme
 dcl-ds *N PSDS ;
  nom_du_pgm CHAR(10) POS(1);
  init_user  CHAR(10) POS(254);
  enc_user  CHAR(10) POS(358);
End-ds ;
// ds format  DBOP0100
Dcl-DS ds_parm_t qualified template ;
  taille_entete  Int(10);
  format         Char(8);
  offset_liste   Int(10);
  nbr_fichiers   Int(10);
  taille_liste   Int(10);
  job            Char(10);
  profil         Char(10);
  jobnbr         Char(6);
  cur_profil     Char(10);
  reste          Char(1024);
End-DS;
  // liste des fichiers dans notre cas un seul
Dcl-DS liste  ;
  fichier       Char(10);
  biblio        Char(10);
  membre        Char(10);
  filler         Char(2);
  typefichier    Int(10);
  sous_jacent    Int(10);
  access         Char(4);
End-DS;
// variable de travail
Dcl-S partage      char(4);
Dcl-S puser      char(10);

ds_parm.offset_liste += 1;
dsply   enc_user ;

  liste = %subst(ds_parm : ds_parm.offset_liste :
  ds_parm.taille_liste);

    ds_parm.offset_liste += ds_parm.taille_liste;
 // lecture des informations dans le fichier de verrouillage explicite
 // le verrouillage est donc par utilisateur
   exec sql
   SELECT PARTAGE, PUSER into :partage , :puser
      FROM DB_OPENLCK WHERE FICHIER = :FICHIER and BIBLIO
             = :BIBLIO and MEMBRE = :MEMBRE  ;
   //
   // La régle mise en oeuvre ici
   // on autorise
   // si même utilisateur
   // si non trouvé en modification
   // Si on on a dit partage à oui
   //
  if (sqlcode = 100  or partage = 'OUI' or puser = enc_user) ;
    reponse = 1 ;
  else ;
    reponse = 0 ;
  endif ;
  // fin de programme

*inlr = *on; 

ici notre règle est la suivante
on autorise
Si le source n’est pas présent dans le fichier
Si l’utilisateur est le même que celui en cours
Si on a accepté le partage et donc le risque

Pour ajouter votre pgm exit

SYSTEM/ADDEXITPGM EXITPNT(QIBM_QDB_OPEN)
FORMAT(DBOP0100)
PGMNBR(1)
PGM(GDATA/OPENSRC)
REPLACE(*NO)

Quand on essaye d’accéder par VSCDE à notre source

Remarque


Le contrôle marche aussi avec RDI
Il marche également pour SEU, parfois on préfère un contrôle spécifique SEU, vous devrez utiliser le programme d’exit
QIBM_QSU_ALW_EDIT en utilisant le même fichier par exemple !

 **free                                                                                       
 //                                                                                           
 // Ce programme eviter de travailler à 2 sur un même source                                 
 //                                                                                           
 Dcl-Pi *N;                                                                                   
   biblio     char(10);                                                                       
   fichier    char(10);                                                                       
   membre     char(10);                                                                       
   reponse    char(1);                                                                        
 End-Pi;                                                                                      
 // dsprogramme                                                                               
  dcl-ds *N PSDS ;                                                                            
   nom_du_pgm CHAR(10) POS(1);                                                                
   init_user  CHAR(10) POS(254);                                                              
   enc_user  CHAR(10) POS(358);                                                               
 End-ds ;                                                                                     
                                                                                              
 Dcl-S partage      char(4);                                                                  
 Dcl-S puser      char(10); 
                                                                           
 // lecture des informations dans le fichier de verrouillage explicite     
 // le verrouillage est donc par utilisateur                               
   exec sql                                                               
   SELECT PARTAGE, PUSER into :partage , :puser                           
      FROM DB_OPENLCK WHERE FICHIER = :FICHIER and BIBLIO                 
             = :BIBLIO and MEMBRE = :MEMBRE  ;                            
   //                                                                     
   // La règle mise en œuvre ici                                         
   // on autorise                                                         
   // si même utilisateur                                                 
   // si non trouvé en modification                                       
   // Si on on adit partage à oui                                         
   //                                                                     
  if (sqlcode = 100  or partage = 'OUI' or puser = enc_user) ;            
    reponse = '1' ;                                                       
  else ;                                                                  
    reponse = '0' ;                                                       
  endif ;     
  // fin de programme    
                         
*inlr = *on;      

On ajoute comme ca

SYSTEM/ADDEXITPGM EXITPNT(QIBM_QSU_ALW_EDIT)
FORMAT(EXTP0100)
PGMNBR(1)
PGM(GDATA/OPENSRCE)
REPLACE(*NO)


Ca ne fait pas tout, que faire si on est 2 sur le même source ? peut être faut il avoir un source de référence pour éviter le versionnage

Remarque :

Pour diminuer le nombre d’appels du programme d’exit , vous pouvez limiter le déclenchement aux fichiers qui sont audités.

Vous devez indiquer le paramètre PGMDTA(*JOB *CALC ‘*OBJAUD’) sur les commandes ADDEXITPGM ou CHGEXITPGM.

Exemple :

ADDEXITPGM EXITPNT(QIBM_QDB_OPEN)

PGMDTA(*JOB *CALC ‘*OBJAUD’)

Vous devez ensuite indiquer les fichiers à auditer :

Exemple :

CHGOBJAUD OBJ(GDATA/QRPGLESRC)
OBJTYPE(FILE) OBJAUD(CHANGE)

A partir de ce moment la, seuls les fichiers audités déclencheront l’appel du programme d’exit QIBM_QDB_OPEN