Compare commits

...

10 commits

Author SHA1 Message Date
Cédrix
a95139d6ae Donner à ces scripts un dépôt qui dit ce qu'ils sont
Ces trois fichiers vivaient dans le dépôt du wiki, seuls fichiers exécutables
parmi cent dix pages de documentation. Le wiki est public et sa branche main
n'est pas protégée ; le serveur de l'install party en recopie deux toutes les
heures et les distribue en root aux machines des participants. Un commit de
documentation et un commit de code y avaient donc les mêmes conséquences, et
rien ne le disait.

Ils sont désormais chez eux, classés par contexte d'exécution — qui les lance,
sur quelle machine, avec quels droits — et non par distribution : « poste/ »
tient les trois ensemble, ce qu'impose de toute façon l'appel du relevé par
install.sh, qui le cherche à côté de lui.

La branche stable est ce que les machines reçoivent, main ce sur quoi on
travaille. Avancer l'une sur l'autre demande d'avoir essayé sur une machine :
c'est le relecteur qui manquait.

L'historique des trois fichiers est conservé, depuis la migration du monorepo
alpinux.site.2026 en mai. Les adresses de téléchargement de leurs en-têtes
sont reprises.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BssvzfDMeXVoUQHEcnavW7
2026-09-25 23:50:38 +02:00
Cédrix
e301327f45 Compter les caractères et non les octets, dans le tableau des disques
Ces trois correctifs avaient été éprouvés sur alpicache jeudi soir — commit
5d606ab du dépôt du serveur, 22 h 56 — cinq minutes après que ce dépôt-ci ait
repris le relevé. Ils n'ont donc jamais été versés ici, et la version amont,
celle que le serveur redistribue, les ignorait. Les voilà.

« printf %-10s » compte les octets : « mécanique » en pèse dix pour neuf
signes, et le tableau des disques glissait d'un cran à partir de la deuxième
colonne. La fonction « colonne » compte les caractères, ce que Bash sait faire
quand la locale le permet — d'où la locale forcée plutôt qu'espérée.

Le contrôle affiche par ailleurs les chiffres sur lesquels il se fonde, et pas
seulement ce qui cloche : modèle, type, taille, état SMART, heures de marche
pour un disque mécanique ou usure pour un SSD, et les trois compteurs de
secteurs. On peut répondre à qui demande comment se porte son disque, même
quand tout va bien.

Cette version est désormais identique, au seul en-tête d'usage près, à celle
qui tourne sur la machine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BssvzfDMeXVoUQHEcnavW7
2026-09-25 23:02:56 +02:00
Alpinux
790874682b Faire voyager le verdict avec la fiche, au lieu de le refaire au serveur
Sur une machine réelle, un HP 250 G4 affichait devant son propriétaire
« NE PAS INSTALLER EN L'ÉTAT — 976 secteurs en attente », et apparaissait
« sain » sur le tableau de bord. La fiche ne transportait que le compteur
de secteurs réalloués, nul sur ce disque, et le serveur rejugeait
là-dessus. Deux juges, deux verdicts, et c'est le rassurant qu'on lisait.

Le verdict part désormais avec la fiche — champs « verdict » et
« motifs », ces derniers débarrassés de leurs couleurs — et le serveur
les affiche sans rien recalculer. Un seul juge, celui qui a vu le disque.

Les trois compteurs SMART cessent d'être confondus : les secteurs
réalloués disent l'usure et se comptent, ceux en attente et les
illisibles disent une panne en cours, et un seul suffit à déclencher
l'alerte. Ce ne sont pas des secteurs fatigués mais des données que le
disque n'arrive plus à relire. Les deux nouveaux compteurs voyagent aussi
dans la fiche.

Le relevé se chronomètre enfin, sur la collecte seule : la pause du
verdict attend quelqu'un devant l'écran, la mesurer reviendrait à
chronométrer la personne.

Constat, correctifs et version de référence : session alpicache.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:56:13 +02:00
Alpinux
7795ad3158 Reprendre le relevé éprouvé sur machine, avec ses trois correctifs
La session alpicache a essayé le relevé sur une machine réelle et en a
tiré trois défauts, tous vérifiés là-bas :

Le verdict ne sortait qu'en session live. Relancé après installation, le
relevé ne disait donc rien de l'état des disques — précisément ce dont on
voulait parler au propriétaire pendant qu'il est encore là. Il sort
maintenant à tous les coups, sauf en mode --montrer.

Un disque mécanique n'était signalé qu'au-delà de cinq ans de marche, ce
qui laissait passer sans un mot un disque de trois ans — alors que c'est
le premier frein d'une machine par ailleurs correcte. Il se signale
désormais quel que soit son âge, qui est simplement rappelé.

Le verdict défilait sans que personne ne le lise : trois secondes quand
tout va bien, vingt avec des réserves, et il attend une touche — deux
minutes au plus — quand il dit de ne pas installer. Jamais indéfiniment,
ni du tout sans terminal : le script tourne aussi appelé par install.sh.

La détection de session live est reprise dans sa version, plus fine que
celle que j'avais faite : /cdrom et /rofs comptent de nouveau, mais
seulement montés, et pour /cdrom seulement avec un système de fichiers
de média. Un répertoire vide ne prouve plus rien.

Seul l'en-tête reste de moi : le script se prend dans ce dépôt, pas sur
le serveur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:41:19 +02:00
Alpinux
b5efc63600 alpi-fiche : ne plus prendre un /cdrom vide pour une session live
L'installateur de Mint laisse derrière lui un /cdrom vide sur la machine
installée. La détection s'y fiait, et déclarait donc « session live » des
machines parfaitement installées — ce qui fausse aussi le chronométrage,
puisqu'il se calcule entre les deux relevés.

Restent les deux signes qui ne mentent pas : la racine montée en overlay
ou squashfs, et boot=casper sur la ligne du noyau. /rofs disparaît avec
/cdrom, n'ayant pas plus de valeur probante.

Constat de la session alpicache, sur une machine réelle. Je n'ai pas pu
le reproduire moi-même — la machine d'essai ne se laissait pas piloter —
mais le changement est sûr dans les deux sens : ces deux signes-là sont
présents sur toute session live, et aucun ne l'est sur un système
installé.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:38:04 +02:00
Alpinux
d1f400a599 Verser le relevé de fiche matérielle dans le dépôt
La demande initiale faisait télécharger un script depuis alpicache et
l'exécuter en root sur la machine de chaque participant. Le relevé est
désormais versionné ici, relu comme le reste, et appelé localement : rien
n'est tiré du réseau, rien n'est exécuté sans avoir été publié.

Le script vient de la session alpicache, éprouvé sur place. Il relève le
modèle, le processeur, la mémoire et le détail de ses connecteurs, les
disques et leur état SMART, puis envoie la fiche au serveur de la salle.
Depuis une session live, il rend en plus un verdict — bon, avec réserves,
ou ne pas installer — que le bénévole peut opposer à un disque qui
faiblit pendant que la machine est encore là.

install.sh l'appelle en tête de fichier, dans sa passe root, et seulement
s'il le trouve à côté de lui : une machine qui rejoue le script chez son
propriétaire n'envoie rien à personne.

