8 min de lecture

Les tâches SSH survivent à un appel d’outil réussi

Testez les tâches SSH en arrière-plan, nohup, le contrôle des tâches et les processus détachés pour ne pas clore l’audit trop tôt.

Les tâches SSH survivent à un appel d’outil réussi

Un code de sortie nul pour un appel SSH prouve que la commande distante a signalé une réussite. Il ne prouve pas que tous les processus lancés par cette commande se sont arrêtés, ont fini leurs écritures ou ont atteint un résultat correct. Si la commande a lancé un travail en arrière-plan, l’action qui compte peut tout juste commencer lorsque l’outil enregistre sa fin.

Je considère cet écart comme une limite d’audit, pas comme une curiosité du shell. Un agent peut exécuter un script de déploiement, recevoir le code 0 et poursuivre pendant qu’une migration détachée continue de modifier des données. La transcription dit la vérité sur le canal SSH, mais elle incite à tirer une conclusion erronée sur le travail distant. Il faut nommer le cycle de vie que l’on veut observer, le tester avec le même shell et les mêmes conditions de terminal que l’agent, puis obtenir la preuve de fin auprès du système responsable du processus durable.

Le zéro décrit la commande distante, pas ses descendants

OpenSSH renvoie le code de sortie fourni par la commande distante, ou 255 si le client SSH lui-même rencontre une erreur. RFC 4254 est encore plus précis : lorsque la commande à l’autre extrémité se termine, le serveur peut envoyer une requête de canal exit-status, puis fermer le canal. Aucun des deux documents ne dit que le serveur attend récursivement chaque descendant de la commande.

Cette distinction compte dès qu’un shell exécute une liste asynchrone. POSIX définit une commande terminée par & comme asynchrone : le shell la lance et continue sans attendre. S’il n’a plus rien à faire, il peut se terminer avec succès alors que l’enfant asynchrone vit encore. Le code SSH appartient à ce shell.

Il existe au moins quatre résultats que l’on appelle facilement réussite :

  • La connexion SSH et l’authentification ont réussi.
  • Le shell distant a accepté et lancé une commande.
  • La charge de travail lancée s’est terminée avec le code 0.
  • L’effet attendu est durable et observable.

Un entier ne peut pas établir les quatre. Un journal d’audit clair doit indiquer quel événement l’a produit. J’emploie ssh_command_exit_status pour le résultat du canal et je réserve workload_result aux preuves communiquées par le responsable distant du travail.

Le même avertissement vaut lorsque l’exécutable distant se transforme en démon. Un lanceur peut renvoyer 0 après un fork réussi, même si son enfant échoue quelques secondes plus tard en ouvrant une base de données, en liant un port ou en lisant une configuration. Le lanceur a respecté son contrat, mais l’auditeur a choisi le mauvais contrat.

Une sonde de douze secondes révèle l’écart

Vous pouvez reproduire cette réussite trompeuse sans démon, accès root ni réglage inhabituel du shell. Exécutez ceci avec un compte Unix jetable. Les redirections explicites sont importantes, car elles permettent au processus en arrière-plan de libérer le canal SSH tout en continuant son exécution.

ssh testhost 'rm -f /tmp/ssh-bg.done /tmp/ssh-bg.log; (sleep 12; date -u +%FT%TZ > /tmp/ssh-bg.done) > /tmp/ssh-bg.log 2>&1 < /dev/null & printf "launcher_pid=%s\n" "$!"'
printf 'ssh_status=%s\n' "$?"
ssh testhost 'test -f /tmp/ssh-bg.done; printf "done_status=%s\n" "$?"'
sleep 13
ssh testhost 'cat /tmp/ssh-bg.done'

Un résultat immédiat courant présente cette forme :

launcher_pid=41872
ssh_status=0
done_status=1
2026-07-24T10:14:05Z

Le PID et l’horodatage seront différents. La contradiction est le but : ssh_status=0 et done_status=1 coexistent parce qu’ils répondent à deux questions. Le shell a bien lancé la liste asynchrone, mais le fichier témoin n’existait pas encore.

