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

SNMP : lancement d'un script qui ne rend pas la main immédiatement

4 réponses
Avatar
Francois Lafont
Bonjour à tous,

J'ai le problème suivant que je détaille plus bas : via SNMP, je
veux lancer un script shell qui va lancer à son tour une commande
qui peut durer un peu longtemps, mais il va lancer cette commande
en arrière plan afin de redonner la main immédiatement. Mon souci
est que le script me rend bien la main immédiatement (ce que je
souhaite) quand je le lance en console, mais quand je le lance via
SNMP le script dure longtemps comme s'il attendait la fin de la
commande en arrière plan alors que justement, en tant que commande
lancée en arrière plan, elle ne devrait pas être bloquante.

Je vous donne les commandes ci-dessous qui permettent de reproduire
le problème en 5 minutes chrono via des copier-coller. L'exemple est
réalisé sur une Debian Wheezy. Ici la requête SNMP (le client) est
faite sur le serveur SNMP lui-même (le client et le serveur sont la
même machine) afin de pouvoir réaliser le test facilement juste sur
une seule et même machine (mais dans la pratique le client et le
serveur sont 2 machines différentes).

Sur une Debian Wheezy à jour, je fais donc ceci :

------------------------------------------------------------
# Je crée un script pipeau qui représente la commande à exécuter.
cat > /tmp/run <<EOF
#!/bin/sh
echo debut
echo fin
EOF

# Je le rends exécutable pour tout le monde.
chmod a+x /tmp/run

# Installation de SNMP partie serveur.
apt-get update && apt-get -y install snmpd

# Je place une conf snmpd minimaliste avec
# un compte "toto" qui pourra se connecter en SNMPv3
# en lecture seule.
cat > /etc/snmp/snmpd.conf <<EOF
agentAddress udp:161
view systemonly included .1.3.6.1.2.1
view systemonly included .1.3.6.1.4.1
agentSecName toto
rouser toto priv -V systemonly
extend run /tmp/run
EOF

# Je crée le compte "toto" avec ses mots de passe etc.
invoke-rc.d snmpd stop
net-snmp-config --create-snmpv3-user -ro -a "1234567890" -A SHA -x "1234567890AZERTY" -X AES toto
invoke-rc.d snmpd start
------------------------------------------------------------

Voilà, à ce stade, j'ai un service snmpd qui écoute sur
localhost et qui pourra répondre aux requêtes de "toto".
"toto" pourra par exemple lancer le script /tmp/run.
Je passe à l'installation de la partie cliente :

------------------------------------------------------------
# Attention non-free est nécessaire
apt-get install -y snmp snmp-mibs-downloader
------------------------------------------------------------

Maintenant, je peux faire une requête pour lancer le script
/tmp/run via SNMP et afficher sa sortie comme ceci :

------------------------------------------------------------
# Désolé, la ligne est un peu longue mais pour les
# copier-coller c'est plus facile.
~# snmpget -OvQ -r 0 -t 15 -u toto -A 1234567890 -X 1234567890AZERTY -a sha -x aes -v3 -l authPriv localhost 'NET-SNMP-EXTEND-MIB::nsExtendOutputFull."run"'
debut
fin
------------------------------------------------------------

J'obtiens bien la sortie de manière quasi immédiate.
Jusqu'ici tout via bien. Là où les problèmes commencent,
c'est quand je veux que mon script lance en arrière plan
une commande susceptible de durer un peu longtemps. Je
modifie alors le script /tmp/run ainsi :

------------------------------------------------------------
#!/bin/sh
echo debut

{
# Début d'une série de commandes qui
# peuvent durer un peu longtemps.
# Mais tout ceci est lancé en arrière plan
# à cause du & plus bas.
echo "background debut" > /tmp/log
sleep 10
echo "background fin" >> /tmp/log
} &

echo fin
------------------------------------------------------------

Pour ne pas avoir de problème de droit sur le fichier /tmp/log
(car via SNMP, c'est le compte Unix snmp qui lance le script au
final), je fais :

touch /tmp/log
chmod 666 /tmp/log