L'en-tête du relevé disait « rien qui touche à la personne ». C'était
inexact : la fiche emporte le nom de la machine — souvent un prénom — et
l'adresse matérielle de la carte réseau. Les deux sont maintenant
nommés, dans le script comme dans le guide, avec la consigne qui va
avec : le dire à la personne dont on installe l'ordinateur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:37:36 +02:00
49c84c04f5 Ne pas régénérer GRUB quand il est déjà configuré (#15) 2026-09-24 20:34:59 +00:00
Cédrix
a25488101e Ne pas régénérer GRUB quand il est déjà configuré
update-grub lance os-prober, qui sonde toutes les partitions à la recherche
d'autres systèmes, puis régénère la configuration entière : vingt à trente
secondes par machine, payées jusqu'ici même lorsqu'il n'y avait rien à changer.
Sur une install party de quinze machines, cela se voit.

La fonction vérifie donc d'abord, et sort si les deux réglages sont là.

Deux défauts corrigés au passage. Le sed insérait ses lignes avec
l'indentation du script : /etc/default/grub se retrouvait avec des directives
décalées, que GRUB accepte mais qui trompent la lecture quand on vient
déboguer. Et il ne faisait rien du tout si « GRUB_DEFAULT=0 » avait disparu,
ce qui arrive dès qu'un autre outil est passé avant nous — les deux lignes sont
désormais ajoutées si elles manquent, plutôt qu'insérées avant une ligne qui
peut ne plus exister.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XLTJ8DoPfvhZMK5Yxtrfx2
2026-09-24 22:34:39 +02:00
Alpinux
1c19aee031 install.sh : afficher le saut de ligne au lieu de l'écrire
« ==> Mise à jour du système\n » s'affichait tel quel, barre-n comprise,
sur l'écran des participants : le echo de cette ligne avait perdu son
-e. Relevé pendant une installation ce soir, à deux jours de l'install
party.

Au passage, « Configuraiton de dconf » devient une ligne de la même
famille que les autres — préfixe ==> et orthographe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-24 22:07:03 +02:00
Alpinux
0a4d321478 install.sh : appeler modif_grub, qui ne l'était pas
Script joué de bout en bout sur une Mint 22.3 fraîchement installée.
Il se termine bien — polices Microsoft sans intervention, logiciels
installés, swappiness à 10, auto-destruction — mais deux choses ne
faisaient pas ce qu'elles annoncent.

modif_grub était définie et jamais appelée : après exécution,
/etc/default/grub contenait toujours GRUB_DEFAULT=0. Le réglage annoncé
— redémarrer sur la dernière entrée utilisée — n'avait donc jamais lieu.
L'appel est ajouté.

opti_fstab, elle, s'exécute sans effet : le fstab écrit par Mint 22.3 ne
contient pas « relatime », donc la substitution ne trouve rien. Constat
documenté dans la fonction plutôt que corrigé à l'aveugle, toucher aux
options de montage méritant une décision explicite.

GREEN et NC étaient utilisés sans être définis : les messages de fin
sortaient sans couleur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BE4rvHVETRoWnYNDTGssdo
2026-09-20 09:12:22 +02:00
4 changed files with 834 additions and 9 deletions

69
README.md Normal file
View file

@ -0,0 +1,69 @@
# alpinux.scripts
Les scripts qu'Alpinux fait tourner sur des machines qui ne sont pas les
siennes. Ils sont publics parce qu'ils doivent pouvoir se télécharger sans
compte, depuis une session live, par quelqu'un qui découvre Linux le matin
même — et parce que du code qui s'exécute en root chez autrui doit pouvoir
être lu par celui qui le lance.
La documentation, elle, vit dans le wiki :
[guide Linux Mint depuis Windows](https://wiki.alpinux.org/guides/linux-mint-depuis-windows/).
## Ce que contient chaque dossier
Le classement suit **qui lance le script, sur quelle machine, avec quels
droits** — c'est ce qui commande la prudence à lui appliquer. Il ne suit ni les
distributions, ni les dates : un script ne change pas de nature quand Mint
change de version.
| Dossier | Ce qui y entre |
|---|---|
| `poste/` | Ce qui tourne sur la machine d'un participant, le plus souvent en root. Rien n'y entre sans avoir été essayé sur une vraie machine. |
D'autres dossiers viendront — la part publiable des scripts du serveur, des
outils de bénévole sans privilège. Chacun avec sa règle, écrite ici.
## `poste/` — trois scripts, dans cet ordre
| Script | Quand | Ce qu'il fait |
|---|---|---|
| `verif-disque.sh` | depuis la session live, **avant** de toucher au disque | lit et affiche : partitions, Windows, BitLocker, réseau. Ne modifie rien. |
| `alpi-fiche.sh` | avant (`--check`) et après l'installation | relève la fiche matérielle, juge l'état des disques, l'envoie au serveur de l'install party s'il est joignable. |
| `install.sh` | après l'installation de Mint | post-installation : paquets, Firefox en français, GRUB. Appelle `alpi-fiche.sh` s'il le trouve. |
**Les trois vont ensemble, dans le même dossier.** `install.sh` cherche le
relevé à côté de lui — `$(dirname "$0")/alpi-fiche.sh` — et ne le télécharge
jamais : aucun code n'est tiré du réseau pendant une installation. Un
`install.sh` téléchargé seul fonctionne, mais ne relève rien, et sans le dire.
Sur le réseau d'une install party, le serveur les sert tous les trois :
```bash
wget http://10.0.0.1/install.sh http://10.0.0.1/alpi-fiche.sh
chmod +x install.sh alpi-fiche.sh
sudo ./install.sh
```
Ailleurs, l'en-tête de chaque script donne son adresse de téléchargement.
## `main` et `stable`
`main` est la branche de travail. **`stable` est celle que les machines
reçoivent** : c'est elle que le serveur de l'install party recopie et
redistribue, sans que personne ne la relise au passage.
On avance donc `stable` sur `main` quand le changement a été essayé sur une
machine — pas quand il a été écrit. Sans cette règle, un commit de vingt-deux
heures part en root sur le poste d'un inconnu avant minuit.
```bash
git switch stable && git merge --ff-only main && git push
```
## Ce qui a déjà mordu
Un fichier corrigé sur le serveur sans être poussé ici est un fichier perdu :
le serveur se resynchronise toutes les heures et rétablit cette version-ci.
C'est arrivé le 24 septembre 2026, et les correctifs ne se sont retrouvés que
parce qu'ils vivaient aussi dans le dépôt du serveur. On corrige ici, on
déploie ensuite.

702
poste/alpi-fiche.sh Executable file
View file

@ -0,0 +1,702 @@
#!/bin/bash
# Relève la fiche d'une machine, et l'envoie au serveur de l'install party.
#
# Ce script vit dans alpinux.scripts, à côté d'install.sh : il est lu et
# versionné comme lui, et c'est de là qu'il faut le prendre.
#
# wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/alpi-fiche.sh
# chmod +x alpi-fiche.sh
# sudo ./alpi-fiche.sh --check # depuis la session live, avant d'installer
# sudo ./alpi-fiche.sh # après l'installation
#
# Sur le réseau de l'install party, plus court :
#
# wget http://10.0.0.1/alpi-fiche.sh && chmod +x alpi-fiche.sh && sudo ./alpi-fiche.sh --check
#
# Pourquoi la garder sur le serveur plutôt que sur le poste : trois semaines
# plus tard, quand quelqu'un rappelle parce que « ça rame », on n'a plus la
# machine sous la main. Savoir qu'elle a huit ans, quatre gigaoctets de
# mémoire et un disque mécanique qui commence à réallouer des secteurs répond
# à la question avant de l'avoir posée.
#
# Ce qui est envoyé : le modèle, le processeur, la mémoire, les disques et
# leur santé, le système installé.
#
# Deux de ces champs touchent à la personne, et il faut le dire plutôt que de
# prétendre le contraire — la première version de cet en-tête annonçait « rien
# qui touche à la personne », ce qui était faux :
#
# · le NOM DE LA MACHINE, choisi pendant l'installation, contient très
# souvent un prénom ;
# · l'ADRESSE MATÉRIELLE de la carte réseau identifie durablement l'appareil.
#
# Les deux servent : le nom pour s'y retrouver, l'adresse pour rapprocher la
# fiche du bail DHCP. Mais ils appartiennent à la personne dont on installe
# l'ordinateur — il faut le lui dire. Ne sont en revanche jamais envoyés : son
# nom d'utilisateur, ses fichiers, son historique, ni rien de ce qu'elle a
# installé. Le numéro de série est tronqué à quatre caractères.
#
# alpi-fiche relève et envoie
# alpi-fiche --montrer relève et affiche, sans rien envoyer
# alpi-fiche --check relève, envoie, et rend un verdict
#
# Le mode « --check » est fait pour tourner **depuis la clé USB, avant
# d'installer** : c'est le seul moment où l'on peut encore renoncer. Un disque
# qui se déclare défaillant, on ne passe pas une heure à installer dessus — on
# le dit à son propriétaire et on cherche une autre solution.
set -u
# Bash ne compte les caractères — et non les octets — que si la locale le dit.
# C'est ce qui aligne les colonnes du tableau des disques.
export LC_ALL="${LC_ALL:-C.UTF-8}"
# Le temps que met la collecte, en millisecondes.
#
# Une machine n'est relevée qu'une fois quand elle n'a pas connu de session
# live : il n'y a alors pas d'écart entre deux passages à mesurer. Cette durée-
# ci existe toujours, et elle se compare — elle dit ce que la machine met à
# s'interroger elle-même : lire le SMART de ses disques, réveiller ceux qui
# dorment, énumérer ses barrettes. Un plateau qui rame se voit là.
#
# On arrête le chronomètre à la fin de la collecte, avant le verdict : celui-ci
# attend une touche, et l'on mesurerait la personne devant l'écran.
DEBUT_NS="$(date +%s%N 2>/dev/null || echo 0)"
# smartctl et dmidecode vivent dans /usr/sbin, que le PATH d'un shell ordinaire
# ne contient pas toujours — un « command -v » y échouait alors que le binaire
# était là.
PATH="$PATH:/usr/sbin:/sbin"
SERVEUR="${ALPI_SERVEUR:-http://alpicache.lan}"
MONTRER=0
CHECK=0
case "${1:-}" in
--montrer) MONTRER=1 ;;
--check) CHECK=1 ;;
esac
if [ -t 1 ]; then
V=$'\e[32m'; O=$'\e[33m'; R=$'\e[31m'; B=$'\e[1m'; D=$'\e[2m'; Z=$'\e[0m'
else
V=""; O=""; R=""; B=""; D=""; Z=""
fi
# Ce dont le relevé a besoin, et qu'il installe lui-même plutôt que de rendre
# un verdict à trous.
#
# « état des disques inconnu » n'aide personne : c'est précisément la réponse
# qu'on était venu chercher. Les deux paquets pèsent moins de deux mégaoctets,
# ils sont dans le cache, et l'installation prend deux secondes sur le réseau
# de l'install party. On ne le fait qu'en root, qu'avec apt, et sans rien
# demander d'autre.
completer_outils() {
[ "$(id -u)" = "0" ] || return 0
command -v apt-get >/dev/null 2>&1 || return 0
local manquants=""
command -v smartctl >/dev/null 2>&1 || manquants="$manquants smartmontools"
command -v dmidecode >/dev/null 2>&1 || manquants="$manquants dmidecode"
[ -z "$manquants" ] && return 0
echo "==> Installation de ce qui manque au relevé :$manquants"
# Par le cache s'il répond, directement sinon. « -qq » pour ne pas noyer
# l'écran : ce n'est pas l'objet de la manœuvre.
local proxy=""
if command -v wget >/dev/null 2>&1 \
&& wget -q --spider --timeout=3 "$SERVEUR/" 2>/dev/null; then
proxy="-o Acquire::http::Proxy=http://apt-proxy:3142"
fi
DEBIAN_FRONTEND=noninteractive apt-get $proxy -qq -y install $manquants \
>/dev/null 2>&1 \
|| echo " (échec — le relevé se fera sans)"
# Ce qui vient d'arriver est dans /usr/sbin, que le PATH d'un shell
# ordinaire ne contient pas toujours.
PATH="$PATH:/usr/sbin:/sbin"
}
completer_outils
lire_dmi() {
cat "/sys/class/dmi/id/$1" 2>/dev/null | tr -d '\n' | sed 's/^ *//; s/ *$//'
}
echapper() {
# De quoi écrire du JSON sans rien installer : guillemets, antislashs et
# sauts de ligne, et c'est tout ce qui peut nous arriver ici.
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr -d '\n\r'
}
champ() { printf '"%s":"%s"' "$1" "$(echapper "${2:-}")"; }
# --- La machine ------------------------------------------------------------
MODELE="$(lire_dmi product_name)"
MARQUE="$(lire_dmi sys_vendor)"
SERIE="$(lire_dmi product_serial)"
# Le numéro de série identifie un objet, pas une personne, mais il n'a rien à
# faire dans un fichier qu'on partage : on n'en garde que les quatre derniers
# caractères, assez pour distinguer deux machines du même modèle.
SERIE="${SERIE: -4}"
BIOS="$(lire_dmi bios_date)"
PROCESSEUR="$(awk -F': ' '/^model name/{print $2; exit}' /proc/cpuinfo)"
COEURS="$(nproc 2>/dev/null || echo '?')"
MEMOIRE_KO="$(awk '/^MemTotal/{print $2}' /proc/meminfo)"
SYSTEME=""
if [ -r /etc/os-release ]; then
SYSTEME="$(. /etc/os-release; echo "${PRETTY_NAME:-$NAME $VERSION}")"
fi
NOYAU="$(uname -r)"
ARCHITECTURE="$(uname -m)"
# Sommes-nous sur la clé USB, ou sur le système installé ?
#
# La distinction n'est pas cosmétique : les deux relevés répondent à deux
# questions différentes. Celui de la session live dit « peut-on installer sur
# cette machine ? » et se fait avant d'effacer quoi que ce soit. Celui d'après
# dit « qu'avons-nous installé, et sur quoi ». Les garder séparés permet de
# comparer — un disque qui allait bien le matin et qui se plaint le soir, cela
# se voit.
#
# Le signe qui ne trompe pas est la racine : une session live la monte en
# overlay au-dessus d'un squashfs, ce qu'aucun système installé ne fait. S'y
# ajoute « boot=casper » ou « boot=live » sur la ligne de commande du noyau.
#
# L'existence de /cdrom ou de /rofs, elle, ne prouve rien : l'installateur de
# Mint laisse un /cdrom vide derrière lui, et la première version de ce script
# a donc déclaré « session live » une machine parfaitement installée. On ne les
# retient que s'ils sont réellement montés.
MOMENT="post-installation"
if findmnt -n -o FSTYPE / 2>/dev/null | grep -qE '^(overlay|aufs|squashfs)$' \
|| grep -qE '(^| )boot=(casper|live)( |$)' /proc/cmdline 2>/dev/null \
|| mountpoint -q /rofs 2>/dev/null \
|| { mountpoint -q /cdrom 2>/dev/null \
&& findmnt -n -o FSTYPE /cdrom 2>/dev/null | grep -qE '^(iso9660|squashfs)$'; }; then
MOMENT="live"
fi
# --- La mémoire, barrette par barrette ------------------------------------
#
# La question qu'on pose vraiment devant une machine lente : peut-on en
# ajouter ? Elle demande trois réponses — combien de connecteurs, combien
# d'occupés, et de quel type. Un slot libre en DDR3 ne se remplit pas avec ce
# qu'on a dans le tiroir.
#
# dmidecode lit cela dans la table du BIOS. Il est présent par défaut sur
# Debian, Ubuntu et Mint ; s'il manque, on ne prétend rien plutôt que de
# deviner.
BARRETTES="["
SLOTS_TOTAL=0
SLOTS_PRIS=0
TYPE_MEMOIRE=""
VITESSE_MEMOIRE=""
if command -v dmidecode >/dev/null 2>&1 && [ "$(id -u)" = "0" ]; then
premier=1
# Chaque bloc « Memory Device » décrit un connecteur, occupé ou non.
while IFS= read -r bloc; do
emplacement="$(printf '%s' "$bloc" | sed -n 's/.*|Locator:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
taille="$(printf '%s' "$bloc" | sed -n 's/.*|Size:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
type_="$(printf '%s' "$bloc" | sed -n 's/.*|Type:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
vitesse="$(printf '%s' "$bloc" | sed -n 's/.*|Speed:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
forme="$(printf '%s' "$bloc" | sed -n 's/.*|Form Factor:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
reference="$(printf '%s' "$bloc" | sed -n 's/.*|Part Number:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
# La vitesse configurée est celle à laquelle la barrette travaille
# vraiment ; « Speed » n'est que ce dont elle serait capable. L'écart
# entre les deux explique bien des machines qui « rament » sans raison
# apparente : une DDR4-3200 bridée à 2133 par la carte mère, ou deux
# barrettes dépareillées qui s'alignent sur la plus lente.
vitesse_reelle="$(printf '%s' "$bloc" | sed -n 's/.*|Configured Memory Speed:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
fabricant="$(printf '%s' "$bloc" | sed -n 's/.*|Manufacturer:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
rang="$(printf '%s' "$bloc" | sed -n 's/.*|Rank:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
tension="$(printf '%s' "$bloc" | sed -n 's/.*|Configured Voltage:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
largeur="$(printf '%s' "$bloc" | sed -n 's/.*|Data Width:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
detail="$(printf '%s' "$bloc" | sed -n 's/.*|Type Detail:\([^|]*\).*/\1/p' | sed 's/^ *//; s/ *$//')"
[ -z "$emplacement" ] && continue
SLOTS_TOTAL=$(( SLOTS_TOTAL + 1 ))
case "$taille" in
*"No Module"*|""|"Unknown") taille="" ;;
*) SLOTS_PRIS=$(( SLOTS_PRIS + 1 ))
[ -z "$TYPE_MEMOIRE" ] && [ "$type_" != "Unknown" ] && TYPE_MEMOIRE="$type_"
# Deux barrettes de vitesses différentes s'alignent sur la plus
# lente : c'est celle-là qu'il faut retenir pour la machine.
if [ -n "$vitesse_reelle" ] && [ "$vitesse_reelle" != "Unknown" ]; then
nombre="${vitesse_reelle%% *}"
case "$nombre" in
''|*[!0-9]*) ;;
*) if [ -z "$VITESSE_MEMOIRE" ] || [ "$nombre" -lt "$VITESSE_MEMOIRE" ]; then
VITESSE_MEMOIRE="$nombre"
fi ;;
esac
fi ;;
esac
[ "$premier" = 0 ] && BARRETTES="$BARRETTES,"
premier=0
BARRETTES="$BARRETTES{$(champ emplacement "$emplacement"),$(champ taille "$taille"),\
$(champ type "$type_"),$(champ vitesse "$vitesse"),$(champ vitesse_reelle "$vitesse_reelle"),\
$(champ forme "$forme"),$(champ reference "$reference"),$(champ fabricant "$fabricant"),\
$(champ rang "$rang"),$(champ tension "$tension"),$(champ largeur "$largeur"),\
$(champ detail "$detail")}"
done < <(dmidecode -t memory 2>/dev/null \
| awk '/^Memory Device$/{bloc=""; dedans=1; next}
/^$/{if (dedans && bloc != "") print bloc; dedans=0; bloc=""}
dedans {gsub(/^[ \t]+/, ""); bloc = bloc "|" $0}
END{if (dedans && bloc != "") print bloc}')
fi
BARRETTES="$BARRETTES]"
TABLE_DISQUES=""
# Remplir une colonne à la bonne largeur.
#
# « printf %-10s » compte les octets, pas les caractères : « mécanique » en
# pèse dix pour neuf signes, et la colonne suivante glisse d'un cran. Bash,
# lui, sait compter les caractères quand la locale est en UTF-8 — on lui
# laisse ce travail, et l'on force la locale plutôt que d'espérer.
colonne() {
local texte="$1" largeur="$2" cote="${3:-gauche}" n
n=${#texte}
if [ "$n" -gt "$largeur" ]; then
printf '%s' "${texte:0:$largeur}"
return
fi
local blancs=""
while [ "$n" -lt "$largeur" ]; do blancs="$blancs "; n=$((n + 1)); done
if [ "$cote" = droite ]; then printf '%s%s' "$blancs" "$texte"
else printf '%s%s' "$texte" "$blancs"; fi
}
# --- Les disques -----------------------------------------------------------
#
# lsblk donne la taille et le type ; le drapeau « rotational » du noyau
# distingue un disque mécanique d'un SSD sans rien installer. C'est la
# distinction qui compte : une machine lente avec un disque mécanique de dix
# ans ne s'arrangera pas avec un système plus léger.
DISQUES="["
premier=1
for chemin in /sys/block/*; do
nom="$(basename "$chemin")"
case "$nom" in loop*|ram*|sr*|zram*|dm-*) continue ;; esac
[ -r "$chemin/size" ] || continue
taille=$(( $(cat "$chemin/size") * 512 ))
[ "$taille" -lt 1000000000 ] && continue # moins d'un Go : pas un disque
rotatif="$(cat "$chemin/queue/rotational" 2>/dev/null || echo '?')"
modele="$(cat "$chemin/device/model" 2>/dev/null | sed 's/ *$//')"
# L'état SMART, s'il est lisible. Sans smartctl on ne prétend rien.
sante=""
reallocations=""
pending=""
illisibles=""
heures=""
usure=""
ecrit=""
if command -v smartctl >/dev/null 2>&1; then
sortie="$(smartctl -H -A "/dev/$nom" 2>/dev/null)"
sante="$(printf '%s' "$sortie" | grep -iE 'overall-health|SMART Health' \
| sed 's/.*: *//' | head -1)"
reallocations="$(printf '%s' "$sortie" \
| awk '/Reallocated_Sector_Ct|Reallocated_Event/{print $10; exit}')"
# Les secteurs que le disque n'arrive plus à lire. Ils manquaient à la
# fiche, et c'est ce qui a fait qu'alpicache annonçait « sain » un
# disque déclaré à remplacer : il ne les avait jamais reçus.
pending="$(printf '%s' "$sortie" | awk '$2 == "Current_Pending_Sector"{print $10; exit}')"
illisibles="$(printf '%s' "$sortie" | awk '$2 == "Offline_Uncorrectable"{print $10; exit}')"
heures="$(printf '%s' "$sortie" | awk '/Power_On_Hours/{print $10; exit}')"
# L'usure d'un SSD, en pour cent de vie consommée.
#
# Un disque mécanique s'use en tournant, un SSD en écrivant : les
# heures de marche ne disent rien de son état. Chaque constructeur a
# son attribut, et deux conventions coexistent — les uns comptent ce
# qui est consommé, les autres ce qui reste. On ramène tout à
# « consommé », qui est le sens qu'on veut lire. NVMe donne directement
# « Percentage Used » ; pour les SATA, la colonne VALUE part de 100 et
# descend.
usure="$(printf '%s' "$sortie" | awk '/Percentage Used:/{gsub(/[^0-9]/,"",$3); print $3; exit}')"
if [ -z "$usure" ]; then
usure="$(printf '%s' "$sortie" | awk '
/Media_Wearout_Indicator|Wear_Leveling_Count|SSD_Life_Left|Percent_Lifetime_Remain/ {
if ($4 ~ /^[0-9]+$/ && $4 <= 100) { print 100 - $4; exit }
}')"
fi
# Ce que le disque a écrit depuis sa mise en service : l'autre façon de
# juger un SSD, dont les modèles courants tiennent 100 à 600 To.
ecrit="$(printf '%s' "$sortie" | awk '
/Total_LBAs_Written|Host_Writes_32MiB|Total_Host_Writes/ {
if ($10 ~ /^[0-9]+$/) { print $10; exit }
}
/Data Units Written:/ { gsub(/[^0-9]/,"",$4); print $4; exit }')"
fi
# Les mêmes chiffres, mis en table pour être lus par quelqu'un. Le verdict
# ne dit que ce qui cloche ; on veut aussi voir ce qui va, et sur quoi le
# verdict se fonde.
taille_go="$(awk -v o="$taille" 'BEGIN{printf "%.1f", o/1000000000}' | tr . ,)"
secteurs="—"
for valeur in "${pending:-0}" "${illisibles:-0}" "${reallocations:-0}"; do
case "$valeur" in ''|*[!0-9]*) continue ;; esac
[ "$valeur" -gt 0 ] && secteurs="!"
done
[ "$secteurs" = "!" ] && secteurs="$(printf '%s/%s/%s' \
"${reallocations:-0}" "${pending:-0}" "${illisibles:-0}")"
usure_dite="—"
if [ "$rotatif" = 1 ]; then
case "$heures" in ''|*[!0-9]*) ;; *)
usure_dite="$heures h"
[ "$heures" -ge 8760 ] && usure_dite="$usure_dite ($(( heures / 8760 )),$(( (heures % 8760) * 10 / 8760 )) an)" ;;
esac
else
case "$usure" in ''|*[!0-9]*) ;; *) usure_dite="usé à $usure %" ;; esac
fi
TABLE_DISQUES="$TABLE_DISQUES$(
colonne "$nom" 7; printf ' '
colonne "${modele:-—}" 20; printf ' '
colonne "$([ "$rotatif" = 1 ] && echo mécanique || echo SSD)" 10; printf ' '
colonne "$taille_go Go" 10 droite; printf ' '
colonne "${sante:-—}" 8; printf ' '
colonne "$usure_dite" 16; printf ' '
printf '%s' "$secteurs")"$'\n'
[ "$premier" = 0 ] && DISQUES="$DISQUES,"
premier=0
DISQUES="$DISQUES{$(champ nom "$nom"),$(champ modele "$modele"),\
\"octets\":$taille,$(champ type "$([ "$rotatif" = 1 ] && echo mécanique || echo SSD)"),\
$(champ sante "$sante"),$(champ reallocations "$reallocations"),\
$(champ pending "$pending"),$(champ illisibles "$illisibles"),$(champ heures "$heures"),\
$(champ usure "$usure"),$(champ ecrit "$ecrit")}"
done
DISQUES="$DISQUES]"
# L'occupation, telle qu'elle est maintenant.
OCCUPATION="["
premier=1
while read -r point taille utilise dispo pourcent; do
[ "$premier" = 0 ] && OCCUPATION="$OCCUPATION,"
premier=0
OCCUPATION="$OCCUPATION{$(champ point "$point"),\"taille\":$taille,\
\"utilise\":$utilise,$(champ pourcent "$pourcent")}"
done < <(df -B1 --output=target,size,used,avail,pcent 2>/dev/null \
| awk 'NR>1 && $2+0 > 1000000000 {print $1, $2, $3, $4, $5}')
OCCUPATION="$OCCUPATION]"
# --- Le rapport ------------------------------------------------------------
# L'adresse matérielle de l'interface par laquelle on sort — celle que le
# serveur a vue en donnant le bail, donc celle qui permettra de rapprocher
# cette fiche de la machine servie. « ip -o link » a failli nous donner
# l'adresse de diffusion, qui est la même pour tout le monde.
INTERFACE="$(ip -o -4 route show default 2>/dev/null | awk '{print $5; exit}')"
[ -z "$INTERFACE" ] && INTERFACE="$(ls /sys/class/net 2>/dev/null \
| grep -v '^lo$' | head -1)"
MAC="$(cat "/sys/class/net/$INTERFACE/address" 2>/dev/null)"
RAPPORT="{$(champ machine "$(hostname)"),$(champ mac "$MAC"),\
$(champ marque "$MARQUE"),$(champ modele "$MODELE"),$(champ serie "$SERIE"),\
$(champ bios "$BIOS"),$(champ processeur "$PROCESSEUR"),\"coeurs\":${COEURS:-0},\
\"memoire_ko\":${MEMOIRE_KO:-0},$(champ systeme "$SYSTEME"),$(champ noyau "$NOYAU"),\
$(champ architecture "$ARCHITECTURE"),$(champ moment "$MOMENT"),\
$(champ type_memoire "$TYPE_MEMOIRE"),\
$(champ vitesse_memoire "$VITESSE_MEMOIRE"),\
\"slots_total\":$SLOTS_TOTAL,\"slots_pris\":$SLOTS_PRIS,\"barrettes\":$BARRETTES,\
\"disques\":$DISQUES,\"occupation\":$OCCUPATION}"
DUREE_MS=0
case "$DEBUT_NS" in
''|*[!0-9]*) ;;
0) ;;
*) FIN_NS="$(date +%s%N 2>/dev/null || echo 0)"
case "$FIN_NS" in
''|*[!0-9]*|0) ;;
*) DUREE_MS=$(( (FIN_NS - DEBUT_NS) / 1000000 )) ;;
esac ;;
esac
[ "$DUREE_MS" -lt 0 ] && DUREE_MS=0
RAPPORT="${RAPPORT%\}},\"duree_releve_ms\":$DUREE_MS}"
if [ "$MONTRER" = 1 ]; then
if command -v python3 >/dev/null 2>&1; then
printf '%s' "$RAPPORT" | python3 -m json.tool 2>/dev/null || printf '%s\n' "$RAPPORT"
else
printf '%s\n' "$RAPPORT"
fi
exit 0
fi
[ "$CHECK" = 1 ] && printf " %sLa fiche est envoyée à alpicache…%s\n\n" "$D" "$Z"
echo "Relevé : $([ "$MOMENT" = live ] && echo "session live, avant installation" \
|| echo "système installé")"
echo "Machine : $MARQUE $MODELE"
echo "Processeur : $PROCESSEUR ($COEURS cœurs)"
if [ "$SLOTS_TOTAL" -gt 0 ]; then
echo "Mémoire : $(( MEMOIRE_KO / 1024 )) Mo ${TYPE_MEMOIRE:+$TYPE_MEMOIRE }\
${VITESSE_MEMOIRE:+à $VITESSE_MEMOIRE MT/s }· \
$SLOTS_PRIS connecteur(s) occupé(s) sur $SLOTS_TOTAL"
else
echo "Mémoire : $(( MEMOIRE_KO / 1024 )) Mo"
fi
echo "Système : $SYSTEME"
echo
MOTIFS=""
# --- Le verdict ------------------------------------------------------------
#
# Trois réponses possibles, et une seule question : peut-on installer sur cette
# machine ? Les seuils viennent de ce qu'on rencontre en install party, pas
# d'un idéal — une machine de dix ans avec 4 Go et un disque sain fait très
# bien l'affaire, et c'est souvent celle qu'on nous apporte.
# Le verdict sort toujours, live ou non. C'est l'information qu'on est venu
# chercher : la première version ne le rendait qu'en session live, et le
# relevé d'après installation défilait sans rien dire de l'état des disques —
# précisément ce dont on voulait parler au propriétaire.
[ "$MONTRER" = 1 ] || CHECK=1
if [ "$CHECK" = 1 ]; then
GRAVITE=0 # 0 rien à signaler · 1 à surveiller · 2 ne pas installer
REMARQUES=""
note() {
[ "$1" -gt "$GRAVITE" ] && GRAVITE="$1"
REMARQUES="$REMARQUES$2"$'\n'
# Le même motif, débarrassé de ses couleurs et de ses retours à la
# ligne, pour qu'alpicache dise exactement ce qui a été dit ici.
if [ "$1" -ge 1 ]; then
# Une note tient sur plusieurs lignes à l'écran ; le motif, lui,
# est d'un seul tenant — il ira dans une case de tableau.
nu="$(printf '%s' "$2" | tr '\n' ' ' | sed -e 's/\x1b\[[0-9;]*m//g' \
-e 's/[[:space:]]\{1,\}/ /g' \
-e 's/^ *[!?✗·] *//' \
-e 's/^ *//' -e 's/ *$//')"
[ -n "$nu" ] && MOTIFS="$MOTIFS$nu"$'\n'
fi
}
# Les disques. C'est pour eux qu'on fait ce contrôle.
for chemin in /sys/block/*; do
nom="$(basename "$chemin")"
case "$nom" in loop*|ram*|sr*|zram*|dm-*) continue ;; esac
[ -r "$chemin/size" ] || continue
taille=$(( $(cat "$chemin/size") * 512 ))
[ "$taille" -lt 1000000000 ] && continue
if ! command -v smartctl >/dev/null 2>&1; then
note 1 " ${O}?${Z} état des disques inconnu — smartmontools n'est pas installé.
${D}sudo apt install -y smartmontools${Z} ${D}(il passera par le cache)${Z}"
break
fi
sortie="$(smartctl -H -A "/dev/$nom" 2>/dev/null)"
if printf '%s' "$sortie" | grep -qiE 'overall-health.*(FAILED|FAILING)'; then
note 2 " ${R}✗${Z} ${B}/dev/$nom se déclare défaillant${Z} — ne pas installer dessus."
fi
# Les trois attributs ne disent pas la même chose, et les confondre
# revient à sous-estimer les deux qui comptent.
#
# Reallocated_Sector_Ct des secteurs déjà remplacés par la réserve
# du disque. Quelques-uns sont de l'usure
# ordinaire ; c'est leur nombre qui parle.
# Current_Pending_Sector des secteurs que le disque n'arrive plus à
# lire et n'a pas encore pu déplacer. Ce
# qu'ils contenaient est en train d'être
# perdu. Un seul suffit à s'inquiéter.
# Offline_Uncorrectable des secteurs définitivement illisibles.
#
# Les deux derniers ne sont pas de l'usure mais une panne en cours :
# on ne les mesure pas à la même aune que le premier.
for attribut in Reallocated_Sector_Ct Current_Pending_Sector Offline_Uncorrectable; do
valeur="$(printf '%s' "$sortie" | awk -v a="$attribut" '$2 == a {print $10; exit}')"
case "$valeur" in ''|*[!0-9]*) continue ;; esac
[ "$valeur" = "0" ] && continue
case "$attribut" in
Reallocated_Sector_Ct)
libelle="secteur(s) déjà remplacé(s)"
if [ "$valeur" -ge 50 ]; then
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur $libelle${Z} — disque à remplacer."
else
note 1 " ${O}!${Z} /dev/$nom : $valeur $libelle — à surveiller."
fi ;;
Current_Pending_Sector)
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur secteur(s) illisible(s)${Z} en attente.
${R}Ce ne sont pas des secteurs usés mais des données que le disque
n'arrive plus à relire : le disque est en train de lâcher.${Z}
${D}Sauvegarder ce qui doit l'être avant toute autre chose, et
ne rien installer dessus — l'installation ne tiendrait pas.${Z}" ;;
Offline_Uncorrectable)
note 2 " ${R}✗${Z} /dev/$nom : ${B}$valeur secteur(s) définitivement perdu(s)${Z}.
${R}Le disque a renoncé à les relire. À remplacer.${Z}" ;;
esac
done
heures="$(printf '%s' "$sortie" | awk '/Power_On_Hours/{print $10; exit}')"
rotatif="$(cat "$chemin/queue/rotational" 2>/dev/null)"
# L'usure, recalculée ici : cette boucle est distincte de celle du
# relevé et ne voit pas ses variables.
usure="$(printf '%s' "$sortie" | awk '/Percentage Used:/{gsub(/[^0-9]/,"",$3); print $3; exit}')"
if [ -z "$usure" ]; then
usure="$(printf '%s' "$sortie" | awk '
/Media_Wearout_Indicator|Wear_Leveling_Count|SSD_Life_Left|Percent_Lifetime_Remain/ {
if ($4 ~ /^[0-9]+$/ && $4 <= 100) { print 100 - $4; exit }
}')"
fi
# Un disque mécanique se signale toujours, et pas seulement quand il
# est vieux. C'est le premier frein d'une machine par ailleurs
# correcte : remplacer un plateau par un SSD fait plus pour elle que
# n'importe quel réglage, et c'est le genre de conseil qu'on donne
# pendant qu'on a la machine ouverte devant soi.
# Les heures de marche, dites en heures et en années de fonctionnement.
# « 477 j 15 h » se lit mal : on croit à un disque épuisé alors que
# c'est une année et quelques mois de marche, ce qui est peu.
marche=""
case "$heures" in
''|*[!0-9]*) ;;
*) marche="$heures h"
if [ "$heures" -ge 8760 ]; then
marche="$marche, soit $(( heures / 8760 )),$(( (heures % 8760) * 10 / 8760 )) an de marche"
fi ;;
esac
if [ "$rotatif" = "1" ]; then
# Un disque mécanique se fatigue en tournant. Les constructeurs
# annoncent 300 000 à 600 000 heures de MTBF, mais le retour du
# terrain est plus sobre : au-delà de 30 000 heures — trois ans et
# demi de marche continue — les pannes deviennent sensiblement plus
# fréquentes, et à 45 000 on est dans le sursis.
gravite_disque=1
supplement=""
case "$heures" in
''|*[!0-9]*) ;;
*) if [ "$heures" -ge 45000 ]; then
gravite_disque=2
supplement="
${R}Au-delà de 45 000 heures : sauvegarder avant toute chose.${Z}"
elif [ "$heures" -ge 30000 ]; then
supplement="
${O}Plus de 30 000 heures de marche : il a fait son temps.${Z}"
fi ;;
esac
note "$gravite_disque" " ${O}!${Z} /dev/$nom est un ${B}disque mécanique${Z}${marche:+ · $marche}.
${D}Le remplacer par un SSD ferait plus pour cette machine que
tout le reste réuni — démarrage, ouverture des programmes, réactivité.${Z}$supplement"
else
# Un SSD ne s'use pas en tournant mais en écrivant. Les heures de
# marche ne disent rien de son état : c'est l'usure qu'on regarde.
case "$usure" in
''|*[!0-9]*)
[ -n "$marche" ] && note 0 " ${V}·${Z} /dev/$nom est un SSD · $marche.
${D}Usure non déclarée par ce modèle.${Z}" ;;
*) if [ "$usure" -ge 90 ]; then
note 2 " ${R}✗${Z} /dev/$nom : SSD usé à ${B}${usure} %${Z} — il approche de sa fin.
${R}Sauvegarder, et prévoir son remplacement.${Z}"
elif [ "$usure" -ge 70 ]; then
note 1 " ${O}!${Z} /dev/$nom : SSD usé à ${usure} %${marche:+ · $marche}.
${D}Encore utilisable, mais il faudra y penser.${Z}"
else
note 0 " ${V}·${Z} /dev/$nom : SSD usé à ${usure} % seulement${marche:+ · $marche}."
fi ;;
esac
fi
if [ "$taille" -lt 32000000000 ]; then
note 1 " ${O}!${Z} /dev/$nom ne fait que $(( taille / 1000000000 )) Go — c'est juste."
fi
done
# La mémoire, le processeur.
MEMOIRE_MO=$(( MEMOIRE_KO / 1024 ))
if [ "$MEMOIRE_MO" -lt 2048 ]; then
note 2 " ${R}✗${Z} ${B}${MEMOIRE_MO} Mo de mémoire${Z} — trop peu pour Mint.
${D}Envisager une version légère, ou ajouter de la mémoire.${Z}"
elif [ "$MEMOIRE_MO" -lt 4096 ]; then
libres=$(( SLOTS_TOTAL - SLOTS_PRIS ))
note 1 " ${O}!${Z} ${MEMOIRE_MO} Mo de mémoire — Mint tournera, mais sans aisance.$(
[ "$libres" -gt 0 ] && printf '\n %s' "${D}$libres connecteur(s) ${TYPE_MEMOIRE} libre(s) : la machine est extensible.${Z}")"
fi
if [ "$ARCHITECTURE" != "x86_64" ] && [ "$ARCHITECTURE" != "aarch64" ]; then
note 2 " ${R}✗${Z} processeur ${ARCHITECTURE} — les versions 64 bits ne s'installeront pas."
fi
echo
case "$GRAVITE" in
0) printf " %s%s BON POUR L'INSTALLATION %s\n" "$B$V" "✓" "$Z" ;;
1) printf " %s%s INSTALLATION POSSIBLE, AVEC DES RÉSERVES %s\n" "$B$O" "!" "$Z" ;;
2) printf " %s%s NE PAS INSTALLER EN L'ÉTAT %s\n" "$B$R" "✗" "$Z" ;;
esac
echo
if [ -n "$TABLE_DISQUES" ]; then
printf ' %s' "$D"
colonne "Disque" 7; printf ' '
colonne "Modèle" 20; printf ' '
colonne "Type" 10; printf ' '
colonne "Taille" 10 droite; printf ' '
colonne "SMART" 8; printf ' '
colonne "Marche / usure" 16; printf ' '
printf 'Secteurs R/P/I%s\n' "$Z"
printf '%s' "$TABLE_DISQUES" | sed "s/^/ /"
printf " %sR : remplacés · P : illisibles en attente · I : perdus%s\n" "$D" "$Z"
echo
fi
[ -n "$REMARQUES" ] && printf "%s\n" "$REMARQUES"
# Une pause, sans quoi tout cela défile et personne ne le lit. Sa durée
# suit la gravité, et l'on n'attend jamais indéfiniment : le script tourne
# aussi sans personne devant l'écran, appelé par install.sh.
if [ -t 0 ]; then
case "$GRAVITE" in
2) printf " %sEn parler au propriétaire AVANT de toucher à quoi que ce soit.%s\n" "$D" "$Z"
printf " %sEntrée pour continuer malgré tout%s " "$B" "$Z"
read -r -t "${ALPI_PAUSE_ALERTE:-120}" _ || echo ;;
1) printf " %sEntrée pour continuer (reprise automatique dans %ss)%s " \
"$D" "${ALPI_PAUSE:-20}" "$Z"
read -r -t "${ALPI_PAUSE:-20}" _ || echo ;;
*) sleep "${ALPI_PAUSE_OK:-3}" ;;
esac
echo
fi
fi
# Ce que l'on vient de dire à la personne part avec la fiche.
#
# Sans cela, alpicache rejugeait à partir des seuls champs reçus — et ne voyait
# pas les secteurs en attente, qui ne lui étaient pas envoyés. Il affichait
# « sain » un disque que ce script venait de déclarer à remplacer. Deux juges,
# deux verdicts : il n'y en a plus qu'un, et c'est celui-ci.
VERDICT="ok"
case "${GRAVITE:-0}" in
2) VERDICT="alerte" ;;
1) VERDICT="attention" ;;
esac
MOTIFS_JSON="["
premier=1
while IFS= read -r ligne; do
[ -n "$ligne" ] || continue
[ "$premier" = 0 ] && MOTIFS_JSON="$MOTIFS_JSON,"
premier=0
MOTIFS_JSON="$MOTIFS_JSON$(printf '%s' "$ligne" \
| sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$/"/')"
done <<< "${MOTIFS:-}"
MOTIFS_JSON="$MOTIFS_JSON]"
RAPPORT="${RAPPORT%\}},$(champ verdict "$VERDICT"),\"motifs\":$MOTIFS_JSON}"
if command -v curl >/dev/null 2>&1; then
reponse="$(printf '%s' "$RAPPORT" | curl -s --max-time 15 \
-H 'Content-Type: application/json' --data-binary @- "$SERVEUR/fiche")"
elif command -v wget >/dev/null 2>&1; then
reponse="$(printf '%s' "$RAPPORT" | wget -qO- --timeout=15 \
--header='Content-Type: application/json' --post-file=- "$SERVEUR/fiche")"
else
echo "Ni curl ni wget : la fiche n'a pas pu être envoyée." >&2
exit 1
fi
if [ -n "$reponse" ]; then
echo "$reponse"
else
echo "Le serveur n'a pas répondu — la fiche n'est pas enregistrée." >&2
exit 1
fi

View file

@ -1,14 +1,39 @@
#!/bin/bash #!/bin/bash
##################################################
# Fiche matérielle pour le serveur de l'install party
#
# Placée au début, et non à la fin : si un disque faiblit, autant le savoir
# avant d'avoir passé une heure à installer dessus — c'est le moment où l'on
# peut encore en parler à son propriétaire. Et si l'installation s'arrête en
# route, on a tout de même la fiche.
#
# Le relevé est le script voisin du dépôt, lu et versionné comme celui-ci :
# rien n'est téléchargé ni exécuté depuis le réseau. S'il n'est pas là, on
# passe — la machine d'un particulier qui rejoue ce script chez elle ne doit
# rien avoir à faire de tout ceci.
#
# C'est la seconde passe, celle que « sudo » relance, qui l'exécute :
# dmidecode et smartctl demandent les droits d'administration.
##################################################
if [ "$(id -u)" = "0" ] && [ -x "$(dirname "$0")/alpi-fiche.sh" ]; then
"$(dirname "$0")/alpi-fiche.sh" || true
fi
# Script de post-installation pour Linux Mint 22.3 # Script de post-installation pour Linux Mint 22.3
# testé le 25/03/2026 # testé le 25/03/2026
# #
# utilisation # utilisation
# wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki/raw/branch/main/code/linux/linux-mint/install.sh # wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/install.sh
# && chmod +x install.sh # && chmod +x install.sh
# && ./install.sh # && ./install.sh
GREEN=$'\e[32m'
NC=$'\e[0m'
abort() { abort() {
echo "" echo ""
echo "" echo ""
@ -98,7 +123,14 @@ opti_journald() {
opti_fstab () { opti_fstab () {
echo -e "==> Optimisation du montage des disques (noatime)\n" echo -e "==> Optimisation du montage des disques (noatime)\n"
# Replaces relatime with noatime everywhere in the file # Remplace relatime par noatime partout dans le fichier.
#
# ATTENTION : sans effet sur une installation Mint 22.3. Le fstab qu'écrit
# l'installateur ne mentionne ni « relatime » ni « noatime » — il s'en remet
# à « defaults », et relatime est le comportement par défaut du noyau. Il n'y
# a donc rien à substituer. Vérifié le 20/09/2026 sur une installation neuve.
# Pour poser réellement noatime, il faudrait ajouter l'option aux lignes
# concernées plutôt que d'en remplacer une qui n'y est pas.
sudo sed -i 's/relatime/noatime/g' /etc/fstab sudo sed -i 's/relatime/noatime/g' /etc/fstab
} }
@ -144,6 +176,7 @@ modif_systeme() {
microcode microcode
opti_journald opti_journald
opti_fstab opti_fstab
modif_grub
mises_a_jour mises_a_jour
@ -216,7 +249,7 @@ install_logiciels() {
DEBIAN_FRONTEND=noninteractive apt install -y ttf-mscorefonts-installer || abort "problème lors de l'installation des polices" DEBIAN_FRONTEND=noninteractive apt install -y ttf-mscorefonts-installer || abort "problème lors de l'installation des polices"
# mise à jour du système # mise à jour du système
echo "==> Mise à jour du système\n" echo -e "==> Mise à jour du système\n"
apt update || abort "impossible de récupérer les informations des dépôts" apt update || abort "impossible de récupérer les informations des dépôts"
apt full-upgrade -y || abort "erreur lors de la mise à jour des paquets" apt full-upgrade -y || abort "erreur lors de la mise à jour des paquets"
@ -278,7 +311,7 @@ install_logiciels() {
# Mises à jour # # Mises à jour #
################ ################
mises_a_jour() { mises_a_jour() {
echo -e "Configuraiton de dconf\n" echo -e "==> Configuration de dconf\n"
# Configuration de dconf pour permettre de modifier certaines configurations # Configuration de dconf pour permettre de modifier certaines configurations
# par défaut # par défaut
mkdir -p /etc/dconf/profile mkdir -p /etc/dconf/profile
@ -326,11 +359,32 @@ configuration_apt() {
# GRUB : sélection automatique de la dernière entrée utilisée # # GRUB : sélection automatique de la dernière entrée utilisée #
############################################################### ###############################################################
modif_grub() { modif_grub() {
# Rien à faire si c'est déjà en place.
#
# update-grub lance os-prober, qui part sonder toutes les partitions de la
# machine à la recherche d'autres systèmes, puis régénère la configuration
# entière : vingt à trente secondes qu'on ne rattrape pas, et qu'on payait
# jusqu'ici à chaque exécution, y compris quand il n'y avait rien à
# changer. Sur une install party de quinze machines, cela se voit.
if grep -q '^GRUB_DEFAULT=saved' /etc/default/grub \
&& grep -q '^GRUB_SAVEDEFAULT=true' /etc/default/grub; then
echo -e "==> GRUB est déjà configuré, rien à changer\n"
return 0
fi
echo -e "==> Modification de GRUB\n" echo -e "==> Modification de GRUB\n"
# On comment la ligne "GRUB_DEFAULT=0" et on ajoute les "bonnes" options # On commente la ligne d'origine, puis on ajoute les nôtres si elles
sed -i '/^GRUB_DEFAULT=0/ i#GRUB_DEFAULT=0\ # manquent. Les ajouter plutôt que les insérer règle deux choses : le
GRUB_DEFAULT=saved\ # fichier ne se retrouve plus avec des lignes indentées — GRUB les accepte,
GRUB_SAVEDEFAULT=true' /etc/default/grub # mais elles trompent la lecture quand on vient déboguer —, et la fonction
# fait toujours son travail lorsque « GRUB_DEFAULT=0 » a disparu, ce qui
# arrive dès qu'un autre outil est passé avant nous.
sed -i 's/^GRUB_DEFAULT=0/#&/' /etc/default/grub
grep -q '^GRUB_DEFAULT=saved' /etc/default/grub \
|| echo 'GRUB_DEFAULT=saved' >> /etc/default/grub
grep -q '^GRUB_SAVEDEFAULT=true' /etc/default/grub \
|| echo 'GRUB_SAVEDEFAULT=true' >> /etc/default/grub
# On met à jour GRUB # On met à jour GRUB
update-grub update-grub
} }

View file

@ -3,7 +3,7 @@
# Alpinux — état des lieux d'une machine avant d'installer Linux Mint # Alpinux — état des lieux d'une machine avant d'installer Linux Mint
# #
# À lancer depuis la session live, AVANT de toucher au disque : # À lancer depuis la session live, AVANT de toucher au disque :
# wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-wiki/raw/branch/main/code/linux/linux-mint/verif-disque.sh # wget https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.scripts/raw/branch/stable/poste/verif-disque.sh
# chmod +x verif-disque.sh # chmod +x verif-disque.sh
# ./verif-disque.sh # ./verif-disque.sh
# #