Ne transformez pas cet exemple en orchestration de production. Les fichiers témoins dans /tmp peuvent entrer en collision, disparaître ou être falsifiés par un autre processus suffisamment autorisé. La sonde sert à rendre le calendrier visible. Un enregistrement de fin en production exige un identifiant d’exécution unique, un stockage protégé, un rédacteur authentifié et un état d’échec défini.

Répétez la sonde par le chemin exact qu’utilise l’agent. Une commande directe dans un terminal, une requête exec SSH non interactive, un appel SSH avec pseudo-terminal et une passerelle d’outils peuvent choisir d’autres fichiers de démarrage, shells et dispositions de descripteurs. Un test qui omet ces détails a testé un système voisin.

Des descripteurs ouverts peuvent simuler une attente synchrone

La mise en arrière-plan et la fermeture du canal sont deux mécanismes distincts. Un enfant qui hérite de la sortie standard ou de l’erreur standard du canal SSH peut garder ce canal lisible après la fin du shell distant. Le processus local ssh semble parfois attendre l’enfant parce que le tube n’a pas atteint la fin de fichier, pas parce que SSH supervise le résultat de l’enfant.

Comparez ces deux appels et mesurez leur durée :

time ssh testhost 'sleep 12 &'
time ssh testhost 'sleep 12 > /tmp/sleep.log 2>&1 < /dev/null &'

Avec des combinaisons courantes d’OpenSSH et de shell, le premier appel peut rester ouvert jusqu’à la fin de sleep, alors que le second revient rapidement. Traitez cela comme une observation à vérifier, pas comme une promesse portable. L’implémentation du shell, le serveur, l’allocation d’un pseudo-terminal et la gestion des descripteurs par l’enfant peuvent modifier le résultat.

Cette attente accidentelle constitue une preuve faible. Le processus peut fermer ses descripteurs tôt et continuer son travail. Il peut créer un petit-enfant qui les ferme. Il peut envoyer sa sortie par un socket ou écrire directement dans le stockage. À l’inverse, un auxiliaire qui se contente de garder stdout ouvert peut faire croire que l’appel travaille encore après l’échec de l’opération importante.

Les descripteurs restent utiles à inspecter, car ils expliquent de nombreux tests incohérents. Sous Linux, capturez le PID distant et examinez-les tant que l’appel SSH est actif :

pid=$(cat /run/user/$(id -u)/agent-job.pid)
ps -o pid=,ppid=,pgid=,sid=,stat=,etime=,args= -p "$pid"
ls -l "/proc/$pid/fd/0" "/proc/$pid/fd/1" "/proc/$pid/fd/2"

Consignez le PID parent, le groupe de processus, l’ID de session, l’état, la durée, la commande et les cibles des descripteurs 0, 1 et 2. Si /proc n’existe pas, utilisez les outils natifs du système. Ne réduisez pas le test à pgrep name : les noms entrent en collision, les enveloppes les modifient et un PID peut être réutilisé après une sortie.

nohup traite la déconnexion, pas la responsabilité

nohup modifie la gestion des signaux pour que la commande appelée ignore SIGHUP. Il ne place pas la commande en arrière-plan. Le manuel GNU Coreutils le dit explicitement et demande d’ajouter & pour une exécution asynchrone. Cette réserve disparaît souvent des extraits de déploiement copiés.

Ses règles de redirection surprennent aussi via SSH. GNU nohup redirige l’entrée standard seulement si elle est un terminal, envoie la sortie vers nohup.out seulement si stdout est un terminal et applique normalement le même choix à l’erreur standard. Une commande SSH non interactive utilise souvent des tubes, donc nohup peut laisser ces descripteurs reliés au canal SSH.

Ces deux commandes font donc des promesses différentes :

ssh testhost 'nohup /opt/jobs/rebuild-index &'
ssh testhost 'nohup /opt/jobs/rebuild-index > /var/log/rebuild-index.log 2>&1 < /dev/null &'