Maintenant, si je lance le script /tmp/run directement en console,
pas de souci. Le script me rend la main quasiment immédiatement et
le fichier /tmp/log est bien écrit avec la ligne « background fin »
qui apparaît bien au bout de 10 secondes alors que le script /tmp/run
m'a déjà rendu le prompt depuis longtemps. Bref, en console, tout se
passe comme prévu.

Maintenant, hélas, via SNMP il n'en est pas de même :

~# snmpget -OvQ -r 0 -t 15 -u toto -A 1234567890 -X 1234567890AZERTY -a sha -x aes -v3 -l authPriv localhost 'NET-SNMP-EXTEND-MIB::nsExtendOutputFull."run"'
debut
fin

J'ai bien la sortie du script qui apparaît mais seulement au
bout de 10 secondes environ ! En clair, la commande snmpget
ne se termine seulement une fois que le script /tmp/run a
terminé d'écrire dans le fichier /tmp/log alors que pourtant
ces opérations d'écriture sont lancées en arrière plan et donc
le script ne devrait pas attendre la fin de ces opérations
(comme cela se produit en console).

Savez-vous pourquoi le comportement est différent via SNMP ?
Et surtout comment faire en sorte que, via SNMP, je puisse
récupérer le prompt instantanément pendant que les tâches en
arrière plan continuent de s'exécuter ?

Merci d'avance pour votre aide.

--
François Lafont

4 réponses

Avatar
Olivier Miakinen
Le 09/02/2014 19:01, Francois Lafont a écrit :

J'ai le problème suivant que je détaille plus bas : via SNMP, je
veux lancer un script shell qui va lancer à son tour une commande
qui peut durer un peu longtemps, mais il va lancer cette commande
en arrière plan afin de redonner la main immédiatement. Mon souci
est que le script me rend bien la main immédiatement (ce que je
souhaite) quand je le lance en console, mais quand je le lance via
SNMP le script dure longtemps comme s'il attendait la fin de la
commande en arrière plan alors que justement, en tant que commande
lancée en arrière plan, elle ne devrait pas être bloquante.

[...]
------------------------------------------------------------
#!/bin/sh
echo debut

{
# Début d'une série de commandes qui
# peuvent durer un peu longtemps.
# Mais tout ceci est lancé en arrière plan
# à cause du & plus bas.
echo "background debut" > /tmp/log
sleep 10
echo "background fin" >> /tmp/log
} &

echo fin
------------------------------------------------------------
[...]



Je ne suis sûr de rien, mais peut-être que le script appelant est
bloqué parce que des ressources sont utilisées par le script appelé,
en particulier les entrées et sorties standards.

Tu peux essayer d'ajouter « nohup » devant l'appel.

Tu peux aussi essayer d'ajouter « 0</dev/null », « >/dev/null »
et « 2>/dev/null » derrière l'appel (mais avant le « & »).

Cordialement,
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 09/02/2014 22:19, je répondais à François Lafont :

[...]
------------------------------------------------------------
#!/bin/sh
echo debut

{
# Début d'une série de commandes qui
# peuvent durer un peu longtemps.
# Mais tout ceci est lancé en arrière plan
# à cause du & plus bas.
echo "background debut" > /tmp/log
sleep 10
echo "background fin" >> /tmp/log
} &

echo fin
------------------------------------------------------------
[...]



Je ne suis sûr de rien, mais [...]



... mais peut-être que remplacer tes accolades par des parenthèses
pour lancer vraiment un sous-shell au lieu de faire exécuter les
commandes par le shell appelant, ça pourrait aider...

Donc : « ( ... )& » au lieu de « { ... }& ».
Avatar
Francois Lafont
Bonsoir,

Le 09/02/2014 22:19, Olivier Miakinen a écrit :

Je ne suis sûr de rien, mais peut-être que le script appelant est
bloqué parce que des ressources sont utilisées par le script appelé,
en particulier les entrées et sorties standards.



