Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Perl cgi avec Apache2 (Wheezy), le script garde un état persistent

4 réponses
Avatar
Francois Lafont
Bonjour à tous,

À vrai dire, je ne sais pas trop dire si mon problème est lié à Perl
ou bien à Apache2. Du coup, je poste sur 2 groupes en même temps. Sur
une Debian Wheezy, j'ai installé Apache2 et Perl CGI avec :

apt-get install apache2 libapache2-mod-perl2

Ensuite, j'ai configuré le site "default" comme ceci :

----------------------------------------------------------
<VirtualHost *:80>
ServerAdmin webmaster@localhost

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions -ParseHeaders
Options +ExecCGI -MultiViews -SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
----------------------------------------------------------

Après, j'ai mis le script test.pl ci-dessous dans
/usr/lib/cgi-bin/ :

----------------------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key --> [$share::module::hash{$key}]";
}
----------------------------------------------------------

et j'ai mis le fichier module.pm dans /etc/apache2/share/.
Voici son contenu :

----------------------------------------------------------
package share::module;

use strict;
use warnings;
use 5.010;

our %hash = ( a => 1,
b => 2,
c => 3,
);

1
----------------------------------------------------------

Puis j'ai redémarré apache2. Ensuite, je teste mon script Perl
avec curl :

$ curl http://localhost/cgi-bin/test.pl
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Ça correspond donc à ce que j'attendais (j'appelle cette
sortie la sortie 1). Mais si je relance plusieurs fois la
commande, je finis (au bout de 3 ou 4 exécutions de curl)
par avoir ça :

$ curl http://localhost/cgi-bin/test.pl
BEFORE: e --> [5]
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
BEFORE: d --> [4]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Et une fois que j'ai ça (j'appelle cette sortie la sortie 2),
j'ai l'impression que ça ne bouge plus j'ai toujours cette sortie
2. Si je redémarre apache2, je me retrouve avec la sortie 1 à
nouveau mais au bout de 3 ou 4 curl, je retombe sur la sortie 2
etc. etc.

Pourquoi je finis par avoir cette sortie 2 ? Elle signifie
qu'au moment même où le script perl s'exécute, j'ai déjà
%share::module::hash qui est mis à jour alors qu'en principe
la première boucle "for" du script test.pl arrive *avant*
la mise à jour de %share::module::hash. Du coup je ne comprends
pas trop.

Si jamais je me débarrasse du module "share::module" et que
je mets directement le hash dans le script test.pl, je ne
constate pas ce phénomène.

Est-ce possible de faire en sorte que l'état du script test.pl
« reparte à zéro » à chaque requête (via curl dans mon cas).

Merci d'avance pour votre aide.

--
François Lafont

4 réponses

Avatar
Jean-Louis Morel
Le 06/04/2014 03:01, Francois Lafont a écrit :
Bonjour à tous,

À vrai dire, je ne sais pas trop dire si mon problème est lié à Perl
ou bien à Apache2. Du coup, je poste sur 2 groupes en même temps. Sur
une Debian Wheezy, j'ai installé Apache2 et Perl CGI avec :

apt-get install apache2 libapache2-mod-perl2

Ensuite, j'ai configuré le site "default" comme ceci :

----------------------------------------------------------
<VirtualHost *:80>
ServerAdmin

DocumentRoot /var/www
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>

ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions -ParseHeaders
Options +ExecCGI -MultiViews -SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log

# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn

CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
----------------------------------------------------------

Après, j'ai mis le script test.pl ci-dessous dans
/usr/lib/cgi-bin/ :

----------------------------------------------------------
#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key --> [$share::module::hash{$key}]";
}
----------------------------------------------------------

et j'ai mis le fichier module.pm dans /etc/apache2/share/.
Voici son contenu :

----------------------------------------------------------
package share::module;

use strict;
use warnings;
use 5.010;

our %hash = ( a => 1,
b => 2,
c => 3,
);

1
----------------------------------------------------------

Puis j'ai redémarré apache2. Ensuite, je teste mon script Perl
avec curl :

$ curl http://localhost/cgi-bin/test.pl" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://localhost/cgi-bin/test.pl
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Ça correspond donc à ce que j'attendais (j'appelle cette
sortie la sortie 1). Mais si je relance plusieurs fois la
commande, je finis (au bout de 3 ou 4 exécutions de curl)
par avoir ça :

$ curl http://localhost/cgi-bin/test.pl" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://localhost/cgi-bin/test.pl
BEFORE: e --> [5]
BEFORE: c --> [3]
BEFORE: a --> [1]
BEFORE: b --> [2]
BEFORE: d --> [4]
AFTER: e --> [5]
AFTER: c --> [3]
AFTER: a --> [1]
AFTER: b --> [2]
AFTER: d --> [4]