La seconde forme déconnecte explicitement les descripteurs standard. Elle n’indique toujours pas si rebuild-index s’est terminé. nohup signale les échecs d’appel, comme une commande absente, et son code suit sinon la commande invoquée. Une fois l’appel placé en arrière-plan, le shell signale normalement le lancement, pas le résultat final.

L’immunité à SIGHUP n’est qu’une condition de survie. Le processus peut mourir parce qu’un gestionnaire de connexion supprime la session, qu’un gestionnaire de services tue son groupe de contrôle, que le noyau applique une politique de mémoire insuffisante, qu’un administrateur révoque le compte ou que l’hôte redémarre. Il peut aussi survivre et produire le mauvais résultat. nohup n’apporte ni identité, ni reprise, ni limite de ressources, ni état durable, ni preuve fiable de fin.

J’utilise encore nohup pour une petite maintenance jetable lorsque la perte du résultat est acceptable et que je surveille l’hôte. Je ne l’utilise pas pour transformer l’appel SSH d’un agent en tâche de production gérée. Ce conseil reste populaire parce que l’extrait est court et survit souvent à la perte du terminal. Il est mauvais dès que quelqu’un doit prouver ce qui s’est terminé.

Le contrôle des tâches change avec un terminal

Gardez les clés hors des agents
Sallyport exécute les actions SSH tandis que la clé reste dans son coffre chiffré.

Le contrôle des tâches du shell regroupe les processus afin qu’un utilisateur interactif puisse suspendre, reprendre et déplacer des pipelines entre premier et arrière-plan. Un shell non interactif s’exécute généralement sans mode moniteur et une requête exec SSH n’a normalement pas de pseudo-terminal sauf demande du client. Les scripts qui dépendent de jobs, %1, disown ou des signaux du terminal peuvent donc changer de comportement avec un agent.

POSIX lie les identifiants de tâche et les PID d’arrière-plan connus à l’environnement du shell actuel. Son utilitaire wait peut attendre ces processus connus, mais un wait lancé dans un autre shell n’hérite pas de la table des tâches. Cette technique d’audit échoue :

ssh testhost 'long_task & printf "%s\n" "$!"'
ssh testhost 'wait 41872; printf "wait_status=%s\n" "$?"'

Le deuxième appel lance un nouveau shell. Même si 41872 vit toujours, ce shell ne le connaît pas comme enfant. POSIX impose 127 pour un PID inconnu transmis à wait. Les autorisations et la réutilisation des PID rendent toute reconstruction encore moins fiable.

Gardez lancement et attente dans le même shell lorsque le contrat exige une fin synchrone :

ssh testhost 'long_task > /tmp/long-task.log 2>&1 < /dev/null & pid=$!; printf "pid=%s\n" "$pid"; wait "$pid"; rc=$?; printf "workload_status=%s\n" "$rc"; exit "$rc"'

Ce modèle renvoie le code de l’enfant et maintient l’action SSH ouverte. Il fonctionne pour un enfant qui reste attaché à ce shell. Si long_task crée un autre processus et que l’original sort, wait peut finir avant le vrai travailleur. Testez le véritable exécutable, pas un sleep de remplacement, avant d’accepter ce contrat.

Les pseudo-terminaux ajoutent des signaux et modifient la mise en tampon. Un terminal peut envoyer SIGHUP à la fin de sa session, et les groupes d’arrière-plan qui lisent le terminal de contrôle peuvent recevoir SIGTTIN et s’arrêter. Certains programmes passent à une mise en tampon par ligne ou changent leur sortie lorsqu’ils détectent un terminal. Sauf besoin réel, l’automatisation doit éviter d’en allouer un et régler délibérément les trois descripteurs standard.

setsid détache un processus sans produire de preuve

setsid crée une nouvelle session et un nouveau groupe de processus, initialement sans terminal de contrôle. Cette séparation est plus forte que la seule ignorance de SIGHUP. Elle explique pourquoi un enfant peut survivre au shell et pourquoi les signaux du terminal cessent de le suivre.