Je ne suis pas sûr de comprendre car pour moi il n'y a qu'un seul
script, c'est le script /tmp/run. Ou alors tu considères que le
script appelé est /tmp/run et que le script appelant est l'agent
SNMP responsable sur lancement de /tmp/run déclenché par la
requête qu'il reçoit, c'est ça ?

Tu peux essayer d'ajouter « nohup » devant l'appel.



J'avais essayé le nohup déjà mais sans succès.

Tu peux aussi essayer d'ajouter « 0</dev/null », « >/dev/null »
et « 2>/dev/null » derrière l'appel (mais avant le « & »).



Bien joué, ça a résolu mon problème. En fait, ceci semble
suffisant d'après mes tests :

------------------------------------------------------------
#!/bin/sh
echo debut

{
# Début d'une série de commandes qui
# peuvent durer un peu longtemps.
# Mais tout ceci est lancé en arrière plan
# à cause du & plus bas.
echo "background debut" > /tmp/log
sleep 10
echo "background fin" >> /tmp/log
} >/dev/null 2>&1 &

echo fin
------------------------------------------------------------

Mais par précaution, je rajouterai aussi le « 0</dev/null » pour
l'entrée standard.

Maintenant, pour l'explication ça correspond à ce tu disais il
me semble : l'agent SNMP, avant d'envoyer sur le réseau la sortie
du script, « voit » que les sorties standards sont potentiellement
occupées par les processus lancés en arrière plan (même si en
pratique ce n'est pas le cas) alors il attend que ça se finisse
pour envoyer la sortie définitive. Avec les redirections, l'agent
SNMP voit qu'une fois le « echo fin » exécuté les sorties standards
ne plus occupées et donc il envoie dans la foulée la sortie
définitive sur le réseau. En somme, il n'y a pas tant de différence
avec le script exécuté en console, simplement tout s'affiche au fur
et à mesure et, une fois que j'ai le prompt, il est toujours possible
que des choses soient écrites sur mon terminal. Alors que dans le
cas de l'agent SNMP, il attend que les sorties standards soient
définitivement libérées pour envoyer le résultat sur le réseau.

J'ai bon ?

Enfin, est-il préférable que je remplace les accolades du script
ci-dessus par des parenthèses ? Qu'est-ce que ça change au final ?

Merci encore Olivier pour ton aide.

--
François Lafont
Avatar
Olivier Miakinen
Le 10/02/2014 01:54, Francois Lafont m'a répondu :

Je ne suis sûr de rien, mais peut-être que le script appelant est
bloqué parce que des ressources sont utilisées par le script appelé,
en particulier les entrées et sorties standards.



Je ne suis pas sûr de comprendre car pour moi il n'y a qu'un seul
script, c'est le script /tmp/run. Ou alors tu considères que le
script appelé est /tmp/run et que le script appelant est l'agent
SNMP responsable sur lancement de /tmp/run déclenché par la
requête qu'il reçoit, c'est ça ?



Mon vocabulaire était mal choisi, d'autant que je me rends compte
que je n'avais pas les idées très claires. À vrai dire je crois
n'avoir jamais mis un « & » derrière autre chose que le nom d'un
programme ou un script externe, en tout cas jamais derrière une
liste de commandes entourées de parenthèses, et encore moins
d'accolades. Je ne savais même pas que c'était possible.

Tu peux aussi essayer d'ajouter « 0</dev/null », « >/dev/null »
et « 2>/dev/null » derrière l'appel (mais avant le « & »).



Bien joué, ça a résolu mon problème.



Je suis ravi d'avoir trouvé la solution malgré mon incompétence
dont je me rends compte à présent ! ;-)

En fait, ceci semble
suffisant d'après mes tests :

[...]

J'ai bon ?



Je n'ose plus rien affirmer, et j'espère que de plus doués que moi
viendront donner leur avis.

Enfin, est-il préférable que je remplace les accolades du script
ci-dessus par des parenthèses ? Qu'est-ce que ça change au final ?



Pareil, je préfère me taire car je suis surpris que la syntaxe
« { ... } & » puisse fonctionner.

Cordialement,
--
Olivier Miakinen