Et une fois que j'ai ça (j'appelle cette sortie la sortie 2),
j'ai l'impression que ça ne bouge plus j'ai toujours cette sortie
2. Si je redémarre apache2, je me retrouve avec la sortie 1 à
nouveau mais au bout de 3 ou 4 curl, je retombe sur la sortie 2
etc. etc.

Pourquoi je finis par avoir cette sortie 2 ? Elle signifie
qu'au moment même où le script perl s'exécute, j'ai déjà
%share::module::hash qui est mis à jour alors qu'en principe
la première boucle "for" du script test.pl arrive *avant*
la mise à jour de %share::module::hash. Du coup je ne comprends
pas trop.

Si jamais je me débarrasse du module "share::module" et que
je mets directement le hash dans le script test.pl, je ne
constate pas ce phénomène.

Est-ce possible de faire en sorte que l'état du script test.pl
« reparte à zéro » à chaque requête (via curl dans mon cas).

Merci d'avance pour votre aide.




Le comportement que vous décrivez est tout à fait normal !

Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé. Il peut y avoir
plusieurs fois le même script dans le cache s'il y a plusieurs
requêtes simultanées.

Les variables globales sont initialisées à la première invocation,
puis elles gardent leurs valeurs. Il faut donc absolument initialiser
les variables globales en début de script. Si une variable globale
est définie dans un module, il faut une fonction d'initialisation :

package share::module;

use strict;
use warnings;
use 5.010;

our %hash;

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}

1;

On initialise à chaque exécution. Si votre hash est de taille
importante, il est recommandé de le vider à la fin du script
pour épargner de la mémoire.

#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

share::module::init(); # initialisation obligatoire

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key -|-> [$share::module::hash{$key}]";
}

%share::module::hash = (); # vidage facultatif

Attention : si vous modifiez le script, le changement est pris en
compte, car le cache est plus vieux que le script et Apache le
recompile et le remet en cache. Par contre, si vous modifiez
share::module, Apache ne s'aperçoit de rien et votre modification
n'est pas prise en compte. Il faut relancer Apache pour vider le
cache ce qui force une recompilation. (ou alors il faut utiliser
le module Apache::StatINC).

HTH

--
J-L
http://www.bribes.org/perl
Avatar
Francois Lafont
Bonjour,

Le 08/04/2014 20:33, Jean-Louis Morel a écrit :

Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé.



En effet, c'est justement cette rapidité d'exécution qui
m'a orienté vers Apache2 + Perl via CGI.

Il peut y avoir
plusieurs fois le même script dans le cache s'il y a plusieurs
requêtes simultanées.

Les variables globales sont initialisées à la première invocation,
puis elles gardent leurs valeurs.



J'imagine qu'une variable globale est une variable déclarée
avec le mot-clé our (je suis vraiment débutant en Perl). J'avoue
qu'après avoir cherché des infos sur le web, les choses ne restent
pas super claires pour moi au niveau de la différence entre my et our.
Déjà, pour moi, une variable globale peut être utilisée directement
par son nom (par exemple %hash). Or quand j'avais testé dans mon cas,
la variable %hash n'était accessible dans le script que via son nom
pleinement qualifié (ie %share::module::hash) mais pas par son nom
directement. Si je tentais « %hash » directement, j'obtenais :

Global symbol "%hash" requires explicit package name

Donc ça serait une variable globale mais dont le nom est
disponible uniquement dans l'espace de noms du package "module".
Pour moi, ça ne correspond pas tout à fait à la notion que
j'ai d'une variable globale mais encore une fois je ne
connais pas très bien Perl pour l'instant.

Il faut donc absolument initialiser
les variables globales en début de script. Si une variable globale
est définie dans un module, il faut une fonction d'initialisation :

package share::module;

use strict;
use warnings;
use 5.010;

our %hash;

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}

1;

On initialise à chaque exécution. Si votre hash est de taille
importante, il est recommandé de le vider à la fin du script
pour épargner de la mémoire.

#!/usr/bin/perl

use strict;
use warnings;
use share::module;
use 5.010;
use CGI;

my $q = CGI->new;
print $q->header();

share::module::init(); # initialisation obligatoire

foreach my $key (keys %share::module::hash) {
say "BEFORE: $key --> [$share::module::hash{$key}]";
}

# Update the hash.
$share::module::hash{d} = 4;
$share::module::hash{e} = 5;

foreach my $key (keys %share::module::hash) {
say "AFTER: $key -|-> [$share::module::hash{$key}]";
}

%share::module::hash = (); # vidage facultatif



Ok, je vois l'idée.
Mais en fait, ce que je comprends de tout ça, c'est que j'aurais
surtout tout intérêt à me débarrasser complètement des variables
globales. Faire un truc du genre :

my %hash = share::module::get_initial_hash();