Elle ne place pas le processus sous supervision. Après la sortie du parent original, un autre processus peut adopter le descendant. Sur un hôte classique, il peut s’agir de PID 1 ; dans un conteneur ou un arbre de services, d’un sous-récupérateur. Cette nouvelle parenté ne dit rien sur la réussite et peut effacer le lien le plus simple avec l’action de lancement.

Un test utile capture l’identité avant la disparition du shell :

ssh testhost 'run_id=agent-probe-20260724-1014; setsid sh -c '\''printf "%s\n" "$$" > /tmp/'"$run_id"'.pid; sleep 12; printf "complete\n" > /tmp/'"$run_id"'.state'\'' > /tmp/'"$run_id"'.log 2>&1 < /dev/null & printf "run_id=%s\n" "$run_id"'

Interrogez ensuite avec l’ID renvoyé, considérez le PID enregistré comme un indice et vérifiez l’heure de démarrage et la commande avant d’agir. Un PID seul n’est pas une identité durable. Si le processus sort et que le noyau réutilise son numéro, une commande de nettoyage ultérieure peut viser un travail sans rapport.

Le double fork, setsid, disown et la fermeture des descripteurs sont des techniques d’implémentation. Les équipes les confondent souvent avec un protocole de tâches parce qu’elles rendent la main au terminal. Un protocole répond à d’autres questions : qui possède le travail, comment l’interroger, quels sont ses états finaux, où trouver la raison de sortie, comment annuler tout l’arbre et quel identifiant relie requête, journaux, effets et audit ?

Si la réponse de lancement ne répond pas à ces questions, consignez un démarrage détaché, pas une action terminée.

Définissez trois événements dans le contrat d’audit

Révoquez aussitôt l’exécution agent
Sessions journal bloque les nouvelles actions de passerelle après une soumission suspecte.

Une action distante auditable demande des événements séparés pour l’acceptation, la fin du canal et la fin de la charge. Les réduire à un booléen success crée une fausse certitude et fait dépendre l’enquête de l’historique du shell.

J’utilise un enregistrement de cette forme conceptuelle :

{
  "action_id": "act_01J3M8Q4",
  "remote_host": "worker-07",
  "launch": {"state": "accepted", "at": "2026-07-24T10:14:00Z"},
  "ssh_command": {"state": "exited", "status": 0, "at": "2026-07-24T10:14:01Z"},
  "workload": {"id": "job_8931", "state": "running", "result": null},
  "completion_source": "remote-job-manager"
}

Les noms importent moins que la séparation. accepted signifie que le responsable distant a validé et pris en charge la requête. exited signifie que la commande SSH s’est terminée. running signifie que le travail durable n’a pas atteint un état final. Seul le composant responsable doit écrire succeeded, failed ou cancelled.

Rendez les transitions explicites. Un lancement peut échouer avant de créer un ID. Un canal peut casser après l’acceptation distante, laissant l’appelant dans l’incertitude plutôt qu’en échec. La charge peut échouer après une sortie propre du canal. Une annulation peut être demandée sans être terminée. Un modèle incapable de représenter unknown finira par enregistrer une supposition comme un fait.

L’idempotence appartient à ce contrat. Si le client perd le canal après envoi, il doit réessayer avec le même ID d’action et demander si le responsable l’a déjà accepté. Lancer une seconde migration parce que la première réponse s’est perdue est pire qu’un journal désordonné.

La preuve de fin doit inclure l’ID, l’état terminal, la cause de sortie, les heures de début et de fin, et l’identité du gestionnaire qui a observé l’état. Ajoutez une preuve propre à l’effet si le risque le justifie, comme une révision déployée, un manifeste de sauvegarde complet ou une version de schéma. Ne prenez pas une ligne contenant done comme seule autorité, sauf si son enregistreur et son stockage appartiennent au protocole fiable.

Testez les fenêtres d’échec, pas seulement le bon déroulement

