52 lines
2.3 KiB
Python
52 lines
2.3 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
||
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||
|
|
"""Ce qu'est un nom de machine dans ~/.ssh/config, décidé en un seul endroit.
|
||
|
|
|
||
|
|
Trois lecteurs de ce fichier vivent dans ce dépôt — l'énumération des alias,
|
||
|
|
le menu de montage qui veut aussi l'adresse, et l'inventaire des entrées de
|
||
|
|
VM — et ils divergeaient sur la MÊME question. L'un comparait le mot-clé avec
|
||
|
|
la casse, un autre exigeait un espace là où une tabulation est légale, un
|
||
|
|
troisième laissait passer le motif nié. Un alias déclaré `host exo` était donc
|
||
|
|
vu par l'un et invisible aux autres, ce qui se lit comme une panne
|
||
|
|
intermittente de la découverte.
|
||
|
|
|
||
|
|
Le module ne lit aucun fichier et ne suit pas `Include` : il tranche une
|
||
|
|
ligne, rien de plus. Un alias déclaré dans un fichier inclus reste donc
|
||
|
|
invisible aux trois lecteurs, alors même que `ssh -G` le résoudrait — la
|
||
|
|
source est incomplète sans être fausse.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import re
|
||
|
|
|
||
|
|
# Le mot-clé, suivi d'au moins un blanc. ssh_config ne distingue pas la casse
|
||
|
|
# de ses mots-clés, et sépare par espace OU tabulation. Le blanc est exigé
|
||
|
|
# sans quoi `HostName` serait lu comme une déclaration d'hôte.
|
||
|
|
DECLARATION = re.compile(r"^[ \t]*host[ \t]+", re.IGNORECASE)
|
||
|
|
|
||
|
|
|
||
|
|
def declared_names(line) -> list | None:
|
||
|
|
"""Les noms de machine d'une ligne « Host », ou None si ce n'en est pas une.
|
||
|
|
|
||
|
|
Rend None pour toute autre ligne, et une liste — possiblement VIDE —
|
||
|
|
quand la ligne est une déclaration. Distinguer les deux compte : `Host *`
|
||
|
|
EST une déclaration qui ne nomme aucune machine, et un lecteur qui
|
||
|
|
accumule un bloc doit clore le précédent malgré tout. Rendre `[]` dans
|
||
|
|
les deux cas rattachait les directives d'un bloc générique au bloc
|
||
|
|
précédent.
|
||
|
|
|
||
|
|
Trois formes ne désignent aucune machine et sortent : les jokers `*` et
|
||
|
|
`?`, qui décrivent une règle appliquée à plusieurs hôtes, et le motif NIÉ
|
||
|
|
`!nom`, qui RETIRE un nom de l'ensemble que la ligne vient de décrire.
|
||
|
|
Retenir un motif nié rend une cible dont le nom commence par `!`, que ssh
|
||
|
|
ne résoudra jamais.
|
||
|
|
"""
|
||
|
|
if not DECLARATION.match(line or ""):
|
||
|
|
return None
|
||
|
|
return [
|
||
|
|
nom
|
||
|
|
for nom in line.split()[1:]
|
||
|
|
if "*" not in nom and "?" not in nom and not nom.startswith("!")
|
||
|
|
]
|