La pratique de Perl: Vous avez dit `rsh' ? Non j'ai dit `remsh' !

by Hal Pomeranz traduction Philippe Bereski

La dernière fois j'ai montré comment Perl peut émuler bon nombre des filtres et autres outils de gestion de l'information d'UNIX. En retour du temps ainsi passé à "réinventer la roue", vous obtenez des scripts plus portables. Au bout d'un certain temps vous finirez quand même par devoir invoquer des commandes du système. C'est là que vous rencontrerez les véritables problèmes de portabilité. "Dans quel répertoire se trouve donc cette commande, et quelles sont ses options ?" Dans certains cas le nom même de la commande diffère - sur certaines machines system V, rsh est le shell restreint alors que remsh exécute les commandes sur la machine distante. Cet article est destiné à vous aider à vous y retrouver dans la tour de babel des dialectes UNIX et se termine par un shell script qui tournera sur la pluspart d'entre eux. Cet article parle peu de Perl, si donc vous n'avez pas le souci de maintenir des scripts pour de nombreuses architectures, peut-être ne vous interessera-t-il pas.

Où suis-je ?

Le premier truc consiste à deviner le type de votre machine. La pluspart des systèmes implémentent une commande /bin/arch qui retourne d'une façon ou d'une autre le constructeur et l'architecture de la machine sur laquelle votre script exécute. La commande arch ne retourne pas toujours la version de l'OS (c'est parfois bien utile), de plus, elle n'est pas universelle. Il vaut donc mieux essayer /bin/uname.

Vous pouvez obtenir le hostname de la machine, son type d'OS et son architecture matérielle par l'instruction suivante :

     ($host, $os, $arch) = split(/\s+/, '/bin/uname -nrm');
Ceci fonctionne sur tous les UNIX que j'ai rencontrés excepté sur Convex, qui pour des raisons que j'ignore n'implémente pas uname. Pour les machines dépourvues de la commande uname, il vous suffit de traiter un cas spécial sur la base de /bin/hostname. Si vous avez un grand nombre de machines de ce cas particulier, vous pouvez construire un tableau associatif static :
     ENV{`PATH'} = "/bin:/usr/bin";
          %machines = ("convex1", "ConvexOS: Convex", ...);
     ($host, $os, $arch) = split(/\s+/, '/bin/uname -nrm');
     unless ($host) { 
          chop($host = 'hostname'); 
          ($os, $arch) = split(/:/, $machines{$host}); 
          die "Unknown: $host\n" unless ($os); 
     }
Notez que j'ai utilisé un chemin relatif pour l'invocation de la commande hostname. (hostname se trouve dans /bin ou /usr/bin selon le type d'UNIX que vous utilisez. Je ne l'ai jamais rencontré ailleurs). Si vous utilisez des chemins relatifs dans vos scripts prenez bien soin d'initialiser $PATH avec une liste de répertoires réputés sains ou vous vous exposez à des chevaux de troie. N'incluez jamais le répertoire courant (".") ou un répertoire utilisateur dans $PATH.

Le problème est que les chaînes $os et plus encore $arch ont une signification pour le constructeur mais pas forcément pour les utilisateurs. Par example, sur les machines SGI $arch à la forme "IP\d+" alors que sur Amdahls c'est un code numérique du type "580". Vous n'avez pas d'autre alternative qu'examiner toutes vos machines pour obtenir la liste exhaustive des valeurs possibles.

Quand vous aurez identifié le type de votre machine, vous pourrez choisir des valeurs par défaut puis les modifier en fonction de l'architectures et de la version de l'OS :

     $bigwords = 0; 
     $gooduucp = 1; 
     $confdir = "/etc"; 
     $ps = "ps -e"

     if ($arch =~ /^sun/) { 
          $ps = "ps -ax"; 
          $gooduucp = 0 unless ($os =~ /^4\./);
     } 
     elsif ($arch =~ /^IP\d+$/) { 
          $confdir = "/usr/etc"; 
     } 
     elsif ($arch =~ /^CRAY/) { 
          $bigwords = 1; 
     } 
          : 
          : 
     else { 
          die "$host: unknown arch $arch\n"; 
     } 
Les Suns utilisent une commande ps à la Berkeley (à moins que vous ne tourniez Solaris 2.x - voyez pour celà $os). Les vieilles versions de Sun ont un UUCP déficient. Les machines SGI rangent les fichiers de configuration dans /usr/etc au lieu de /etc. Les machines CRAY ont des mots longs, il faut être prudent avec les shifts binaires. C'est une bonne précaution que de traiter le cas des architectures inconnues.

Ne le faites qu'une seule fois

Si vous avez de nombreux scripts Perl, celà peu devenir lourd d'avoir à répéter encore et toujours ces conditions. Il y a plusieures approches à ce problème :

Une solution consiste à implémenter un fichier de configuration universelle qui définit les valeurs par défaut pour tous vos scripts Perl. Rangez ce script au même endroit sur toutes vos machines, et vos scripts n'ont qu`à invoquer ce fichier par require ou par :

     eval { do "$configfile"; };
     die "Error in $configfile:\n$@" if ($@);
Souvenez vous que si vous utilisez require, la dernière instruction du fichier doit s'evaluer à vrai. La pluspart des packages se terminenet simplement par :
     1;

Si vous avez de nombreuses architectures ou de nombreux scripts Perl, ces conditions peuvent être longues. En revanche, vous n'avez qu'un seul fichier à maintenir, et il est relativement aisé d'ajouter une nouvelle architecture et de porter l'ensemble de vos script d'un seul coup.

Un seconde approche consiste à avoir un fichier de configuration par machine dans /etc. Vous pouvez alors simplement utiliser des assignations au lieu d'avoir de longues conditionnelles. Bien que ceci puisse paraître coûteux, il y a en fait de fortes chances pour que vous n'ayez qu'un fichier par architecture ou au pire quelques fichiers par architectures si vous avez de nombreuses versions d'OS installées. Vous pourez distribuer les fichiers sur vos machines depuis une machines "maîtresse" par un outil du type rdist. Vous pouvez même imaginer un script "méta-configurateur" tournant dans un cron qui reconstruit automatiquement ce fichier sur chaque machine (un programme en Bourne shell a été présenté par Bob Arnold à LISAV1).

La troisième approche est en fait le mélange des deux idées précédantes. Placez l'information spécifique d'une architécture ou d'un OS dans des fichiers différents mais au même endroit pour chaque machine. Une convention de nommage appropriée permettra à vos scripts d'utiliser le bon :

     $configdir = "/usr/local/configs"; 
     ($host, $os, $arch) = split(/\s+/, `/bin/uname -nrm`); 
     die "$host: no config file $arch.$os\n" unless (-f "$configdir/$arch.$os");
     eval { "do $configdir/$arch.$os"; }; 
     die "$host: config error:\n$@" if ($@);
Dans ce cas, tous les fichiers config sont dans /usr/local/configs et ont leur nom composé de $arch et $os retrounés par la commande uname.

Quelque soit la méthode que vous choisirez, faites attention à éviter les collisions entre les noms de variables utilisées dans les scripts et les fichiers de configuration. J'ai tendance à utiliser des majuscules pour les variables des fichiers de configuration et des minuscules dans les scripts.

Conclusion

Tout ceci vous apparaît certainement comme un gaspillage d'énerie si vous n'êtes pas administrateur d'un gros site ou si vous n'avez qu'une ou deux architectures à maintenir, et vous avez tout à fait raison. (Je vous avais d'ailleurs prévenu dès le début de cet article). Mais si vous êtes administrateur d'un gros site et que vous vous bagarez avec de nombreux scripts Perl, ces technique vous simplifiront grandement la vie.

1. Arnold, Bob, "If You've Seen One UNIX, You've Seen Them All", LISA V Conference Proceedings, 1991.
Traduction de ;login: Vol. 19 No. 1, February 1994.


Dernière édition: 15 mars 1998 phb
Original 11/25/96pc
Back to the original
Retour à l'index