Une matrice utile fait varier le mode de détachement, le devenir des descripteurs et le moment où connexion ou processus échoue. Exécutez-la sur chaque classe d’hôte, car gestionnaires de connexion, shells et gestionnaires de services modifient la survie.

Couvrez au moins ces cas :

  • Commande au premier plan, tâche de shell en arrière-plan, nohup avec arrière-plan, nouvelle session par setsid et programme qui se transforme en démon.
  • Sans terminal et avec pseudo-terminal.
  • Descripteurs hérités, redirigés vers des fichiers et fermés par l’enfant.
  • Déconnexion avant acceptation, après acceptation mais avant réponse, et après la sortie SSH.
  • Enfant qui sort en erreur, reçoit un signal, se bloque, crée un petit-enfant et survit jusqu’à l’annulation explicite.

Pour chaque cas, capturez quatre horloges : démarrage client, accusé de lancement, fermeture du canal et état terminal distant. Gardez séparés le code SSH et le résultat du travail. Inspectez groupe et session pendant l’exécution, puis prouvez que l’annulation atteint tous les descendants.

Un banc compact peut échouer si le code 0 arrive sans preuve terminale :

result=$(ssh testhost '/usr/local/bin/job-submit agent-probe-42')
ssh_rc=$?
printf 'ssh_rc=%s response=%s\n' "$ssh_rc" "$result"
job_id=$(printf '%s\n' "$result" | sed -n 's/^job_id=//p')
test "$ssh_rc" -eq 0 && test -n "$job_id" || exit 1
/usr/local/bin/poll-job "$job_id" || exit 1

L’exemple suppose que job-submit renvoie exactement une ligne job_id= et que poll-job authentifie la requête, attend un état terminal et sort avec le résultat réel. Ce sont des exigences du contrat, pas des propriétés de SSH. En production, refusez toute sortie supplémentaire, imposez un délai, conservez l’incertitude à expiration et stockez la réponse brute.

Testez aussi l’observateur. Arrêtez l’agent après le lancement. Redémarrez le client. Faites tourner l’identifiant SSH. Redémarrez l’hôte si le travail doit survivre. Si le seul exemplaire de l’ID vit dans la fenêtre de contexte d’un agent, le système n’est pas auditable.

Un gestionnaire de services est généralement le bon responsable

Faites passer SSH par la passerelle
Le shim sp mcp envoie le travail SSH via Sallyport et son auxiliaire sans état.

Lorsqu’un travail doit survivre à la commande SSH, confiez-le à un service ou gestionnaire distant et renvoyez son identifiant durable. Le gestionnaire doit posséder le groupe, collecter la sortie, appliquer les ressources et l’annulation, conserver l’état et proposer une requête qui distingue exécution et état terminal.

Sur un hôte systemd, un service transitoire ou modèle fournit un groupe de contrôle et une identité dans le journal. Le manuel systemd-run distingue le démarrage asynchrone de l’attente de fin et avertit qu’un service simple peut considérer le démarrage réussi après le fork, avant l’exécution du programme. Je préfère Type=exec si l’échec d’exécution doit être visible, mais cela prouve encore le démarrage, pas le succès final.

Une unité modèle peut établir la limite ainsi :

[Unit]
Description=Agent job %i

[Service]
Type=exec
ExecStart=/usr/local/libexec/agent-job %i
StandardOutput=journal
StandardError=journal
KillMode=control-group
TimeoutStopSec=30s

Soumettez un ID unique et validé, puis interrogez l’unité jusqu’à un état terminal. Consignez ActiveState, SubState, Result, ExecMainStatus, les heures et l’ID. Vérifiez le comportement si le programme fait un fork, car le type de service et le programme doivent s’accorder. Ne transformez aucune saisie arbitraire en nom d’unité ou argument sans validation stricte.

Une file, un ordonnanceur par lots, un orchestrateur de conteneurs ou une table propre à l’application offrent la même limite. Choisissez le responsable qui gère déjà ressources et reprise. SSH doit soumettre et interroger, pas imiter un ordonnanceur avec une longue chaîne d’opérateurs shell.