Autrement dit le package "module" contient une fonction qui
va me générer le hash de départ dont j'ai besoin (mais le
package lui-même ne contient pas un tel hash).

Attention : si vous modifiez le script, le changement est pris en
compte, car le cache est plus vieux que le script et Apache le
recompile et le remet en cache. Par contre, si vous modifiez
share::module, Apache ne s'aperçoit de rien et votre modification
n'est pas prise en compte. Il faut relancer Apache pour vider le
cache ce qui force une recompilation. (ou alors il faut utiliser
le module Apache::StatINC).



Ok, si je modifie un script appelé directement, pas besoin de
redémarrer apache2 mais si je change un package qui est utilisé
(via use) par un des scripts, là je dois redémarrer apache2. C'est
noté.

Merci beaucoup pour votre aide.

--
François Lafont
Avatar
Paul Gaborit
À (at) Wed, 09 Apr 2014 05:23:18 +0200,
Francois Lafont écrivait (wrote):

Le 08/04/2014 20:33, Jean-Louis Morel a écrit :

Si vous utilisez mod_perl avec Apache::Registry, un script perl
est compilé puis mis en cache mémoire. Il y a gain de rapidité,
car Apache va réutiliser le script déjà compilé.



En effet, c'est justement cette rapidité d'exécution qui
m'a orienté vers Apache2 + Perl via CGI.



Si vous utilisez mod_perl, vous n'utilisez pas CGI !!!

En CGI, le script est un processus externe lancé par Apache à chaque
requête (les échanges entre Apache et ce processus externe se font
selon le protocole CGI).

En FastCGI, le script est un processus externe auquel Apache se
connecte au démarrage et auquel il transmet les requêtes une par une
(les échanges entre Apache et ce processus externe se font selon le
protocole FastCGI).

En mod_perl, Apache intègre un interpréteur Perl et les scripts Perl
tournent dans le processus Apache.

--
Paul Gaborit - <http://perso.mines-albi.fr/~gaborit/&gt;
Perl en français - <http://perl.mines-albi.fr/&gt;
Avatar
Jean-Louis Morel
Le 09/04/2014 05:23, Francois Lafont a écrit :

J'imagine qu'une variable globale est une variable déclarée
avec le mot-clé our (je suis vraiment débutant en Perl). J'avoue
qu'après avoir cherché des infos sur le web, les choses ne restent
pas super claires pour moi au niveau de la différence entre my et our.
Déjà, pour moi, une variable globale peut être utilisée directement
par son nom (par exemple %hash). Or quand j'avais testé dans mon cas,
la variable %hash n'était accessible dans le script que via son nom
pleinement qualifié (ie %share::module::hash) mais pas par son nom
directement. Si je tentais « %hash » directement, j'obtenais :

Global symbol "%hash" requires explicit package name

Donc ça serait une variable globale mais dont le nom est
disponible uniquement dans l'espace de noms du package "module".
Pour moi, ça ne correspond pas tout à fait à la notion que
j'ai d'une variable globale mais encore une fois je ne
connais pas très bien Perl pour l'instant.



Pour la différence entre my et our voir perlfunc :
my :
http://www.bribes.org/perl/docfr/perlfunc.html#LA5DF3A2B" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://www.bribes.org/perl/docfr/perlfunc.html#LA5DF3A2B

our :
http://www.bribes.org/perl/docfr/perlfunc.html#LB9C68853" target="_blank" class="text-blue hover:opacity-90 " style="word-break: break-all;" rel="noopener nofollow">http://www.bribes.org/perl/docfr/perlfunc.html#LB9C68853

Si vous voulez pouvoir utiliser directement la variable %hash dans
votre script sans utiliser son nom pleinement qualifié, il faut
l'exporter - deux lignes à ajouter :

package share::module;

use strict;
use warnings;
use 5.010;

use Exporter qw( import );
our @EXPORT = ('%hash');

our %hash;

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);
}

1;

Ensuite, si dans votre script vous faites
use share::module;
la variable %hash est importée automatiquement et vous pouvez
l'utiliser sous son nom.

Mais en fait, ce que je comprends de tout ça, c'est que j'aurais
surtout tout intérêt à me débarrasser complètement des variables
globales. Faire un truc du genre :

my %hash = share::module::get_initial_hash();

Autrement dit le package "module" contient une fonction qui
va me générer le hash de départ dont j'ai besoin (mais le
package lui-même ne contient pas un tel hash).




Oui.
Si vous pouvez vous débarrasser d'une variable globale, n'hésitez
pas à vous en débarrasser ! c'est des soucis en moins.

Je pensais que votre exemple de code était simplifié et que
vous aviez plusieurs variables globales à initialiser. C'est
facile avec une seule fonction :

sub init {
%hash = (
a => 1,
b => 2,
c => 3,
);

@liste = (...);

$foo = 'bar';
}

--
J-L
http://www.bribes.org/perl