Pour un travail court, garder la commande au premier plan et renvoyer son vrai code est plus simple. Le détachement coûte un stockage d’état, une identité, une sémantique d’annulation, une rétention et une réconciliation. Ne payez ce coût que si le travail doit réellement survivre à l’appel.

L’audit se termine à l’état terminal distant

Une passerelle d’action peut enregistrer exactement l’appel SSH sans savoir qu’un descendant distant existe. Sallyport consigne l’action SSH dans Activity journal et l’exécution de l’agent dans Sessions journal ; l’entrée prouve donc le résultat du canal, pas la liste des processus de l’hôte. L’ID distant et son événement terminal doivent revenir par une action explicite et auditable.

Cette division garde chaque registre honnête. La passerelle prouve quelle exécution a invoqué SSH, quelle clé protégée a autorisé l’action, quel appel s’est produit et quel résultat est revenu. Le gestionnaire distant prouve la suite. Reliez les deux avec un ID d’action que l’agent ne peut remplacer entre lancement et requête.

Ne marquez pas un lancement détaché completed. Utilisez submitted ou detached, affichez l’ID et gardez l’action parente ouverte ou visiblement en attente jusqu’à ce qu’un observateur fiable inscrive l’état terminal. Si l’observation expire, affichez unknown et exigez une réconciliation. Un état rouge gêne parfois, mais un vert fondé sur le mauvais processus est dangereux.

Le moment de l’approbation demande la même précision. Approuver l’usage d’une clé SSH autorise une tentative avec les informations alors affichées. Le clic n’approuve pas toute action future de descendants sans limite et ne certifie pas l’effet final. Si une tâche peut durer des heures ou créer des processus, affichez-le avant le lancement et liez l’approbation à l’ID, l’hôte, l’intention et le type distant. Approbation et fin appartiennent à la même chaîne, mais décrivent deux décisions.

Conservez la sortie brute à chaque limite. Sauvegardez la réponse avant analyse, capturez stderr séparément si le protocole le permet et notez si un pseudo-terminal a fusionné les flux. L’analyseur doit rejeter les ID dupliqués, caractères de contrôle, réponses tronquées et lignes supplémentaires qui confondent deux réponses. Les champs analysés servent l’automatisation ; les octets bruts permettent de revoir un défaut d’analyse, de citation shell ou de programme. Aucun ne doit contenir de secret.

Un témoin de fin exige aussi une publication atomique. Le travailleur doit écrire dans un fichier temporaire protégé, vider les données si la durabilité compte et renommer le fichier seulement lorsque l’enregistrement est complet. La requête doit vérifier ID, propriétaire attendu, type et état avant de lui faire confiance. Mieux vaut qu’un gestionnaire ou une base expose l’état par une interface authentifiée. Un témoin inscriptible par tous dans /tmp montre le temps, mais ne tranche pas un incident.

Prévoyez une livraison incertaine pour l’envoi et l’annulation. Si la connexion disparaît après acceptation mais avant réception de l’ID, l’état local correct est unknown. Reconnectez-vous avec la clé d’idempotence originale et recherchez cet envoi. Ne le répétez pas en silence. Si la réponse d’annulation se perd, interrogez jusqu’à ce que le gestionnaire annonce un état terminal et un groupe vide. Envoyer un signal est une tentative, pas une preuve d’arrêt.

La réconciliation doit survivre à l’agent. Stockez les ID non résolus hors de la conversation, attribuez un responsable et lancez une requête périodique qui clôt ou escalade les anciens enregistrements. Définissez délai et échec séparément : une tâche peut dépasser l’attente tout en restant saine et gérée. Le journal doit montrer quand le client a cessé d’attendre, qui observe encore et si la fin est arrivée. Sinon, une expiration devient un faux état terminal.

Les réviseurs ont besoin du même vocabulaire que l’exécution. Cherchez les actions avec ssh_command.status nul et workload.state absent, trop longtemps actif ou inconnu. Cherchez les tâches terminales sans approbation de lancement et les soumissions répétées partageant une clé d’idempotence. Ces requêtes transforment la distinction en contrôle capable de trouver des lacunes.

Donnez aux opérateurs une action pour rouvrir le dossier distant. La vue doit montrer la dernière observation, le composant qui l’a fournie et si elle vient d’une requête active ou d’un cache. Ne remplacez jamais unknown par failed pour vider une file. Gardez l’incertitude jusqu’à la réponse du responsable ou la résolution documentée par un réviseur autorisé. Des dossiers restent ouverts après une panne, ce qui est inconfortable et exact.

Testez aussi la rétention. Les dossiers doivent rester interrogeables plus longtemps que la tâche attendue et pendant la période d’audit ou d’enquête. Si le gestionnaire élimine aussitôt les détails d’une unité transitoire, copiez son résultat terminal dans le dossier durable avant collecte. Conserver un ID qu’aucun système ne peut résoudre la semaine suivante apporte une corrélation sans responsabilité.

Exécutez la sonde de douze secondes par le chemin réel de l’agent, puis avec la charge qui compte. Si la carte SSH passe au vert avant l’existence du témoin distant, vous avez trouvé une lacune d’audit. Gardez le zéro, car il constitue une preuve valable sur la commande. Cessez de lui demander de témoigner sur un travail qu’il n’a jamais observé.

FAQ

SSH attend-il la fin des processus en arrière-plan ?

SSH attend la commande distante et la fermeture des données du canal, pas un arbre abstrait de descendants. Un processus qui garde un descripteur ouvert peut retarder le client, mais ce comportement accidentel ne constitue pas une supervision.

Pourquoi ssh renvoie-t-il 0 alors que la tâche continue ?

Le shell distant peut lancer correctement une commande asynchrone puis sortir avec le code 0. SSH rapporte ce code, tandis que la tâche aura son propre résultat plus tard.

nohup place-t-il une commande SSH en arrière-plan ?

Non. nohup modifie le traitement de SIGHUP ; l’opérateur & du shell lance en arrière-plan. Il faut aussi choisir les destinations des trois flux standard.

Pourquoi nohup semble-t-il parfois bloquer SSH ?

Dans une session non interactive, stdout et stderr peuvent être des tubes, donc nohup peut les laisser inchangés. Un descendant qui les garde ouverts retarde la fermeture du canal.

Puis-je attendre un PID distant dans un second appel SSH ?

Un nouveau shell ne peut pas utiliser wait pour un processus qui n’est ni son enfant ni dans sa table. Interrogez un gestionnaire avec un ID durable plutôt que de reconstruire la parenté.

setsid suffit-il pour une tâche détachée fiable ?

setsid crée une nouvelle session et retire le lien au terminal de contrôle. Il n’ajoute ni identité durable, ni stockage d’état, ni reprise, ni annulation complète, ni preuve de succès.

Une commande SSH automatisée doit-elle allouer un pseudo-terminal ?

En général non, sauf si elle exige un comportement de terminal. Le pseudo-terminal modifie signaux, tampons, contrôle des tâches et entrée, et peut masquer des différences avec l’agent.

Que doit consigner l’audit d’une tâche SSH en arrière-plan ?

Consignez séparément acceptation, sortie du canal et fin de la charge. Gardez l’ID distant, l’état terminal, la raison, les heures, la source de fin et l’ID d’action commun.

Comment annuler sans risque un processus distant détaché ?

Demandez au gestionnaire responsable de l’annuler et vérifiez un état cancelled ou failed. Tuer un PID mémorisé est risqué, car des descendants peuvent échapper et le PID être réutilisé.

Quand une commande SSH au premier plan est-elle préférable ?

Utilisez-la pour un travail borné lorsque la connexion peut rester ouverte et que la commande rend le vrai résultat. Prenez un gestionnaire si le travail doit survivre, être interrogé plus tard ou être annulé par groupe.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov