# [COURS] EXERCICES LINUX ULTRA-DÉTAILLÉS - NIVEAU PRODUCTION

## [LIVRE] INTRODUCTION

Ce document contient **5 exercices pratiques corrigés** pour chaque section de commandes Linux, conçus selon la même méthodologie pédagogique que les exercices Apache.

**Format de chaque exercice :**
- [OK] Contexte professionnel réaliste
- [OK] Objectifs pédagogiques clairs
- [OK] Solution complète étape par étape
- [OK] Explications ligne par ligne (POURQUOI/COMMENT/QUAND)
- [OK] Tests de validation
- [OK] Erreurs courantes et solutions
- [OK] Points clés à retenir
- [OK] Pour aller plus loin

**Niveaux de progression :**
- [VERT] Exercice 1 : Débutant
- [JAUNE] Exercice 2 : Intermédiaire
- [JAUNE] Exercice 3 : Intermédiaire
- [ROUGE] Exercice 4 : Avancé
- [ROUGE] Exercice 5 : Expert

---

# [DOSSIER] SECTION 1 : NAVIGATION & SYSTÈME DE FICHIERS

---

## [VERT] EXERCICE 1.1 : ORGANISATION D'UN PROJET WEB

### [LISTE] ÉNONCÉ

#### Contexte professionnel
Tu es développeur web junior dans une agence. On te demande de créer la structure de répertoires pour un nouveau projet client : un site e-commerce avec front-end, back-end et base de données.

#### Cahier des charges
Créer une arborescence complète avec :
- Dossiers pour le code source (frontend, backend, database)
- Dossiers pour la documentation
- Dossiers pour les tests
- Dossiers pour les assets (images, CSS, JS)
- Fichiers README dans chaque dossier principal
- Structure respectant les conventions de l'industrie

#### Contraintes
- Utiliser uniquement les commandes de navigation Linux
- Créer au minimum 20 dossiers et 10 fichiers
- Suivre une nomenclature cohérente
- Temps estimé : 30-45 minutes

---

### [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Naviguer efficacement dans l'arborescence Linux
- [OK] Créer des dossiers et sous-dossiers multiples
- [OK] Utiliser les chemins absolus et relatifs
- [OK] Créer des fichiers vides
- [OK] Visualiser une arborescence avec `tree`
- [OK] Comprendre la structure d'un projet professionnel

---

### [DOCS] PRÉREQUIS

- Accès à un terminal Linux
- Droits d'écriture dans ton répertoire home
- Aucune connaissance préalable requise

---

### [OK] SOLUTION COMPLÈTE

#### ÉTAPE 1 : Créer le dossier racine du projet

```bash
# Se placer dans le répertoire home
cd ~

# Créer le dossier principal du projet
mkdir ecommerce-project

# Vérifier la création
ls -la | grep ecommerce
```

**Explication des commandes :**

**`cd ~`**
- `cd` = Change Directory (changer de répertoire)
- `~` = Raccourci pour le répertoire home de l'utilisateur
- Équivalent à `cd /home/ton_username`
- Ou `cd $HOME`

**`mkdir ecommerce-project`**
- `mkdir` = Make Directory (créer un répertoire)
- Crée un seul dossier au nom spécifié
- Par défaut, créé dans le répertoire courant

**`ls -la | grep ecommerce`**
- `ls` = List (lister le contenu)
- `-l` = Format long (permissions, taille, date)
- `-a` = All (afficher les fichiers cachés commençant par `.`)
- `|` = Pipe (rediriger la sortie vers une autre commande)
- `grep ecommerce` = Filtrer les lignes contenant "ecommerce"

**Résultat attendu :**
```
drwxr-xr-x  2 user user 4096 Dec 30 10:00 ecommerce-project
```

**Décomposition du résultat :**
- `d` = Type directory (répertoire)
- `rwxr-xr-x` = Permissions
  - `rwx` = Propriétaire : Read, Write, Execute
  - `r-x` = Groupe : Read, Execute
  - `r-x` = Autres : Read, Execute
- `2` = Nombre de liens
- `user user` = Propriétaire et groupe
- `4096` = Taille en octets
- `Dec 30 10:00` = Date de modification
- `ecommerce-project` = Nom du répertoire

---

#### ÉTAPE 2 : Créer la structure principale

```bash
# Entrer dans le projet
cd ecommerce-project

# Afficher le chemin absolu actuel
pwd
```

**`pwd`** = Print Working Directory (afficher le répertoire courant)

**Résultat exemple :**
```
/home/sirrdev/ecommerce-project
```

---

**Créer les dossiers principaux :**

```bash
# Méthode 1 : Un par un
mkdir src
mkdir docs
mkdir tests
mkdir config

# Méthode 2 : Plusieurs à la fois (plus efficace)
mkdir assets scripts database backups
```

**Explication :**

`mkdir` peut créer plusieurs dossiers en une seule commande en les séparant par des espaces.

**Vérifier :**

```bash
ls
```

**Résultat :**
```
assets  backups  config  database  docs  scripts  src  tests
```

---

#### ÉTAPE 3 : Créer les sous-dossiers (structure hiérarchique)

**Créer des sous-dossiers dans `src/` :**

```bash
# Méthode avec chemins relatifs
mkdir src/frontend
mkdir src/backend
mkdir src/shared

# Vérifier
ls src/
```

**Résultat :**
```
backend  frontend  shared
```

---

**Créer plusieurs niveaux de sous-dossiers avec l'option `-p` :**

```bash
# -p = --parents (créer les dossiers parents si nécessaires)
mkdir -p src/frontend/components
mkdir -p src/frontend/pages
mkdir -p src/frontend/styles
mkdir -p src/frontend/assets/images
mkdir -p src/frontend/assets/fonts
mkdir -p src/frontend/assets/icons
```

**Explication de `-p` :**

Sans `-p` :
```bash
mkdir src/frontend/components
# Erreur si src/frontend n'existe pas !
```

Avec `-p` :
```bash
mkdir -p src/frontend/components
# Crée src/ puis src/frontend/ puis src/frontend/components/
# Même si src/ et src/frontend/ n'existent pas encore
```

**Cas d'usage typique de `-p` :**

```bash
# Créer une arborescence profonde en une seule commande
mkdir -p projet/src/modules/auth/controllers/api/v1
```

---

**Créer la structure backend :**

```bash
mkdir -p src/backend/controllers
mkdir -p src/backend/models
mkdir -p src/backend/routes
mkdir -p src/backend/middlewares
mkdir -p src/backend/services
mkdir -p src/backend/utils
```

---

**Créer la structure de tests :**

```bash
mkdir -p tests/unit
mkdir -p tests/integration
mkdir -p tests/e2e
mkdir -p tests/fixtures
```

**Explication des types de tests :**

**Unit tests (tests unitaires) :**
- Testent une fonction/classe isolée
- Rapides à exécuter
- Exemples : tester une fonction de calcul, une validation

**Integration tests (tests d'intégration) :**
- Testent l'interaction entre modules
- Exemples : API + Base de données

**E2E tests (End-to-End) :**
- Testent le parcours utilisateur complet
- Exemples : Selenium, Cypress

**Fixtures :**
- Données de test statiques
- Exemples : fichiers JSON, CSV, images de test

---

**Créer la structure de documentation :**

```bash
mkdir -p docs/api
mkdir -p docs/architecture
mkdir -p docs/guides
mkdir -p docs/screenshots
```

---

**Créer la structure de configuration :**

```bash
mkdir -p config/development
mkdir -p config/production
mkdir -p config/staging
```

---

**Créer la structure de scripts :**

```bash
mkdir -p scripts/deploy
mkdir -p scripts/database
mkdir -p scripts/utils
```

---

#### ÉTAPE 4 : Visualiser l'arborescence complète

**Installer `tree` si nécessaire :**

```bash
# Sur Debian/Ubuntu
sudo apt install tree -y

# Sur RedHat/CentOS
sudo yum install tree -y
```

---

**Afficher l'arborescence :**

```bash
tree
```

**Résultat :**
```
.
├── assets
├── backups
├── config
│   ├── development
│   ├── production
│   └── staging
├── database
├── docs
│   ├── api
│   ├── architecture
│   ├── guides
│   └── screenshots
├── scripts
│   ├── database
│   ├── deploy
│   └── utils
├── src
│   ├── backend
│   │   ├── controllers
│   │   ├── middlewares
│   │   ├── models
│   │   ├── routes
│   │   ├── services
│   │   └── utils
│   ├── frontend
│   │   ├── assets
│   │   │   ├── fonts
│   │   │   ├── icons
│   │   │   └── images
│   │   ├── components
│   │   ├── pages
│   │   └── styles
│   └── shared
└── tests
    ├── e2e
    ├── fixtures
    ├── integration
    └── unit
```

**Options utiles de `tree` :**

```bash
# Limiter la profondeur
tree -L 2    # Affiche seulement 2 niveaux

# Afficher seulement les dossiers
tree -d

# Afficher les tailles
tree -h

# Afficher les permissions
tree -p

# Exporter en HTML
tree -H . -o structure.html
```

---

#### ÉTAPE 5 : Créer des fichiers README

**Créer un README dans chaque dossier principal :**

```bash
# Méthode 1 : touch (crée un fichier vide)
touch README.md
touch src/README.md
touch docs/README.md
touch tests/README.md

# Méthode 2 : Plusieurs à la fois
touch config/README.md scripts/README.md database/README.md
```

**`touch` :**
- Crée un fichier vide s'il n'existe pas
- Met à jour la date de modification s'il existe
- Peut créer plusieurs fichiers en une commande

---

**Créer des fichiers avec contenu initial :**

```bash
# Méthode 1 : echo avec redirection
echo "# Ecommerce Project" > README.md

# Méthode 2 : cat avec heredoc
cat > src/README.md << 'EOF'
# Source Code

Ce dossier contient le code source du projet.

## Structure

- `frontend/` : Application front-end
- `backend/` : API et logique serveur
- `shared/` : Code partagé entre front et back
EOF
```

**Explication des redirections :**

**`>`** = Redirection de sortie (écrase le fichier)
```bash
echo "Nouveau contenu" > fichier.txt
# Écrase le contenu existant
```

**`>>`** = Redirection en mode append (ajoute à la fin)
```bash
echo "Ligne supplémentaire" >> fichier.txt
# Ajoute sans écraser
```

**Heredoc (`<< 'EOF'`) :**
```bash
cat > fichier.txt << 'EOF'
Ligne 1
Ligne 2
Ligne 3
EOF
```
- Permet d'écrire plusieurs lignes
- `EOF` = End Of File (marqueur de fin, peut être n'importe quoi)
- Guillemets simples `'EOF'` = Pas d'interpolation de variables
- Sans guillemets = Interpolation activée

---

#### ÉTAPE 6 : Naviguer dans l'arborescence

**Chemins relatifs vs absolus :**

```bash
# Chemin absolu (depuis la racine /)
cd /home/sirrdev/ecommerce-project/src/frontend

# Chemin relatif (depuis le répertoire courant)
cd src/frontend    # Si on est dans ecommerce-project
cd ../backend      # Remonter d'un niveau puis descendre dans backend
cd ../../docs      # Remonter de 2 niveaux puis descendre dans docs
```

**Explication des chemins relatifs :**

**`.`** = Répertoire courant
```bash
cd .     # Ne fait rien (reste au même endroit)
./script.sh   # Exécute script.sh dans le répertoire courant
```

**`..`** = Répertoire parent
```bash
cd ..     # Remonte d'un niveau
cd ../..  # Remonte de 2 niveaux
```

**`~`** = Répertoire home
```bash
cd ~      # Va dans /home/sirrdev
cd ~/Documents   # Va dans /home/sirrdev/Documents
```

**`-`** = Répertoire précédent
```bash
cd /var/log
cd /tmp
cd -       # Retourne dans /var/log
```

---

**Navigations courantes :**

```bash
# Retour au répertoire home
cd
# ou
cd ~

# Aller au répertoire précédent
cd -

# Aller à la racine du projet
cd ~/ecommerce-project

# Aller dans un sous-dossier profond
cd ~/ecommerce-project/src/frontend/components
```

---

#### ÉTAPE 7 : Créer des fichiers de configuration

```bash
# Se placer dans config/
cd ~/ecommerce-project/config

# Créer des fichiers .env
touch development/.env.development
touch production/.env.production
touch staging/.env.staging

# Créer des fichiers de config
touch development/database.json
touch production/database.json
```

**Ajouter du contenu aux fichiers :**

```bash
cat > development/.env.development << 'EOF'
NODE_ENV=development
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=ecommerce_dev
DEBUG=true
EOF
```

---

#### ÉTAPE 8 : Créer une structure de fichiers plus complète

```bash
# Retourner à la racine du projet
cd ~/ecommerce-project

# Créer les fichiers de gestion de projet
touch .gitignore
touch .editorconfig
touch package.json
touch docker-compose.yml
touch Dockerfile

# Créer des fichiers dans src/frontend
touch src/frontend/index.html
touch src/frontend/app.js
touch src/frontend/styles/main.css
touch src/frontend/styles/variables.css

# Créer des fichiers dans src/backend
touch src/backend/server.js
touch src/backend/routes/api.js
touch src/backend/models/User.js
touch src/backend/models/Product.js
```

---

### [OK] TESTS DE VALIDATION

**1. Vérifier le nombre total de dossiers :**

```bash
cd ~/ecommerce-project
find . -type d | wc -l
```

**Résultat attendu :** Au moins 30 dossiers

---

**2. Vérifier le nombre total de fichiers :**

```bash
find . -type f | wc -l
```

**Résultat attendu :** Au moins 15 fichiers

---

**3. Vérifier l'arborescence avec tree :**

```bash
tree -L 3
```

**Vérifier visuellement que tous les dossiers sont présents.**

---

**4. Vérifier les fichiers README :**

```bash
find . -name "README.md"
```

**Résultat attendu :**
```
./README.md
./src/README.md
./docs/README.md
./tests/README.md
...
```

---

**5. Vérifier que tous les chemins sont accessibles :**

```bash
# Tester quelques chemins profonds
ls src/frontend/assets/images
ls src/backend/controllers
ls tests/integration
ls config/production
```

**Tous doivent répondre sans erreur.**

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "No such file or directory"

**Symptôme :**
```bash
mkdir src/frontend/components
mkdir: cannot create directory 'src/frontend/components': No such file or directory
```

**Cause :** Le dossier parent `src/frontend` n'existe pas.

**Solution :**
```bash
# Utiliser -p pour créer les parents
mkdir -p src/frontend/components
```

---

#### Erreur 2 : Permission denied

**Symptôme :**
```bash
mkdir /var/www/project
mkdir: cannot create directory '/var/www/project': Permission denied
```

**Cause :** Pas de droits d'écriture dans `/var/www/`.

**Solution :**
```bash
# Option 1 : Utiliser sudo
sudo mkdir /var/www/project

# Option 2 : Travailler dans ton home
mkdir ~/project
```

---

#### Erreur 3 : Nom de fichier avec espaces

**Symptôme :**
```bash
mkdir Mon Projet
# Crée 2 dossiers : "Mon" et "Projet"
```

**Solution :**
```bash
# Méthode 1 : Guillemets
mkdir "Mon Projet"

# Méthode 2 : Échappement
mkdir Mon\ Projet

# Méthode 3 (recommandée) : Éviter les espaces
mkdir mon-projet
```

---

#### Erreur 4 : Se perdre dans l'arborescence

**Symptôme :** Ne plus savoir où on est.

**Solution :**
```bash
# Afficher le chemin actuel
pwd

# Retourner à la racine du projet
cd ~/ecommerce-project

# Utiliser tree pour visualiser
tree -L 2
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Commandes de navigation**
- `cd` = Changer de répertoire
- `pwd` = Afficher le répertoire courant
- `ls` = Lister le contenu

**2. Création de dossiers**
- `mkdir nom` = Créer un dossier
- `mkdir -p chemin/complet` = Créer avec parents
- `mkdir dir1 dir2 dir3` = Créer plusieurs dossiers

**3. Création de fichiers**
- `touch fichier` = Créer un fichier vide
- `echo "contenu" > fichier` = Créer avec contenu
- `cat > fichier << 'EOF'` = Créer multi-lignes

**4. Chemins**
- Absolu : `/home/user/projet`
- Relatif : `src/frontend`
- `.` = Répertoire courant
- `..` = Répertoire parent
- `~` = Home
- `-` = Répertoire précédent

**5. Visualisation**
- `tree` = Arborescence visuelle
- `ls -la` = Liste détaillée
- `find . -type d` = Trouver tous les dossiers

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Créer un script d'initialisation de projet**

```bash
#!/bin/bash
# init-project.sh

PROJECT_NAME=$1

if [ -z "$PROJECT_NAME" ]; then
    echo "Usage: $0 <project-name>"
    exit 1
fi

mkdir -p "$PROJECT_NAME"/{src,docs,tests,config,scripts}
mkdir -p "$PROJECT_NAME"/src/{frontend,backend,shared}
mkdir -p "$PROJECT_NAME"/tests/{unit,integration,e2e}

touch "$PROJECT_NAME"/README.md
echo "# $PROJECT_NAME" > "$PROJECT_NAME"/README.md

echo "[OK] Projet '$PROJECT_NAME' créé avec succès !"
tree "$PROJECT_NAME" -L 2
```

**Utilisation :**
```bash
chmod +x init-project.sh
./init-project.sh mon-nouveau-projet
```

---

**2. Utiliser des templates de projet**

```bash
# Cookiecutter (Python)
pip install cookiecutter
cookiecutter https://github.com/audreyr/cookiecutter-pypackage

# Yeoman (JavaScript)
npm install -g yo
yo webapp
```

---

**3. Gérer les projets avec Git**

```bash
cd ~/ecommerce-project
git init
git add .
git commit -m "Initial project structure"
```

---

**4. Automatiser avec Makefiles**

```makefile
# Makefile
.PHONY: init clean

init:
	mkdir -p src/{frontend,backend}
	mkdir -p tests/{unit,integration}
	touch README.md

clean:
	rm -rf src tests
```

**Utilisation :**
```bash
make init
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1.1

**[OK] Félicitations ! Tu sais maintenant organiser un projet professionnel !**

**Ce que tu as appris :**
- Navigation dans l'arborescence Linux
- Création de dossiers et sous-dossiers multiples
- Chemins absolus et relatifs
- Visualisation avec tree
- Structure de projet professionnelle

**Compétences acquises :**
- [OK] Navigation Linux (niveau intermédiaire)
- [OK] Organisation de projet
- [OK] Chemins et arborescence

**Temps moyen de réalisation :** 30-45 minutes

**Prochaine étape :** Exercice 1.2 - Manipulation avancée de fichiers ! [DOSSIER]

---

Je vais m'arrêter ici pour cette première réponse car le document est déjà très long. 

**Ce que j'ai fait :**
[OK] Un exercice ultra-détaillé pour la Section 1 (Navigation & Système de fichiers)
[OK] Format identique aux exercices Apache
[OK] Explications ligne par ligne
[OK] Tests de validation
[OK] Erreurs courantes

**Pour continuer, tu peux me demander :**
1. Les 4 autres exercices de la Section 1 (exercices 1.2 à 1.5)
2. Les exercices de la Section 2 (Affichage & Manipulation)
3. Ou directement une section spécifique qui t'intéresse

Veux-tu que je continue avec les exercices suivants ?

# [JAUNE] EXERCICE 1.2 : COPIE ET DÉPLACEMENT DE FICHIERS

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système dans une entreprise qui migre son site web vers un nouveau serveur. Tu dois :
1. Copier tous les fichiers du site existant vers un nouveau dossier
2. Réorganiser la structure (déplacer certains fichiers)
3. Créer des backups avant chaque opération
4. Renommer des fichiers pour suivre une nouvelle convention de nommage
5. Préserver les permissions et timestamps

### Cahier des charges

- Copier l'intégralité du site (50+ fichiers)
- Créer 3 backups datés différents
- Réorganiser les assets (images, CSS, JS)
- Renommer les fichiers de configuration
- Vérifier l'intégrité après chaque opération
- Ne perdre aucune donnée

### Contraintes

- Commandes : `cp`, `mv`
- Préserver les métadonnées (dates, permissions)
- Mode interactif pour éviter les écrasements accidentels
- Temps estimé : 45 minutes - 1 heure

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Copier des fichiers et dossiers (simple et récursif)
- [OK] Déplacer et renommer des fichiers
- [OK] Préserver les permissions et timestamps
- [OK] Utiliser les options de sécurité (-i, -n, -u)
- [OK] Faire des backups efficaces
- [OK] Vérifier l'intégrité des copies
- [OK] Gérer les conflits de noms

---

## [DOCS] PRÉREQUIS

- Exercice 1.1 terminé
- Compréhension des chemins (absolus/relatifs)
- Notions de base sur les permissions

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Préparer l'environnement de test

**Créer un site web fictif à migrer :**

```bash
# Créer la structure du site source
mkdir -p ~/migration-exercise/old-site
cd ~/migration-exercise/old-site

# Créer des dossiers
mkdir -p css js images uploads config logs

# Créer des fichiers CSS
cat > css/style.css << 'EOF'
body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
}
EOF

cat > css/mobile.css << 'EOF'
@media (max-width: 768px) {
    body {
        padding: 10px;
    }
}
EOF

cat > css/print.css << 'EOF'
@media print {
    body {
        background: white;
    }
}
EOF

# Créer des fichiers JS
cat > js/main.js << 'EOF'
console.log('Site loaded');

function init() {
    console.log('Initializing...');
}

window.onload = init;
EOF

cat > js/utils.js << 'EOF'
function formatDate(date) {
    return date.toLocaleDateString();
}

function validateEmail(email) {
    return email.includes('@');
}
EOF

# Créer des fichiers HTML
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Mon Site</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <h1>Bienvenue</h1>
    <script src="js/main.js"></script>
</body>
</html>
EOF

cat > about.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>À propos</title>
</head>
<body>
    <h1>À propos de nous</h1>
</body>
</html>
EOF

cat > contact.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Contact</title>
</head>
<body>
    <h1>Contactez-nous</h1>
</body>
</html>
EOF

# Créer des fichiers de configuration
cat > config/database.conf << 'EOF'
host=localhost
port=3306
database=mysite
user=admin
EOF

cat > config/app.conf << 'EOF'
debug=false
port=8080
EOF

# Créer quelques fichiers de logs
echo "2024-12-30 10:00:00 - Site started" > logs/access.log
echo "2024-12-30 10:15:00 - User logged in" >> logs/access.log
echo "2024-12-30 10:30:00 - ERROR: Database connection failed" > logs/error.log

# Créer quelques images factices (fichiers texte pour l'exercice)
echo "IMAGE DATA" > images/logo.png
echo "IMAGE DATA" > images/banner.jpg
echo "IMAGE DATA" > images/icon.svg

# Créer quelques uploads
echo "User uploaded file" > uploads/document1.pdf
echo "User uploaded file" > uploads/photo.jpg
```

**Vérifier la structure créée :**

```bash
tree ~/migration-exercise/old-site
```

**Résultat attendu :**
```
old-site/
├── about.html
├── config
│   ├── app.conf
│   └── database.conf
├── contact.html
├── css
│   ├── mobile.css
│   ├── print.css
│   └── style.css
├── images
│   ├── banner.jpg
│   ├── icon.svg
│   └── logo.png
├── index.html
├── js
│   ├── main.js
│   └── utils.js
├── logs
│   ├── access.log
│   └── error.log
└── uploads
    ├── document1.pdf
    └── photo.jpg
```

---

### ÉTAPE 2 : Comprendre la commande `cp` (copy)

**Syntaxe de base :**

```
cp [OPTIONS] SOURCE DESTINATION
```

**Options principales :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-r` ou `-R` | Récursif (copier dossiers) | `cp -r dir1 dir2` |
| `-i` | Interactif (demander avant écrasement) | `cp -i file1 file2` |
| `-n` | No-clobber (ne jamais écraser) | `cp -n file1 file2` |
| `-u` | Update (copier si plus récent) | `cp -u file1 file2` |
| `-p` | Préserver (permissions, dates) | `cp -p file1 file2` |
| `-a` | Archive (= -dpR, tout préserver) | `cp -a dir1 dir2` |
| `-v` | Verbose (afficher les actions) | `cp -v file1 file2` |
| `-b` | Backup (créer un backup avec ~) | `cp -b file1 file2` |
| `-f` | Force (écraser sans demander) | `cp -f file1 file2` |

---

**Tester les différentes options :**

```bash
# Créer un dossier de test
cd ~/migration-exercise
mkdir test-cp

# Test 1 : Copie simple d'un fichier
cp old-site/index.html test-cp/
ls test-cp/
```

**Résultat :** `index.html` copié dans `test-cp/`

---

**Test 2 : Copie avec renommage**

```bash
cp old-site/index.html test-cp/home.html
ls test-cp/
```

**Résultat :**
```
home.html  index.html
```

**Explication :**
- `cp source destination`
- Si `destination` est un nom de fichier différent -> renommage
- Si `destination` est un dossier -> copie dedans avec le même nom

---

**Test 3 : Copie d'un dossier (DOIT utiliser -r)**

```bash
# Sans -r (ERREUR)
cp old-site/css test-cp/
# cp: -r not specified; omitting directory 'old-site/css'

# Avec -r (CORRECT)
cp -r old-site/css test-cp/
ls test-cp/css/
```

**Résultat :**
```
mobile.css  print.css  style.css
```

**Explication de `-r` (récursif) :**

`-r` ou `-R` = Recursive (récursif)

Sans `-r` :
- `cp` refuse de copier un dossier
- Message d'erreur : "omitting directory"

Avec `-r` :
- Copie le dossier ET tout son contenu
- Descend dans tous les sous-dossiers
- Copie récursivement toute l'arborescence

---

**Test 4 : Mode interactif avec `-i`**

```bash
# Créer un fichier
echo "Version 1" > test-cp/data.txt

# Copier avec écrasement (sans -i)
echo "Version 2" > old-site/data.txt
cp old-site/data.txt test-cp/
# Écrase silencieusement !

# Copier avec mode interactif (-i)
echo "Version 3" > old-site/data.txt
cp -i old-site/data.txt test-cp/
# cp: overwrite 'test-cp/data.txt'? 
```

**Tu dois répondre :**
- `y` ou `yes` = Oui, écraser
- `n` ou `no` = Non, ne pas écraser

**Explication de `-i` (interactive) :**

`-i` = Ask before overwrite (demander avant d'écraser)

**Pourquoi c'est important :**
- Évite les écrasements accidentels
- Sécurité pour les fichiers importants
- Bonne pratique en production

**Recommandation :**
```bash
# Créer un alias permanent
echo "alias cp='cp -i'" >> ~/.bashrc
source ~/.bashrc
```

Maintenant `cp` demandera toujours confirmation !

---

**Test 5 : Préserver les permissions et dates avec `-p`**

```bash
# Créer un fichier avec des permissions spécifiques
touch old-site/script.sh
chmod 755 old-site/script.sh

# Copie normale (SANS -p)
cp old-site/script.sh test-cp/script1.sh
ls -l test-cp/script1.sh
```

**Résultat :**
```
-rw-r--r-- 1 user user 0 Dec 30 11:00 script1.sh
```

**Les permissions ont changé !** (644 au lieu de 755)

---

```bash
# Copie avec préservation (-p)
cp -p old-site/script.sh test-cp/script2.sh
ls -l test-cp/script2.sh
```

**Résultat :**
```
-rwxr-xr-x 1 user user 0 Dec 30 10:45 script2.sh
```

**Les permissions ET la date sont préservées !**

---

**Explication de `-p` (preserve) :**

`-p` = Preserve (préserver)

**Préserve :**
- Permissions (mode)
- Timestamps (dates de modification)
- Propriétaire/groupe (si exécuté en root)

**Équivalent à :**
```bash
cp --preserve=mode,ownership,timestamps
```

---

**Test 6 : Mode archive avec `-a`**

```bash
# -a = Archive mode (le plus complet)
cp -a old-site test-cp/old-site-archive

# Équivalent à :
# cp -dpR old-site test-cp/old-site-archive
```

**Explication de `-a` (archive) :**

`-a` = `-dpR` combinés

**Signification :**
- `-d` = Préserver les liens symboliques (comme liens)
- `-p` = Préserver permissions, dates, propriétaire
- `-R` = Récursif (tout copier)

**Quand utiliser `-a` :**
- Backups complets
- Migrations de systèmes
- Archivage exact
- Quand TOUT doit être identique à l'original

---

**Test 7 : Mode verbose avec `-v`**

```bash
cp -v old-site/*.html test-cp/
```

**Résultat :**
```
'old-site/about.html' -> 'test-cp/about.html'
'old-site/contact.html' -> 'test-cp/contact.html'
'old-site/index.html' -> 'test-cp/index.html'
```

**`-v` affiche chaque fichier copié.**

**Très utile pour :**
- Voir la progression
- Déboguer les problèmes
- Vérifier que tout est copié

---

**Test 8 : Copie multiple avec wildcards**

```bash
# Copier tous les fichiers .css
cp old-site/css/*.css test-cp/

# Copier tous les fichiers commençant par "main"
cp old-site/js/main* test-cp/

# Copier tous les fichiers
cp old-site/* test-cp/
# [ATTENTION] Ne copie PAS les dossiers (besoin de -r)

# Copier tout (fichiers ET dossiers)
cp -r old-site/* test-cp/
```

**Explication des wildcards :**

**`*`** = N'importe quelle suite de caractères
```bash
*.txt       # Tous les fichiers .txt
file*       # file1, file2, file_test
*report*    # report.pdf, monthly_report.xlsx
```

**`?`** = Exactement 1 caractère
```bash
file?.txt   # file1.txt, fileA.txt (PAS file10.txt)
```

**`[...]`** = Un caractère parmi ceux listés
```bash
file[123].txt    # file1.txt, file2.txt, file3.txt
file[a-z].txt    # filea.txt, fileb.txt, ... filez.txt
```

---

### ÉTAPE 3 : Copier le site entier (migration)

**Créer le nouveau dossier du site :**

```bash
cd ~/migration-exercise
mkdir new-site
```

---

**Copier tout le contenu avec préservation :**

```bash
# Méthode recommandée : cp -a (archive)
cp -av old-site/* new-site/
```

**Résultat :**
```
'old-site/about.html' -> 'new-site/about.html'
'old-site/config' -> 'new-site/config'
'old-site/config/app.conf' -> 'new-site/config/app.conf'
'old-site/config/database.conf' -> 'new-site/config/database.conf'
...
```

---

**Vérifier que tout a été copié :**

```bash
# Comparer le nombre de fichiers
find old-site -type f | wc -l
find new-site -type f | wc -l
```

**Les deux doivent être identiques !**

---

**Comparer les arborescences :**

```bash
# Installer diff si nécessaire
sudo apt install diffutils -y

# Comparer les deux dossiers
diff -rq old-site new-site
```

**Si identiques :**
```
# Aucune sortie = Tout est identique
```

**Si différences :**
```
Files old-site/file1.txt and new-site/file1.txt differ
Only in old-site: file2.txt
```

---

**Comparer avec des checksums (MD5) :**

```bash
# Générer les checksums de l'ancien site
find old-site -type f -exec md5sum {} \; | sort > old-checksums.txt

# Générer les checksums du nouveau site
find new-site -type f -exec md5sum {} \; | sort > new-checksums.txt

# Comparer
diff old-checksums.txt new-checksums.txt
```

**Si aucune différence = Copie parfaite !**

---

### ÉTAPE 4 : Créer des backups datés

**Créer un backup avec la date dans le nom :**

```bash
# Obtenir la date actuelle au format YYYYMMDD
DATE=$(date +%Y%m%d)
echo $DATE
# 20241230

# Créer un backup avec la date
cp -a new-site new-site-backup-$DATE

ls -d new-site*
```

**Résultat :**
```
new-site
new-site-backup-20241230
```

---

**Créer plusieurs backups avec timestamps différents :**

```bash
# Backup avec date ET heure
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
cp -a new-site new-site-backup-$TIMESTAMP

# Backup avec nom descriptif
cp -a new-site new-site-backup-before-migration

# Backup compressé (plus efficace pour l'espace disque)
tar -czf new-site-backup-$TIMESTAMP.tar.gz new-site/
```

**Explication de `date` :**

**Formats courants :**

| Format | Résultat | Usage |
|--------|----------|-------|
| `%Y` | 2024 | Année (4 chiffres) |
| `%m` | 12 | Mois (01-12) |
| `%d` | 30 | Jour (01-31) |
| `%H` | 14 | Heure (00-23) |
| `%M` | 30 | Minutes (00-59) |
| `%S` | 45 | Secondes (00-59) |
| `%Y%m%d` | 20241230 | YYYYMMDD |
| `%Y-%m-%d` | 2024-12-30 | ISO 8601 |
| `%Y%m%d_%H%M%S` | 20241230_143045 | Timestamp complet |

---

**Script de backup automatique :**

```bash
#!/bin/bash
# backup-site.sh

SOURCE="$1"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$SOURCE-backup-$TIMESTAMP"

if [ -z "$SOURCE" ]; then
    echo "Usage: $0 <source-directory>"
    exit 1
fi

if [ ! -d "$SOURCE" ]; then
    echo "Erreur : $SOURCE n'existe pas"
    exit 1
fi

echo "[PACKAGE] Création du backup : $BACKUP_DIR"
cp -av "$SOURCE" "$BACKUP_DIR"

if [ $? -eq 0 ]; then
    echo "[OK] Backup créé avec succès"
else
    echo "[X] Erreur lors du backup"
    exit 1
fi
```

**Utilisation :**

```bash
chmod +x backup-site.sh
./backup-site.sh new-site
```

---

### ÉTAPE 5 : Comprendre la commande `mv` (move)

**Syntaxe de base :**

```
mv [OPTIONS] SOURCE DESTINATION
```

**Options principales :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-i` | Interactif (demander avant écrasement) | `mv -i file1 file2` |
| `-n` | No-clobber (ne jamais écraser) | `mv -n file1 file2` |
| `-u` | Update (déplacer si plus récent) | `mv -u file1 file2` |
| `-v` | Verbose (afficher les actions) | `mv -v file1 file2` |
| `-b` | Backup (créer backup avec ~) | `mv -b file1 file2` |
| `-f` | Force (écraser sans demander) | `mv -f file1 file2` |

**Différences avec `cp` :**

| | `cp` | `mv` |
|---|------|------|
| **Fichier source** | Conservé | Supprimé |
| **Action** | Copie | Déplace ou renomme |
| **Option `-r`** | Nécessaire pour dossiers | Pas nécessaire |
| **Rapidité** | Lent (copie les données) | Rapide (change juste les métadonnées) |

**`mv` est TOUJOURS récursif automatiquement !**

---

**Test 1 : Renommer un fichier**

```bash
cd ~/migration-exercise/new-site

# Renommer index.html en home.html
mv index.html home.html

ls *.html
```

**Résultat :**
```
about.html  contact.html  home.html
```

**`index.html` n'existe plus, remplacé par `home.html`**

---

**Test 2 : Déplacer un fichier vers un autre dossier**

```bash
# Déplacer home.html dans le dossier config
mv home.html config/

ls config/
```

**Résultat :**
```
app.conf  database.conf  home.html
```

---

**Test 3 : Déplacer ET renommer en une seule commande**

```bash
# Ramener home.html à la racine ET le renommer en index.html
mv config/home.html index.html

ls *.html
```

**Résultat :**
```
about.html  contact.html  index.html
```

---

**Test 4 : Déplacer un dossier complet**

```bash
# Créer un nouveau dossier assets
mkdir assets

# Déplacer images, css, js dans assets
mv images assets/
mv css assets/
mv js assets/

tree assets
```

**Résultat :**
```
assets/
├── css
│   ├── mobile.css
│   ├── print.css
│   └── style.css
├── images
│   ├── banner.jpg
│   ├── icon.svg
│   └── logo.png
└── js
    ├── main.js
    └── utils.js
```

**[ATTENTION] Pas besoin de `-r` ! `mv` est toujours récursif.**

---

**Test 5 : Déplacer plusieurs fichiers vers un dossier**

```bash
# Créer un dossier pages
mkdir pages

# Déplacer tous les fichiers HTML dans pages
mv *.html pages/

ls pages/
```

**Résultat :**
```
about.html  contact.html  index.html
```

**[ATTENTION] Avec plusieurs sources, la destination DOIT être un dossier !**

**Erreur si destination = fichier :**
```bash
mv file1.txt file2.txt file3.txt destination.txt
# mv: target 'destination.txt' is not a directory
```

**Correct :**
```bash
mv file1.txt file2.txt file3.txt destination-folder/
```

---

**Test 6 : Renommer un dossier**

```bash
# Renommer "pages" en "html-pages"
mv pages html-pages

ls
```

**`mv` fonctionne exactement pareil pour les fichiers et dossiers !**

---

**Test 7 : Mode interactif avec `-i`**

```bash
# Créer un fichier
echo "Version 1" > file1.txt
echo "Version 2" > file2.txt

# Déplacer avec écrasement potentiel (SANS -i)
mv file2.txt file1.txt
# Écrase file1.txt silencieusement !

# Refaire le test avec -i
echo "Version 1" > file1.txt
echo "Version 2" > file2.txt

mv -i file2.txt file1.txt
# mv: overwrite 'file1.txt'?
```

**Créer un alias pour la sécurité :**

```bash
echo "alias mv='mv -i'" >> ~/.bashrc
source ~/.bashrc
```

---

### ÉTAPE 6 : Réorganiser la structure du nouveau site

**Contexte :** On veut une nouvelle structure plus moderne.

**Objectif :**
```
new-site/
├── public/
│   ├── index.html
│   ├── about.html
│   ├── contact.html
│   └── assets/
│       ├── css/
│       ├── js/
│       └── images/
├── storage/
│   └── uploads/
├── config/
└── logs/
```

---

**Créer la nouvelle structure :**

```bash
cd ~/migration-exercise/new-site

# Créer les dossiers
mkdir -p public/assets
mkdir storage

# Déplacer les HTML dans public
mv html-pages/*.html public/

# Déplacer assets dans public
mv assets/* public/assets/

# Supprimer les anciens dossiers vides
rmdir html-pages assets

# Déplacer uploads dans storage
mv uploads storage/

# Vérifier la nouvelle structure
tree -L 2
```

**Résultat :**
```
.
├── config
│   ├── app.conf
│   └── database.conf
├── logs
│   ├── access.log
│   └── error.log
├── public
│   ├── about.html
│   ├── assets
│   ├── contact.html
│   └── index.html
└── storage
    └── uploads
```

---

### ÉTAPE 7 : Renommer des fichiers en masse

**Contexte :** On veut renommer tous les fichiers `.conf` en `.config`

**Méthode 1 : Boucle bash simple**

```bash
cd ~/migration-exercise/new-site/config

# Renommer .conf en .config
for file in *.conf; do
    mv "$file" "${file%.conf}.config"
done

ls
```

**Résultat :**
```
app.config  database.config
```

**Explication de `${file%.conf}` :**

**Manipulation de variables bash :**

| Syntaxe | Signification | Exemple |
|---------|---------------|---------|
| `${var%suffix}` | Enlever suffix à la fin | `${file%.txt}` -> enlève `.txt` |
| `${var#prefix}` | Enlever prefix au début | `${file#test_}` -> enlève `test_` |
| `${var/old/new}` | Remplacer première occurrence | `${file/old/new}` |
| `${var//old/new}` | Remplacer toutes occurrences | `${file//old/new}` |

**Exemples concrets :**

```bash
file="report_2024.txt"

echo ${file%.txt}        # report_2024
echo ${file%.txt}.pdf    # report_2024.pdf

file="test_script_v2.sh"
echo ${file#test_}       # script_v2.sh

file="2024-12-30-backup.tar.gz"
echo ${file/-/_}         # 2024_12-30-backup.tar.gz
echo ${file//-/_}        # 2024_12_30_backup.tar.gz
```

---

**Méthode 2 : Utiliser `rename` (plus puissant)**

```bash
# Installer rename si nécessaire
sudo apt install rename -y

cd ~/migration-exercise/new-site/config

# Renommer .config en .yml
rename 's/\.config$/.yml/' *.config

ls
```

**Résultat :**
```
app.yml  database.yml
```

**Explication de `rename` :**

**Syntaxe :**
```
rename 's/PATTERN/REPLACEMENT/' FILES
```

**C'est une expression régulière Perl !**

**Exemples utiles :**

```bash
# Remplacer espaces par underscores
rename 's/ /_/g' *

# Passer en minuscules
rename 'y/A-Z/a-z/' *

# Ajouter un préfixe
rename 's/^/prod_/' *.txt

# Ajouter un suffixe
rename 's/$/_backup/' *.sql

# Numéroter des fichiers
rename 's/^/image_/' *.jpg
# Puis utiliser un compteur (plus complexe)
```

---

**Méthode 3 : Script de renommage avancé**

```bash
#!/bin/bash
# mass-rename.sh

# Renommer tous les fichiers dans un dossier
# Usage: ./mass-rename.sh <directory> <old-extension> <new-extension>

DIR="$1"
OLD_EXT="$2"
NEW_EXT="$3"

if [ $# -ne 3 ]; then
    echo "Usage: $0 <directory> <old-ext> <new-ext>"
    echo "Exemple: $0 ./docs txt md"
    exit 1
fi

cd "$DIR" || exit 1

count=0
for file in *."$OLD_EXT"; do
    if [ -f "$file" ]; then
        newname="${file%.$OLD_EXT}.$NEW_EXT"
        mv -v "$file" "$newname"
        ((count++))
    fi
done

echo "[OK] $count fichiers renommés"
```

**Utilisation :**

```bash
chmod +x mass-rename.sh
./mass-rename.sh ~/migration-exercise/new-site/config yml conf
```

---

### ÉTAPE 8 : Vérifier l'intégrité de la migration

**Compter les fichiers :**

```bash
# Dans l'ancien site
find ~/migration-exercise/old-site -type f | wc -l

# Dans le nouveau site
find ~/migration-exercise/new-site -type f | wc -l
```

**Les nombres doivent correspondre !**

---

**Vérifier les checksums :**

```bash
cd ~/migration-exercise

# Générer les checksums de l'ancien site
find old-site -type f -exec md5sum {} \; | \
    sed 's|old-site/||' | sort > old-md5.txt

# Générer les checksums du nouveau site
find new-site -type f -exec md5sum {} \; | \
    sed 's|new-site/[^/]*/||' | sort > new-md5.txt

# Comparer (en ignorant les chemins différents)
diff <(cut -d' ' -f1 old-md5.txt | sort) \
     <(cut -d' ' -f1 new-md5.txt | sort)
```

**Si aucune différence = Tous les fichiers sont identiques !**

---

**Vérifier les permissions :**

```bash
# Comparer les permissions
find old-site -type f -exec stat -c "%a %n" {} \; | sort > old-perms.txt
find new-site -type f -exec stat -c "%a %n" {} \; | sort > new-perms.txt

diff old-perms.txt new-perms.txt
```

---

### [OK] TESTS DE VALIDATION

**1. Tous les fichiers sont copiés**

```bash
OLD_COUNT=$(find ~/migration-exercise/old-site -type f | wc -l)
NEW_COUNT=$(find ~/migration-exercise/new-site -type f | wc -l)

if [ "$OLD_COUNT" -eq "$NEW_COUNT" ]; then
    echo "[OK] Nombre de fichiers identique : $OLD_COUNT"
else
    echo "[X] Nombre différent : $OLD_COUNT vs $NEW_COUNT"
fi
```

---

**2. Aucun fichier perdu**

```bash
# Générer la liste des fichiers de chaque côté
find old-site -type f | sed 's|old-site/||' | sort > old-files.txt
find new-site -type f | sed 's|new-site/[^/]*/||' | sort > new-files.txt

# Chercher les fichiers manquants
comm -23 old-files.txt new-files.txt
```

**Si aucune sortie = Aucun fichier perdu !**

---

**3. Les permissions sont préservées**

```bash
# Vérifier qu'un fichier exécutable reste exécutable
OLD_PERM=$(stat -c "%a" old-site/config/app.conf)
NEW_PERM=$(stat -c "%a" new-site/config/app.yml)

echo "Old: $OLD_PERM"
echo "New: $NEW_PERM"
```

---

**4. Les dates sont préservées (si utilisé -p)**

```bash
# Comparer les dates de modification
OLD_DATE=$(stat -c "%Y" old-site/index.html)
NEW_DATE=$(stat -c "%Y" new-site/public/index.html)

if [ "$OLD_DATE" -eq "$NEW_DATE" ]; then
    echo "[OK] Dates identiques"
else
    echo "[ATTENTION] Dates différentes"
fi
```

---

**5. Les backups existent**

```bash
ls -lh ~/migration-exercise/ | grep backup
```

**Doit afficher au moins 1 backup.**

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "cp: omitting directory"

**Symptôme :**
```bash
cp old-site new-site
cp: omitting directory 'old-site'
```

**Cause :** Oubli de l'option `-r` pour copier un dossier.

**Solution :**
```bash
cp -r old-site new-site
```

---

#### Erreur 2 : Écrasement accidentel

**Symptôme :**
```bash
mv important-file.txt backup-file.txt
# Écrase backup-file.txt sans prévenir !
```

**Solution :**
```bash
# Utiliser -i (interactif)
mv -i important-file.txt backup-file.txt

# Ou -n (never overwrite)
mv -n important-file.txt backup-file.txt

# Créer un alias permanent
echo "alias mv='mv -i'" >> ~/.bashrc
```

---

#### Erreur 3 : Espace disque insuffisant

**Symptôme :**
```bash
cp -r huge-directory backup/
cp: error writing 'backup/huge-directory/file': No space left on device
```

**Solution :**

```bash
# Vérifier l'espace disponible
df -h

# Nettoyer si nécessaire
sudo apt clean
sudo journalctl --vacuum-time=7d

# Ou copier vers un autre disque
cp -r huge-directory /mnt/external-drive/
```

---

#### Erreur 4 : Permissions insuffisantes

**Symptôme :**
```bash
cp file.txt /var/www/
cp: cannot create regular file '/var/www/file.txt': Permission denied
```

**Solution :**

```bash
# Option 1 : Utiliser sudo
sudo cp file.txt /var/www/

# Option 2 : Changer les permissions du dossier de destination
sudo chown $USER:$USER /var/www/
```

---

#### Erreur 5 : Déplacer plusieurs fichiers vers un fichier (au lieu d'un dossier)

**Symptôme :**
```bash
mv file1.txt file2.txt file3.txt destination.txt
mv: target 'destination.txt' is not a directory
```

**Cause :** Avec plusieurs sources, la destination DOIT être un dossier.

**Solution :**
```bash
# Créer un dossier
mkdir destination-folder

# Déplacer dedans
mv file1.txt file2.txt file3.txt destination-folder/
```

---

#### Erreur 6 : Perdre les permissions lors de la copie

**Symptôme :**
```bash
# Script exécutable avant copie
-rwxr-xr-x script.sh

# Après copie normale
cp script.sh backup/
ls -l backup/script.sh
-rw-r--r-- script.sh  # Plus exécutable !
```

**Solution :**
```bash
# Utiliser -p (preserve) ou -a (archive)
cp -p script.sh backup/
# ou
cp -a script.sh backup/
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Commande `cp` (copy)**
- Copie des fichiers/dossiers
- Source reste intacte
- `-r` obligatoire pour les dossiers
- `-p` pour préserver permissions/dates
- `-a` pour copie archive complète

**2. Commande `mv` (move)**
- Déplace OU renomme
- Source est supprimée
- Toujours récursif (pas besoin de `-r`)
- Rapide (change juste les métadonnées)

**3. Options de sécurité**
- `-i` : Demander avant d'écraser (interactif)
- `-n` : Ne jamais écraser (no-clobber)
- `-u` : Copier/déplacer si plus récent (update)

**4. Wildcards**
- `*` : N'importe quelle suite
- `?` : Exactement 1 caractère
- `[abc]` : Un parmi a, b, c

**5. Vérification d'intégrité**
- Compter les fichiers : `find -type f | wc -l`
- Checksums : `md5sum`
- Comparer dossiers : `diff -rq`

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Copie avec progression (rsync)**

```bash
# rsync = cp amélioré avec progression
rsync -av --progress source/ destination/
```

**Avantages de rsync :**
- Affiche la progression
- Reprend après interruption
- Copie seulement les différences
- Synchronisation bidirectionnelle

---

**2. Copie avec exclusions**

```bash
# Exclure certains fichiers/dossiers
rsync -av --exclude='*.log' --exclude='node_modules' source/ dest/

# Ou avec cp (plus complexe)
find source -type f ! -name "*.log" -exec cp --parents {} dest/ \;
```

---

**3. Copie en préservant les liens symboliques**

```bash
# -d = Préserver les liens symboliques
cp -a source/ dest/

# Ou
rsync -av --links source/ dest/
```

---

**4. Copie réseau (entre serveurs)**

```bash
# SCP (Secure Copy)
scp -r /local/path user@server:/remote/path

# Rsync over SSH
rsync -avz -e ssh /local/path user@server:/remote/path
```

---

**5. Script de migration automatique**

```bash
#!/bin/bash
# migrate-site.sh

SOURCE="$1"
DEST="$2"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP="$DEST-backup-$TIMESTAMP"

# Vérifications
if [ -z "$SOURCE" ] || [ -z "$DEST" ]; then
    echo "Usage: $0 <source> <destination>"
    exit 1
fi

if [ ! -d "$SOURCE" ]; then
    echo "[X] Source n'existe pas : $SOURCE"
    exit 1
fi

# Backup si destination existe
if [ -d "$DEST" ]; then
    echo "[PACKAGE] Backup de la destination existante..."
    mv "$DEST" "$BACKUP"
fi

# Migration
echo "[RAPIDE] Migration en cours..."
cp -av "$SOURCE" "$DEST"

# Vérification
SOURCE_COUNT=$(find "$SOURCE" -type f | wc -l)
DEST_COUNT=$(find "$DEST" -type f | wc -l)

if [ "$SOURCE_COUNT" -eq "$DEST_COUNT" ]; then
    echo "[OK] Migration réussie : $SOURCE_COUNT fichiers"
else
    echo "[X] Erreur : $SOURCE_COUNT fichiers source, $DEST_COUNT destination"
    exit 1
fi

# Générer rapport
echo "[GRAPHIQUE] Rapport de migration" > migration-report.txt
echo "Date: $(date)" >> migration-report.txt
echo "Source: $SOURCE" >> migration-report.txt
echo "Destination: $DEST" >> migration-report.txt
echo "Fichiers: $SOURCE_COUNT" >> migration-report.txt
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1.2

**[OK] Félicitations ! Tu maîtrises maintenant la copie et le déplacement de fichiers !**

**Ce que tu as appris :**
- Copier des fichiers avec `cp` (simple et récursif)
- Déplacer et renommer avec `mv`
- Préserver les permissions et dates
- Faire des backups datés
- Réorganiser une arborescence
- Renommer en masse
- Vérifier l'intégrité

**Compétences acquises :**
- [OK] Manipulation de fichiers (niveau intermédiaire)
- [OK] Gestion de backups
- [OK] Migration de données
- [OK] Renommage en masse

**Temps moyen de réalisation :** 45 minutes - 1 heure

**Prochaine étape :** Exercice 1.3 - Suppression sécurisée ! [SUPPRIMER]

---

Je continue avec les exercices 1.3, 1.4 et 1.5 ?

# [JAUNE] EXERCICE 1.3 : SUPPRESSION SÉCURISÉE DE FICHIERS

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois nettoyer un serveur qui contient des fichiers temporaires, des logs obsolètes, des backups expirés et des artefacts de compilation. 

**Problématique :** Une suppression incorrecte peut :
- Effacer des fichiers critiques par erreur
- Rendre le système instable
- Causer des pertes de données irréversibles

Ta mission : Nettoyer le serveur de manière sécurisée et méthodique.

### Cahier des charges

- Supprimer 500+ fichiers temporaires
- Nettoyer les logs de plus de 30 jours
- Supprimer les dossiers vides
- Effacer les fichiers de cache (node_modules, __pycache__, etc.)
- Garder une trace de tout ce qui est supprimé
- Implémenter une "corbeille" avant suppression définitive
- Créer des sauvegardes avant toute suppression importante

### Contraintes

- Commandes : `rm`, `rmdir`, `find`
- Mode interactif obligatoire pour fichiers critiques
- Logs de toutes les suppressions
- Possibilité de récupération pendant 7 jours
- Temps estimé : 1 heure

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Supprimer des fichiers de manière sécurisée
- [OK] Comprendre les dangers de `rm -rf`
- [OK] Utiliser le mode interactif (`-i`)
- [OK] Supprimer des dossiers vides (`rmdir`)
- [OK] Créer une corbeille système
- [OK] Supprimer en masse avec `find`
- [OK] Récupérer des fichiers supprimés (si possible)
- [OK] Automatiser le nettoyage

---

## [DOCS] PRÉREQUIS

- Exercices 1.1 et 1.2 terminés
- Compréhension des permissions
- Notions de base sur les chemins

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre la commande `rm` (remove)

**[ATTENTION] AVERTISSEMENT CRITIQUE :**

`rm` est une des commandes les PLUS DANGEREUSES sous Linux !

**Pourquoi ?**
- Suppression DÉFINITIVE (pas de corbeille)
- Pas de confirmation par défaut
- `rm -rf /` peut DÉTRUIRE tout le système
- Aucun moyen de récupération simple

**Histoires d'horreur réelles :**

```bash
# Erreur catastrophique (2016, GitLab)
rm -rf /var/opt/gitlab/git-data/repositories

# Erreur d'un développeur Steam (2015)
rm -rf "$STEAMROOT/"*
# Si $STEAMROOT était vide -> rm -rf /*

# Erreur pixar (1998)
# Suppression accidentelle de Toy Story 2
# Sauvé grâce à un backup sur le PC d'une employée en congé maternité
```

---

**Syntaxe de base :**

```
rm [OPTIONS] FICHIERS
```

**Options principales :**

| Option | Description | Danger | Exemple |
|--------|-------------|---------|---------|
| `-i` | Interactif (demander) | [VERT] Sûr | `rm -i file.txt` |
| `-I` | Demander si >3 fichiers | [JAUNE] Moyen | `rm -I *.txt` |
| `-f` | Force (pas de confirmation) | [ROUGE] Dangereux | `rm -f file.txt` |
| `-r` ou `-R` | Récursif (dossiers) | [ROUGE] Très dangereux | `rm -r dir/` |
| `-d` | Supprimer dossiers vides | [JAUNE] Moyen | `rm -d emptydir/` |
| `-v` | Verbose (afficher) | [VERT] Utile | `rm -v file.txt` |
| `--preserve-root` | Protéger `/` | [VERT] Sécurité | `rm --preserve-root` |

---

**[ATTENTION] LA COMBINAISON LA PLUS DANGEREUSE : `rm -rf`**

```bash
# NE JAMAIS FAIRE ÇA !
rm -rf /
# Détruit TOUT le système

# Erreur courante
rm -rf / home/user/temp
#       ^ Espace accidentel = CATASTROPHE
```

**Protection par défaut sur les systèmes modernes :**

```bash
# Tester (sera bloqué)
rm -rf /
# rm: it is dangerous to operate recursively on '/'
# rm: use --no-preserve-root to override this failsafe
```

---

### ÉTAPE 2 : Créer un environnement de test

**Créer une structure avec des fichiers à supprimer :**

```bash
# Créer le dossier de test
mkdir -p ~/cleanup-exercise
cd ~/cleanup-exercise

# Créer une structure complexe
mkdir -p {temp,logs,cache,backups,old-projects}
mkdir -p temp/{session-{1..10},uploads}
mkdir -p logs/{apache,nginx,mysql,app}/{2023,2024}
mkdir -p cache/{npm,pip,browser}
mkdir -p backups/{daily,weekly,monthly}

# Créer des fichiers temporaires
for i in {1..50}; do
    touch temp/session-$((RANDOM % 10 + 1))/file-$i.tmp
done

# Créer des logs avec dates
for month in {01..12}; do
    echo "Log data" > logs/apache/2023/access-2023-$month.log
    echo "Log data" > logs/apache/2024/access-2024-$month.log
done

# Créer des fichiers de cache
for i in {1..30}; do
    echo "cache" > cache/npm/cache-$i.tmp
    echo "cache" > cache/pip/package-$i.cache
done

# Créer des backups datés
for day in {1..30}; do
    date=$(date -d "2024-12-$day" +%Y%m%d 2>/dev/null || echo "20241230")
    touch backups/daily/backup-$date.tar.gz
done

# Créer des projets old
mkdir -p old-projects/{project-{1..5}/{src,build,node_modules}}
for proj in {1..5}; do
    touch old-projects/project-$proj/src/main.js
    # Simuler node_modules avec beaucoup de fichiers
    mkdir -p old-projects/project-$proj/node_modules/.bin
    for i in {1..20}; do
        touch old-projects/project-$proj/node_modules/package-$i.js
    done
done

# Créer quelques dossiers vides
mkdir -p empty-{1..5}

# Afficher la structure
echo "Structure créée :"
tree -L 2
```

---

### ÉTAPE 3 : Suppression simple et sécurisée

**Test 1 : Supprimer un fichier unique**

```bash
cd ~/cleanup-exercise

# Créer un fichier de test
echo "Test" > test-file.txt

# Supprimer avec confirmation (-i)
rm -i test-file.txt
# rm: remove regular file 'test-file.txt'? y
```

**Explication de `-i` (interactive) :**

`-i` = Demande confirmation avant CHAQUE suppression

**Réponses possibles :**
- `y` ou `yes` = Oui, supprimer
- `n` ou `no` = Non, conserver

**Créer un alias permanent pour la sécurité :**

```bash
# Ajouter dans ~/.bashrc
echo "alias rm='rm -i'" >> ~/.bashrc
source ~/.bashrc

# Maintenant, rm demande TOUJOURS confirmation
```

---

**Test 2 : Supprimer plusieurs fichiers**

```bash
# Créer plusieurs fichiers
touch file1.txt file2.txt file3.txt

# Supprimer avec -i (demande pour CHAQUE fichier)
rm -i file*.txt
# rm: remove regular file 'file1.txt'? y
# rm: remove regular file 'file2.txt'? y
# rm: remove regular file 'file3.txt'? y
```

**C'est fastidieux pour beaucoup de fichiers !**

---

**Test 3 : Option `-I` (majuscule) - Plus pratique**

```bash
# Créer 10 fichiers
touch test-{1..10}.txt

# -I = Demande UNE SEULE FOIS si >3 fichiers
rm -I test-*.txt
# rm: remove 10 arguments? y
```

**Explication de `-I` (majuscule) :**

`-I` = Interactive (smart)

**Comportement :**
- Si ≤3 fichiers : Pas de confirmation (supprime directement)
- Si >3 fichiers : Demande une fois pour tous

**Plus pratique que `-i` pour les suppressions en masse !**

---

**Test 4 : Mode verbose avec `-v`**

```bash
touch file1.txt file2.txt

rm -v file1.txt file2.txt
```

**Résultat :**
```
removed 'file1.txt'
removed 'file2.txt'
```

**`-v` affiche chaque fichier supprimé.**

**Très utile pour :**
- Vérifier ce qui est supprimé
- Logs d'audit
- Déboguer les scripts

---

**Test 5 : Forcer la suppression avec `-f`**

```bash
# Créer un fichier en lecture seule
echo "Important" > protected.txt
chmod 444 protected.txt  # Lecture seule

# Essayer de supprimer normalement
rm protected.txt
# rm: remove write-protected regular file 'protected.txt'? 
```

**Par défaut, rm demande confirmation pour les fichiers protégés.**

```bash
# Forcer avec -f (force)
rm -f protected.txt
# Supprime SANS demander, même si protégé
```

**[ATTENTION] `-f` est DANGEREUX !**

**Ne jamais utiliser `-f` sauf si tu es ABSOLUMENT SÛR.**

---

### ÉTAPE 4 : Supprimer des dossiers

**Méthode 1 : `rmdir` (seulement dossiers VIDES)**

```bash
cd ~/cleanup-exercise

# Tenter de supprimer un dossier non vide
rmdir temp/
# rmdir: failed to remove 'temp/': Directory not empty
```

**`rmdir` refuse de supprimer un dossier non vide !**

**Supprimer un dossier vide :**

```bash
rmdir empty-1/
# Succès, pas d'erreur

# Vérifier
ls -d empty-*
```

**Supprimer plusieurs dossiers vides :**

```bash
rmdir empty-{2..5}
# Supprime tous les dossiers vides d'un coup

# Ou
rmdir empty-*
```

**Explication de `rmdir` :**

**Sécurité : `rmdir` ne peut PAS supprimer un dossier contenant des fichiers !**

**Avantage :**
- Protection contre les suppressions accidentelles
- Parfait pour nettoyer les dossiers vides

**Inconvénient :**
- Inutile pour les dossiers avec contenu

---

**Méthode 2 : `rm -r` (récursif - DANGEREUX)**

```bash
# [ATTENTION] ATTENTION : Supprime le dossier ET tout son contenu !

# Supprimer un petit dossier de test
mkdir -p test-dir/sub-dir
touch test-dir/file.txt
touch test-dir/sub-dir/file2.txt

# Supprimer récursivement
rm -r test-dir/
```

**`-r` = Recursive (descend dans tous les sous-dossiers)**

**SUPPRIME TOUT :**
- Le dossier
- Tous les sous-dossiers
- Tous les fichiers
- **SANS POSSIBILITÉ DE RÉCUPÉRATION**

---

**[ATTENTION] LA COMBINAISON NUCLÉAIRE : `rm -rf`**

```bash
# LA COMMANDE LA PLUS DANGEREUSE !
rm -rf directory/
```

**Signification :**
- `-r` = Récursif (tout supprimer)
- `-f` = Force (pas de confirmation)

**Résultat :**
- Supprime TOUT
- SANS demander
- AUCUN avertissement
- IRRÉVERSIBLE

**Exemples catastrophiques :**

```bash
# Erreur de typo
rm -rf /home /user/temp
#         ^ Espace au lieu de /home/user/temp
# Supprime /home ET /user/temp !

# Variable vide
DIR="/important/data"
# ... (code qui devrait définir DIR)
rm -rf $DIR/*
# Si DIR est vide -> rm -rf /*  [!]

# Mauvais chemin
cd /var/www/site
rm -rf ./*
# Mais si cd a échoué, tu es encore dans /root !
```

---

### ÉTAPE 5 : Créer une "Corbeille" système

**Pourquoi une corbeille ?**
- Récupération possible en cas d'erreur
- Suppression définitive différée
- Audit des fichiers supprimés

**Créer le système de corbeille :**

```bash
# Créer le dossier corbeille
mkdir -p ~/.trash

# Créer une fonction "safe_rm"
cat >> ~/.bashrc << 'EOF'

# Fonction de suppression sécurisée
safe_rm() {
    TRASH_DIR="$HOME/.trash"
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    
    # Créer le dossier corbeille si inexistant
    mkdir -p "$TRASH_DIR"
    
    # Pour chaque fichier à supprimer
    for item in "$@"; do
        if [ -e "$item" ]; then
            # Nom avec timestamp pour éviter les collisions
            basename=$(basename "$item")
            dest="$TRASH_DIR/${basename}_${TIMESTAMP}"
            
            echo "[SUPPRIMER]  Déplacé vers corbeille : $item -> $dest"
            mv "$item" "$dest"
        else
            echo "[X] N'existe pas : $item"
        fi
    done
}

# Alias
alias trash='safe_rm'

EOF

# Recharger .bashrc
source ~/.bashrc
```

**Utilisation :**

```bash
# Créer un fichier de test
echo "Important data" > important.txt

# "Supprimer" (déplacer vers corbeille)
trash important.txt

# Le fichier n'est pas supprimé, juste déplacé
ls ~/.trash/
# important.txt_20241230_143000
```

---

**Voir le contenu de la corbeille :**

```bash
ls -lh ~/.trash/
```

---

**Récupérer un fichier :**

```bash
# Lister les fichiers dans la corbeille
ls ~/.trash/

# Restaurer un fichier
mv ~/.trash/important.txt_20241230_143000 ./important.txt

echo "[OK] Fichier restauré"
```

---

**Vider la corbeille (suppression définitive) :**

```bash
# Fonction pour vider la corbeille
empty_trash() {
    TRASH_DIR="$HOME/.trash"
    
    if [ ! -d "$TRASH_DIR" ]; then
        echo "[OK] Corbeille déjà vide"
        return
    fi
    
    COUNT=$(find "$TRASH_DIR" -type f | wc -l)
    
    if [ "$COUNT" -eq 0 ]; then
        echo "[OK] Corbeille vide (0 fichiers)"
        return
    fi
    
    echo "[ATTENTION]  $COUNT fichiers dans la corbeille"
    read -p "Vider définitivement ? (y/N) " confirm
    
    if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
        rm -rf "$TRASH_DIR"/*
        echo "[OK] Corbeille vidée"
    else
        echo "[X] Annulé"
    fi
}

# Ajouter à ~/.bashrc
declare -f empty_trash >> ~/.bashrc
```

**Utilisation :**

```bash
empty_trash
```

---

**Nettoyer automatiquement la corbeille après 7 jours :**

```bash
# Créer un script de nettoyage automatique
cat > ~/.clean-trash.sh << 'EOF'
#!/bin/bash
TRASH_DIR="$HOME/.trash"
DAYS=7

# Supprimer les fichiers de plus de 7 jours
find "$TRASH_DIR" -type f -mtime +$DAYS -delete

echo "[SUPPRIMER]  Corbeille nettoyée (fichiers >$DAYS jours supprimés)"
EOF

chmod +x ~/.clean-trash.sh

# Ajouter dans crontab (tous les jours à 2h du matin)
(crontab -l 2>/dev/null; echo "0 2 * * * ~/.clean-trash.sh") | crontab -
```

---

### ÉTAPE 6 : Suppression en masse avec `find`

**`find` est BEAUCOUP plus puissant que `rm` seul !**

**Supprimer tous les fichiers `.tmp` :**

```bash
cd ~/cleanup-exercise

# Trouver tous les .tmp
find . -name "*.tmp"

# Supprimer (méthode 1 : -delete)
find . -name "*.tmp" -delete

# Supprimer (méthode 2 : -exec)
find . -name "*.tmp" -exec rm {} \;

# Supprimer (méthode 3 : xargs)
find . -name "*.tmp" | xargs rm
```

**Explication des méthodes :**

**Méthode 1 : `-delete` (la plus simple)**

```bash
find . -name "*.tmp" -delete
```

**Avantages :**
- Simple et direct
- Pas besoin de `-exec` ou `xargs`
- Rapide

**Inconvénients :**
- Supprime SANS confirmation
- Pas de retour visuel

---

**Méthode 2 : `-exec rm {} \;` (la plus flexible)**

```bash
find . -name "*.tmp" -exec rm {} \;
```

**Explication :**
- `-exec` : Exécute une commande pour chaque résultat
- `rm {}` : `{}` est remplacé par le nom du fichier
- `\;` : Termine la commande (échappement du `;`)

**Mode interactif :**

```bash
find . -name "*.tmp" -exec rm -i {} \;
# Demande confirmation pour CHAQUE fichier
```

**Mode verbose :**

```bash
find . -name "*.tmp" -exec rm -v {} \;
# Affiche chaque fichier supprimé
```

---

**Méthode 3 : `xargs` (la plus efficace)**

```bash
find . -name "*.tmp" | xargs rm
```

**Explication :**
- `find` génère une liste de fichiers
- `|` (pipe) envoie la liste à `xargs`
- `xargs` groupe les fichiers et exécute `rm` UNE SEULE FOIS

**Avantage :**
- Beaucoup plus rapide pour beaucoup de fichiers
- `rm` est appelé moins souvent

**Exemple :**

```bash
# Avec -exec : rm appelé 100 fois pour 100 fichiers
find . -name "*.tmp" -exec rm {} \;

# Avec xargs : rm appelé 1 fois avec les 100 fichiers
find . -name "*.tmp" | xargs rm
```

**[ATTENTION] Problème avec les espaces dans les noms :**

```bash
# Créer un fichier avec espace
touch "mon fichier.tmp"

# Avec xargs (ERREUR)
find . -name "*.tmp" | xargs rm
# rm: cannot remove 'mon': No such file or directory
# rm: cannot remove 'fichier.tmp': No such file or directory

# Solution : -print0 et -0
find . -name "*.tmp" -print0 | xargs -0 rm
```

**Explication :**
- `-print0` : Sépare les résultats par un caractère NULL (pas espace)
- `-0` : `xargs` s'attend à un séparateur NULL

---

**Supprimer les fichiers de plus de 30 jours :**

```bash
# Trouver les fichiers modifiés il y a plus de 30 jours
find . -type f -mtime +30

# Supprimer avec confirmation
find . -type f -mtime +30 -exec rm -i {} \;

# Supprimer sans confirmation (DANGEREUX)
find . -type f -mtime +30 -delete
```

**Explication de `-mtime` :**

`-mtime +N` = Modified time (date de modification)

**Syntaxe :**
- `-mtime +N` : Plus de N jours
- `-mtime -N` : Moins de N jours
- `-mtime N` : Exactement N jours

**Exemples :**

```bash
# Fichiers modifiés il y a plus de 7 jours
find . -mtime +7

# Fichiers modifiés dans les dernières 24 heures
find . -mtime -1

# Fichiers modifiés il y a exactement 5 jours
find . -mtime 5
```

**Autres options temporelles :**

| Option | Signification | Exemple |
|--------|---------------|---------|
| `-mtime` | Modification | Contenu changé |
| `-atime` | Access | Fichier lu |
| `-ctime` | Change | Métadonnées changées (permissions, etc.) |
| `-mmin` | Minutes (modification) | `-mmin +60` = >1 heure |
| `-amin` | Minutes (accès) | `-amin -30` = <30 min |

---

**Supprimer les fichiers de plus de 1 GB :**

```bash
# Fichiers de plus de 1 GB
find . -type f -size +1G -exec rm -i {} \;

# Fichiers entre 100 MB et 500 MB
find . -type f -size +100M -size -500M
```

**Syntaxe de `-size` :**

| Unité | Signification | Exemple |
|-------|---------------|---------|
| `c` | Bytes | `-size 100c` |
| `k` | Kilobytes | `-size +100k` |
| `M` | Megabytes | `-size +10M` |
| `G` | Gigabytes | `-size +1G` |

---

**Supprimer tous les `node_modules` :**

```bash
# Trouver tous les dossiers node_modules
find . -type d -name "node_modules"

# Compter combien d'espace ils occupent
find . -type d -name "node_modules" -exec du -sh {} \;

# Supprimer (ATTENTION !)
find . -type d -name "node_modules" -exec rm -rf {} +
```

**Explication de `{}+` vs `{}\;` :**

**`{} \;`** = Exécute la commande pour CHAQUE résultat
```bash
find . -name "*.txt" -exec echo "Fichier : {}" \;
# Affiche :
# Fichier : ./file1.txt
# Fichier : ./file2.txt
```

**`{} +`** = Groupe les résultats et exécute UNE FOIS
```bash
find . -name "*.txt" -exec echo "Fichiers : {}" +
# Affiche :
# Fichiers : ./file1.txt ./file2.txt
```

**`+` est plus efficace pour les suppressions en masse !**

---

### ÉTAPE 7 : Nettoyer les logs de manière sécurisée

**Scénario : Supprimer les logs de plus de 30 jours**

```bash
cd ~/cleanup-exercise/logs

# D'abord, COMPTER ce qui sera supprimé
echo "[GRAPHIQUE] Logs de plus de 30 jours :"
find . -name "*.log" -mtime +30 | wc -l

# LISTER les fichiers qui seront supprimés
echo "[LISTE] Liste des fichiers :"
find . -name "*.log" -mtime +30

# CALCULER l'espace qui sera libéré
echo "[SAUVEGARDE] Espace qui sera libéré :"
find . -name "*.log" -mtime +30 -exec du -ch {} + | tail -1

# SAUVEGARDER la liste avant suppression
find . -name "*.log" -mtime +30 > ~/deleted-logs-$(date +%Y%m%d).txt

# DEMANDER confirmation
read -p "Supprimer ces fichiers ? (y/N) " confirm

if [ "$confirm" = "y" ]; then
    # Supprimer avec verbose pour voir chaque fichier
    find . -name "*.log" -mtime +30 -exec rm -v {} \;
    echo "[OK] Suppression terminée"
else
    echo "[X] Annulé"
fi
```

---

**Script de nettoyage automatique des logs :**

```bash
#!/bin/bash
# clean-logs.sh

LOG_DIR="/var/log"
DAYS=30
ARCHIVE_DIR="/backup/old-logs"

# Créer l'archive si elle n'existe pas
mkdir -p "$ARCHIVE_DIR"

# Archiver les vieux logs avant de les supprimer
echo "[PACKAGE] Archivage des logs de plus de $DAYS jours..."
find "$LOG_DIR" -name "*.log" -mtime +$DAYS -exec tar -czf "$ARCHIVE_DIR/logs-$(date +%Y%m%d).tar.gz" {} +

# Supprimer
echo "[SUPPRIMER]  Suppression des logs archivés..."
find "$LOG_DIR" -name "*.log" -mtime +$DAYS -delete

echo "[OK] Nettoyage terminé"
```

---

### ÉTAPE 8 : Supprimer les dossiers vides

**Trouver tous les dossiers vides :**

```bash
cd ~/cleanup-exercise

# Méthode 1 : find avec -empty
find . -type d -empty

# Méthode 2 : find avec vérification manuelle
find . -type d -exec sh -c '[ -z "$(ls -A "$1")" ]' _ {} \; -print
```

**Supprimer tous les dossiers vides :**

```bash
# Méthode 1 : Simple
find . -type d -empty -delete

# Méthode 2 : Avec confirmation
find . -type d -empty -exec rmdir -v {} \;
```

**[ATTENTION] Problème : Dossiers vides APRÈS suppression de leur contenu**

```bash
# Exemple :
# dir/
#   subdir/
#     file.txt

# Si on supprime file.txt, subdir devient vide
# Mais dir n'est pas vide (contient subdir)

# Solution : Boucle jusqu'à ce qu'il n'y ait plus de dossiers vides
while find . -type d -empty -delete | grep -q .; do
    echo "Dossiers vides supprimés, recherche de nouveaux..."
done

echo "[OK] Tous les dossiers vides ont été supprimés"
```

---

### ÉTAPE 9 : Créer un script de nettoyage complet

```bash
#!/bin/bash
# cleanup-system.sh - Nettoyage complet et sécurisé

set -e  # Arrêter en cas d'erreur

# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Configuration
TRASH_DIR="$HOME/.trash"
LOG_FILE="$HOME/cleanup-$(date +%Y%m%d_%H%M%S).log"
DRY_RUN=false

# Fonction de log
log() {
    echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

warn() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}

# Fonction de suppression sécurisée
safe_delete() {
    local file="$1"
    
    if [ "$DRY_RUN" = true ]; then
        echo "DRY-RUN: Supprimerais $file"
        return
    fi
    
    # Déplacer vers corbeille au lieu de supprimer
    mkdir -p "$TRASH_DIR"
    mv "$file" "$TRASH_DIR/$(basename "$file")_$(date +%s)"
    log "Déplacé vers corbeille : $file"
}

# Fonction principale de nettoyage
cleanup() {
    log "[NETTOYAGE] Début du nettoyage"
    
    # 1. Supprimer les fichiers temporaires
    log "[DOSSIER] Nettoyage des fichiers temporaires..."
    COUNT=$(find /tmp -type f -mtime +7 2>/dev/null | wc -l)
    if [ "$COUNT" -gt 0 ]; then
        find /tmp -type f -mtime +7 -exec rm -f {} \; 2>/dev/null
        log "[OK] $COUNT fichiers temporaires supprimés"
    else
        log "[OK] Aucun fichier temporaire à supprimer"
    fi
    
    # 2. Nettoyer le cache système
    if [ -d "$HOME/.cache" ]; then
        log "[SAUVEGARDE] Nettoyage du cache utilisateur..."
        BEFORE=$(du -sh "$HOME/.cache" | cut -f1)
        find "$HOME/.cache" -type f -atime +30 -delete
        AFTER=$(du -sh "$HOME/.cache" | cut -f1)
        log "[OK] Cache nettoyé : $BEFORE -> $AFTER"
    fi
    
    # 3. Supprimer les dossiers vides
    log "[DOSSIER] Suppression des dossiers vides..."
    EMPTY_DIRS=$(find "$HOME" -type d -empty 2>/dev/null | wc -l)
    if [ "$EMPTY_DIRS" -gt 0 ]; then
        find "$HOME" -type d -empty -delete 2>/dev/null
        log "[OK] $EMPTY_DIRS dossiers vides supprimés"
    fi
    
    # 4. Nettoyer la corbeille (>7 jours)
    log "[SUPPRIMER]  Nettoyage de la corbeille..."
    if [ -d "$TRASH_DIR" ]; then
        TRASH_COUNT=$(find "$TRASH_DIR" -type f -mtime +7 2>/dev/null | wc -l)
        if [ "$TRASH_COUNT" -gt 0 ]; then
            find "$TRASH_DIR" -type f -mtime +7 -delete
            log "[OK] $TRASH_COUNT fichiers supprimés de la corbeille"
        fi
    fi
    
    # 5. Rapport final
    log "[GRAPHIQUE] Rapport de nettoyage :"
    log "   - Fichiers temporaires : $COUNT"
    log "   - Dossiers vides : $EMPTY_DIRS"
    log "   - Corbeille : $TRASH_COUNT"
    log "[OK] Nettoyage terminé"
    log "[FICHIER] Log complet : $LOG_FILE"
}

# Vérifier si mode dry-run
if [ "$1" = "--dry-run" ]; then
    DRY_RUN=true
    warn "Mode DRY-RUN activé (aucune suppression réelle)"
fi

# Demander confirmation
echo "[ATTENTION]  Ce script va nettoyer :"
echo "   - Fichiers temporaires >7 jours"
echo "   - Cache >30 jours"
echo "   - Dossiers vides"
echo "   - Corbeille >7 jours"
echo ""
read -p "Continuer ? (y/N) " confirm

if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
    cleanup
else
    error "Annulé par l'utilisateur"
    exit 1
fi
```

**Rendre exécutable et tester :**

```bash
chmod +x cleanup-system.sh

# Test en mode dry-run (aucune suppression)
./cleanup-system.sh --dry-run

# Exécution réelle
./cleanup-system.sh
```

---

### [OK] TESTS DE VALIDATION

**1. Vérifier que les fichiers ne sont plus là**

```bash
cd ~/cleanup-exercise

# Créer et supprimer un fichier
touch test.txt
rm test.txt

# Vérifier qu'il n'existe plus
ls test.txt 2>/dev/null && echo "[X] Fichier existe encore" || echo "[OK] Fichier supprimé"
```

---

**2. Vérifier que la corbeille fonctionne**

```bash
# Créer un fichier
echo "Important" > important.txt

# Utiliser trash au lieu de rm
trash important.txt

# Vérifier qu'il est dans la corbeille
ls ~/.trash/ | grep important
```

---

**3. Vérifier que les dossiers vides sont supprimés**

```bash
# Créer des dossiers vides
mkdir -p test/{a,b,c}/empty

# Supprimer les dossiers vides
find test -type d -empty -delete

# Vérifier
find test -type d -empty | wc -l
# Devrait retourner 0
```

---

**4. Tester la protection contre `/`**

```bash
# Tenter de supprimer / (sera bloqué)
rm -rf /
# rm: it is dangerous to operate recursively on '/'
```

---

**5. Vérifier les logs de suppression**

```bash
# Le script cleanup-system.sh crée un log
ls ~/cleanup-*.log

# Lire le dernier log
tail -20 ~/cleanup-*.log
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Suppression accidentelle de fichiers importants

**Symptôme :**
```bash
rm -rf projet/
# Oups, j'ai supprimé le mauvais dossier !
```

**Solutions :**

**1. Utiliser la corbeille au lieu de `rm` :**
```bash
alias rm='trash'  # Rediriger rm vers la fonction trash
```

**2. Toujours utiliser `-i` :**
```bash
alias rm='rm -i'
```

**3. Faire un backup avant toute suppression importante :**
```bash
tar -czf backup-$(date +%Y%m%d).tar.gz projet/
rm -rf projet/
```

**4. Récupération (si pas de backup) :**
```bash
# Immédiatement arrêter d'écrire sur le disque !
# Utiliser photorec ou testdisk
sudo apt install testdisk
sudo photorec
```

---

#### Erreur 2 : "Device or resource busy"

**Symptôme :**
```bash
rm file.txt
rm: cannot remove 'file.txt': Device or resource busy
```

**Cause :** Un processus utilise le fichier.

**Solution :**

```bash
# Trouver quel processus utilise le fichier
lsof file.txt

# Tuer le processus
kill -9 <PID>

# Puis supprimer
rm file.txt
```

---

#### Erreur 3 : "Permission denied"

**Symptôme :**
```bash
rm /var/log/app.log
rm: cannot remove '/var/log/app.log': Permission denied
```

**Solution :**

```bash
# Option 1 : Utiliser sudo
sudo rm /var/log/app.log

# Option 2 : Changer les permissions
sudo chown $USER /var/log/app.log
rm /var/log/app.log
```

---

#### Erreur 4 : "Argument list too long"

**Symptôme :**
```bash
rm *.log
bash: /bin/rm: Argument list too long
```

**Cause :** Trop de fichiers (limite du shell : ~2MB d'arguments).

**Solution :**

```bash
# Méthode 1 : find avec -delete
find . -name "*.log" -delete

# Méthode 2 : xargs
find . -name "*.log" -print0 | xargs -0 rm

# Méthode 3 : Boucle
for f in *.log; do rm "$f"; done
```

---

#### Erreur 5 : Suppression de `/` accidentelle

**Symptôme :**
```bash
cd /important/data
rm -rf ./*
# Mais cd a échoué, donc tu es encore dans /
```

**Protection :**

```bash
# Toujours vérifier où tu es avant rm -rf
pwd

# Utiliser des chemins absolus
rm -rf /important/data/*

# Activer --preserve-root
rm --preserve-root -rf /
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. `rm` est DANGEREUX**
- Suppression définitive (pas de corbeille)
- Aucun moyen de récupération simple
- Ne jamais utiliser `rm -rf` sans réfléchir

**2. Options de sécurité**
- `-i` : Demander confirmation pour chaque fichier
- `-I` : Demander une fois si >3 fichiers
- `-v` : Afficher ce qui est supprimé
- Jamais de `-f` sauf si absolument certain

**3. Alternative sécurisée : Corbeille**
- Créer une fonction `trash` qui déplace au lieu de supprimer
- Récupération possible pendant X jours
- Nettoyage automatique avec cron

**4. `rmdir` pour dossiers vides**
- Sécurisé (refuse si non vide)
- Parfait pour nettoyer l'arborescence

**5. `find` pour suppressions en masse**
- Plus puissant que `rm` seul
- `-delete`, `-exec`, ou `xargs`
- Toujours TESTER avec `-print` avant `-delete`

**6. Bonnes pratiques**
- Toujours faire un backup avant suppression importante
- Utiliser `pwd` pour vérifier où tu es
- Tester avec `--dry-run` ou `echo` dans les scripts
- Logger toutes les suppressions
- Demander confirmation pour actions critiques

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Outil de suppression sécurisée : `shred`**

```bash
# Écraser les données avant suppression
shred -vfz -n 10 fichier-sensible.txt
rm fichier-sensible.txt
```

**Options :**
- `-v` : Verbose
- `-f` : Forcer les permissions
- `-z` : Écraser avec des zéros à la fin
- `-n 10` : 10 passes d'écrasement

---

**2. Outil moderne : `trash-cli`**

```bash
# Installer
sudo apt install trash-cli

# Utiliser
trash fichier.txt        # Mettre à la corbeille
trash-list              # Lister
trash-restore           # Restaurer
trash-empty             # Vider
```

---

**3. Alternative à `rm` : `safe-rm`**

```bash
# Installer
sudo apt install safe-rm

# Configurer les chemins protégés
echo "/home" >> /etc/safe-rm.conf
echo "/etc" >> /etc/safe-rm.conf

# Remplacer rm
alias rm='safe-rm'
```

---

**4. Récupération avec `extundelete` (ext3/ext4)**

```bash
# Installer
sudo apt install extundelete

# Récupérer
sudo extundelete /dev/sda1 --restore-all
```

---

**5. Script de cleanup avancé avec rapport détaillé**

```bash
#!/bin/bash
# advanced-cleanup.sh

generate_report() {
    cat > cleanup-report.html << EOF
<!DOCTYPE html>
<html>
<head>
    <title>Rapport de nettoyage</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        .stat { background: #f0f0f0; padding: 10px; margin: 10px 0; }
        .success { color: green; }
        .warning { color: orange; }
    </style>
</head>
<body>
    <h1>Rapport de nettoyage du $(date)</h1>
    <div class="stat">
        <strong>Fichiers temporaires :</strong> <span class="success">$TEMP_COUNT supprimés</span>
    </div>
    <div class="stat">
        <strong>Espace libéré :</strong> <span class="success">$FREED_SPACE</span>
    </div>
    <div class="stat">
        <strong>Durée :</strong> $DURATION secondes
    </div>
</body>
</html>
EOF
}
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1.3

**[OK] Félicitations ! Tu maîtrises maintenant la suppression sécurisée !**

**Ce que tu as appris :**
- Les dangers de `rm` et `rm -rf`
- Suppression sécurisée avec `-i` et `-I`
- Création d'une corbeille système
- Suppression en masse avec `find`
- `rmdir` pour dossiers vides
- Scripts de nettoyage automatique
- Bonnes pratiques de sécurité

**Compétences acquises :**
- [OK] Suppression sécurisée (niveau intermédiaire-avancé)
- [OK] Gestion de corbeille
- [OK] Nettoyage système
- [OK] Scripts de maintenance

**Temps moyen de réalisation :** 1 heure

**Prochaine étape :** Exercice 1.4 - Liens symboliques et liens durs ! [LIEN]

---

Je continue avec les exercices 1.4 et 1.5 ?

# [ROUGE] EXERCICE 1.4 : LIENS SYMBOLIQUES ET LIENS DURS

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système dans une entreprise qui gère plusieurs versions d'applications. Les développeurs ont besoin de :
1. Basculer rapidement entre différentes versions d'une application (v1.0, v2.0, v3.0)
2. Partager des fichiers de configuration entre plusieurs projets
3. Créer des raccourcis vers des fichiers/dossiers profonds dans l'arborescence
4. Gérer l'espace disque efficacement (éviter les duplications)

Ta mission : Mettre en place un système de liens qui optimise l'organisation et la flexibilité.

### Cahier des charges

- Créer des liens symboliques vers différentes versions d'applications
- Utiliser des liens durs pour partager des fichiers sans duplication
- Créer une structure de "production" pointant vers la bonne version
- Gérer les dépendances entre projets via liens
- Documenter tous les liens créés
- Éviter les liens brisés (broken links)

### Contraintes

- Commandes : `ln`, `readlink`, `find`
- Comprendre la différence entre liens symboliques et liens durs
- Gérer les cas d'erreur (liens cassés)
- Temps estimé : 1h - 1h30

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre la différence entre liens symboliques et liens durs
- [OK] Créer des liens symboliques (`ln -s`)
- [OK] Créer des liens durs
- [OK] Identifier et résoudre les liens brisés
- [OK] Utiliser `readlink` pour suivre les liens
- [OK] Comprendre les inodes
- [OK] Organiser des versions d'applications avec liens
- [OK] Gérer les problèmes de liens circulaires

---

## [DOCS] PRÉREQUIS

- Exercices 1.1, 1.2 et 1.3 terminés
- Compréhension du système de fichiers Linux
- Notions de base sur les chemins

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre la théorie - Inodes et liens

**Qu'est-ce qu'un inode ?**

Un **inode** (index node) est une structure de données qui stocke les métadonnées d'un fichier :
- Permissions (rwx)
- Propriétaire et groupe
- Taille du fichier
- Dates (création, modification, accès)
- Nombre de liens vers ce fichier
- Pointeurs vers les blocs de données sur le disque

**[ATTENTION] L'inode NE contient PAS le nom du fichier !**

Le nom du fichier est stocké dans le répertoire parent.

---

**Schéma conceptuel :**

```
┌─────────────────┐
│  Répertoire     │
├─────────────────┤
│ fichier.txt -> 12345  │  (Le nom pointe vers l'inode 12345)
└─────────────────┘
         v
┌─────────────────┐
│  Inode 12345    │
├─────────────────┤
│ Permissions: 644│
│ Propriétaire: user │
│ Taille: 1024    │
│ Liens: 1        │
│ Blocs: ->->->      │ (Pointeurs vers données)
└─────────────────┘
         v
┌─────────────────┐
│  Données        │
│  du fichier     │
└─────────────────┘
```

---

**Voir l'inode d'un fichier :**

```bash
# Créer un fichier de test
echo "Test content" > test.txt

# Voir l'inode
ls -i test.txt
# 1234567 test.txt
#   ^
# Numéro d'inode

# Voir toutes les informations de l'inode
stat test.txt
```

**Résultat de `stat` :**

```
  File: test.txt
  Size: 13        	Blocks: 8          IO Block: 4096   regular file
Device: 803h/2051d	Inode: 1234567     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    user)   Gid: ( 1000/    user)
Access: 2024-12-30 14:00:00.000000000 +0000
Modify: 2024-12-30 14:00:00.000000000 +0000
Change: 2024-12-30 14:00:00.000000000 +0000
 Birth: 2024-12-30 14:00:00.000000000 +0000
```

**Informations clés :**
- **Inode** : Numéro unique
- **Links** : Nombre de liens (noms) pointant vers cet inode
- **Size** : Taille du fichier
- **Blocks** : Nombre de blocs disque utilisés

---

### ÉTAPE 2 : Comprendre les LIENS DURS (Hard Links)

**Qu'est-ce qu'un lien dur ?**

Un **lien dur** est un nom **supplémentaire** pour le même inode.

**Caractéristiques :**
- Pointe directement vers l'inode (pas vers un autre nom)
- Indistinguable du fichier original
- Suppression d'un lien ne supprime pas le fichier tant qu'il reste au moins 1 lien
- **Ne peut pas pointer vers un répertoire** (sauf . et ..)
- **Ne peut pas traverser les systèmes de fichiers** (doit être sur la même partition)

---

**Schéma conceptuel des liens durs :**

```
Avant :
┌─────────────────┐
│  Répertoire     │
├─────────────────┤
│ original.txt -> 12345 │
└─────────────────┘
         v
┌─────────────────┐
│  Inode 12345    │
│  Links: 1       │
└─────────────────┘

Après création d'un lien dur :
┌─────────────────┐
│  Répertoire     │
├─────────────────┤
│ original.txt -> 12345 │
│ lien.txt     -> 12345 │  (Même inode !)
└─────────────────┘
         v
┌─────────────────┐
│  Inode 12345    │
│  Links: 2       │  (Compteur incrémenté)
└─────────────────┘
```

**Les deux noms pointent vers le MÊME inode !**

---

**Créer un lien dur :**

```bash
# Créer un fichier original
echo "Contenu original" > original.txt

# Voir l'inode et le nombre de liens
ls -li original.txt
# 1234567 -rw-r--r-- 1 user user 17 Dec 30 14:00 original.txt
#                    ^
#                  Links: 1

# Créer un lien dur
ln original.txt lien-dur.txt

# Vérifier
ls -li original.txt lien-dur.txt
# 1234567 -rw-r--r-- 2 user user 17 Dec 30 14:00 original.txt
# 1234567 -rw-r--r-- 2 user user 17 Dec 30 14:00 lien-dur.txt
#   ^                ^
# Même inode     Links: 2 (maintenant)
```

**Observations :**
- Les deux fichiers ont le **même inode** (1234567)
- Le compteur de **Links** est passé à 2
- Les deux fichiers ont la **même taille** et **même date**

---

**Test : Modifier via le lien**

```bash
# Modifier via le lien dur
echo "Nouveau contenu" >> lien-dur.txt

# Vérifier l'original
cat original.txt
# Contenu original
# Nouveau contenu

# Les deux fichiers partagent le même contenu !
```

---

**Test : Supprimer l'original**

```bash
# Supprimer "l'original"
rm original.txt

# Le lien dur existe toujours !
cat lien-dur.txt
# Contenu original
# Nouveau contenu

# Vérifier le nombre de liens
ls -li lien-dur.txt
# 1234567 -rw-r--r-- 1 user user 34 Dec 30 14:05 lien-dur.txt
#                    ^
#                  Links: 1 (décrémenté)
```

**Le fichier n'est supprimé que quand Links = 0 !**

---

**Syntaxe de `ln` (sans options = lien dur) :**

```bash
ln CIBLE NOM_DU_LIEN

# Exemples
ln fichier.txt lien.txt
ln /path/to/source.txt /path/to/link.txt
```

---

**Limitations des liens durs :**

**1. Ne peut pas pointer vers un répertoire**

```bash
mkdir dossier
ln dossier lien-dossier
# ln: dossier: hard link not allowed for directory
```

**Exception :** Les répertoires `.` et `..` sont des liens durs spéciaux.

---

**2. Ne peut pas traverser les systèmes de fichiers**

```bash
# Si /home est sur une partition et /mnt sur une autre
ln /home/user/file.txt /mnt/external/link.txt
# ln: failed to create hard link: Invalid cross-device link
```

**Pourquoi ?**
- Les numéros d'inode sont uniques par système de fichiers
- Un inode sur /dev/sda1 peut avoir le même numéro qu'un inode sur /dev/sda2
- Créer un lien dur entre eux serait ambigu

---

### ÉTAPE 3 : Comprendre les LIENS SYMBOLIQUES (Symbolic Links / Symlinks)

**Qu'est-ce qu'un lien symbolique ?**

Un **lien symbolique** est un fichier spécial qui contient le **chemin** vers un autre fichier/dossier.

**Caractéristiques :**
- C'est un **pointeur** (comme un raccourci Windows)
- A son **propre inode** (différent de la cible)
- Peut pointer vers un **répertoire**
- Peut traverser les **systèmes de fichiers**
- Peut pointer vers un fichier **inexistant** (lien brisé)
- Taille = longueur du chemin stocké

---

**Schéma conceptuel des liens symboliques :**

```
┌─────────────────┐
│  Répertoire     │
├─────────────────┤
│ original.txt -> 12345 │
│ lien.txt     -> 67890 │  (Inode différent)
└─────────────────┘
         v               v
┌─────────────────┐   ┌─────────────────┐
│  Inode 12345    │   │  Inode 67890    │
│  (Fichier)      │   │  (Symlink)      │
│  Données: ...   │   │  Contenu:       │
└─────────────────┘   │  "original.txt" │
                      └─────────────────┘
```

Le lien symbolique pointe vers le **nom**, pas vers l'inode directement.

---

**Créer un lien symbolique :**

```bash
# Créer un fichier original
echo "Contenu original" > original.txt

# Créer un lien symbolique
ln -s original.txt lien-symbolique.txt

# Vérifier
ls -li original.txt lien-symbolique.txt
# 1234567 -rw-r--r-- 1 user user 17 Dec 30 14:00 original.txt
# 7654321 lrwxrwxrwx 1 user user 12 Dec 30 14:05 lien-symbolique.txt -> original.txt
#   ^       ^                        ^
# Inode   Type: l                  Taille = longueur du chemin
# différent (symlink)
```

**Observations :**
- **Inode différent** (7654321 vs 1234567)
- **Type** : `l` (link) au lieu de `-` (fichier)
- **Permissions** : `rwxrwxrwx` (toujours, mais non significatif)
- **Taille** : 12 octets (longueur de "original.txt")
- **Flèche** : `-> original.txt` indique la cible

---

**Syntaxe de `ln -s` :**

```bash
ln -s CIBLE NOM_DU_LIEN

# Exemples
ln -s fichier.txt lien.txt
ln -s /chemin/absolu/vers/fichier.txt lien.txt
ln -s ../relatif/fichier.txt lien.txt
ln -s dossier/ lien-dossier  # Fonctionne avec les dossiers !
```

**[ATTENTION] Important : `-s` pour Symbolic**

```bash
# Sans -s = Lien dur (hard link)
ln fichier.txt lien-dur.txt

# Avec -s = Lien symbolique (symlink)
ln -s fichier.txt lien-sym.txt
```

---

**Test : Modifier via le lien symbolique**

```bash
# Modifier via le lien symbolique
echo "Ajout via symlink" >> lien-symbolique.txt

# Vérifier l'original
cat original.txt
# Contenu original
# Ajout via symlink

# Ça fonctionne comme avec un lien dur !
```

---

**Test : Supprimer l'original**

```bash
# Supprimer l'original
rm original.txt

# Le lien symbolique existe toujours
ls -l lien-symbolique.txt
# lrwxrwxrwx 1 user user 12 Dec 30 14:05 lien-symbolique.txt -> original.txt

# Mais il est "brisé" (broken link)
cat lien-symbolique.txt
# cat: lien-symbolique.txt: No such file or directory
```

**Le lien symbolique devient un "lien brisé" si la cible est supprimée !**

---

**Différence visuelle avec `ls -l` :**

```bash
# Lien brisé (en rouge sur la plupart des terminaux)
ls -l lien-symbolique.txt
# lrwxrwxrwx 1 user user 12 Dec 30 14:05 lien-symbolique.txt -> original.txt
#                                                                  (en rouge)

# Vérifier si le lien est brisé
test -e lien-symbolique.txt && echo "OK" || echo "Brisé"
# Brisé
```

---

### ÉTAPE 4 : Comparaison Liens Durs vs Liens Symboliques

**Tableau comparatif :**

| Caractéristique | Lien Dur | Lien Symbolique |
|-----------------|----------|-----------------|
| **Inode** | Même que l'original | Propre inode |
| **Apparence** | Fichier normal | Type `l`, flèche -> |
| **Pointage** | Vers l'inode | Vers le nom |
| **Suppression original** | Fichier reste accessible | Lien brisé |
| **Répertoires** | [X] Non | [OK] Oui |
| **Cross-filesystem** | [X] Non | [OK] Oui |
| **Taille** | Même que l'original | Longueur du chemin |
| **Performance** | [RAPIDE] Rapide (direct) | [LENT] Légèrement plus lent |
| **Liens brisés** | [X] Impossible | [ATTENTION] Possible |
| **Visibilité** | Indistinguable | Clairement identifiable |

---

**Cas d'usage :**

**Utiliser des LIENS DURS quand :**
- [OK] Tu veux plusieurs noms pour le même fichier
- [OK] Tu veux protéger contre la suppression accidentelle
- [OK] Tu veux économiser l'espace disque (pas de duplication)
- [OK] Tout est sur le même système de fichiers

**Utiliser des LIENS SYMBOLIQUES quand :**
- [OK] Tu veux pointer vers un répertoire
- [OK] Tu veux traverser les systèmes de fichiers
- [OK] Tu veux créer des "raccourcis" clairs
- [OK] Tu veux pointer vers un fichier qui n'existe pas encore

---

### ÉTAPE 5 : Scénario réel - Gestion de versions d'applications

**Contexte :**

Une entreprise déploie une application web en plusieurs versions :
- `app-v1.0/`
- `app-v2.0/`
- `app-v3.0/`

Nginx doit toujours pointer vers `/var/www/app-production`, qui pointe vers la version active.

---

**Créer la structure :**

```bash
# Créer le dossier de travail
mkdir -p ~/links-exercise
cd ~/links-exercise

# Créer les différentes versions
mkdir -p app-v1.0/public
mkdir -p app-v2.0/public
mkdir -p app-v3.0/public

# Créer des fichiers différents par version
echo "<h1>Version 1.0</h1>" > app-v1.0/public/index.html
echo "<h1>Version 2.0 - Nouvelle interface</h1>" > app-v2.0/public/index.html
echo "<h1>Version 3.0 - Améliorations</h1>" > app-v3.0/public/index.html

# Ajouter des fichiers de config
cat > app-v1.0/config.ini << EOF
[app]
version=1.0
database=mysql://localhost/app_v1
EOF

cat > app-v2.0/config.ini << EOF
[app]
version=2.0
database=mysql://localhost/app_v2
feature_x=enabled
EOF

cat > app-v3.0/config.ini << EOF
[app]
version=3.0
database=mysql://localhost/app_v3
feature_x=enabled
feature_y=enabled
EOF

# Afficher la structure
tree
```

**Résultat :**

```
.
├── app-v1.0
│   ├── config.ini
│   └── public
│       └── index.html
├── app-v2.0
│   ├── config.ini
│   └── public
│       └── index.html
└── app-v3.0
    ├── config.ini
    └── public
        └── index.html
```

---

**Créer le lien symbolique vers la version active :**

```bash
# Démarrer avec la v1.0 en production
ln -s app-v1.0 app-production

# Vérifier
ls -l
# lrwxrwxrwx 1 user user   9 Dec 30 15:00 app-production -> app-v1.0

# Tester
cat app-production/public/index.html
# <h1>Version 1.0</h1>
```

---

**Basculer vers la v2.0 (upgrade) :**

```bash
# Supprimer l'ancien lien
rm app-production

# Créer un nouveau lien vers v2.0
ln -s app-v2.0 app-production

# Vérifier
cat app-production/public/index.html
# <h1>Version 2.0 - Nouvelle interface</h1>

cat app-production/config.ini
# [app]
# version=2.0
# ...
```

**Changement instantané ! Nginx voit immédiatement la nouvelle version.**

---

**Rollback rapide (retour à v1.0) :**

```bash
# En cas de problème avec v2.0
rm app-production
ln -s app-v1.0 app-production

# Retour instantané à la v1.0 !
cat app-production/public/index.html
# <h1>Version 1.0</h1>
```

**Rollback en quelques secondes au lieu de re-déployer !**

---

**Utiliser `ln -sf` pour remplacer directement :**

```bash
# -f = Force (remplace le lien existant)
ln -sf app-v3.0 app-production

# Pas besoin de rm avant !
cat app-production/public/index.html
# <h1>Version 3.0 - Améliorations</h1>
```

**Explication de `-f` (force) :**

`-f` = Supprime le lien existant avant de créer le nouveau

**Sans `-f` :**
```bash
ln -s app-v3.0 app-production
# ln: failed to create symbolic link 'app-production': File exists
```

**Avec `-f` :**
```bash
ln -sf app-v3.0 app-production
# Succès ! Ancien lien remplacé
```

---

### ÉTAPE 6 : Liens relatifs vs absolus

**Lien absolu :**

```bash
# Créer un lien avec chemin absolu
ln -s /home/user/links-exercise/app-v1.0 app-prod-abs

# Voir le lien
ls -l app-prod-abs
# lrwxrwxrwx 1 user user 35 Dec 30 15:30 app-prod-abs -> /home/user/links-exercise/app-v1.0
```

**Avantage :**
- Fonctionne de n'importe où

**Inconvénient :**
- Ne fonctionne plus si le dossier parent est déplacé

---

**Lien relatif :**

```bash
# Créer un lien avec chemin relatif
ln -s app-v1.0 app-prod-rel

# Voir le lien
ls -l app-prod-rel
# lrwxrwxrwx 1 user user 9 Dec 30 15:30 app-prod-rel -> app-v1.0
```

**Avantage :**
- Portable (si tout le dossier est déplacé, les liens fonctionnent encore)

**Inconvénient :**
- Dépend du répertoire courant

---

**Test de portabilité :**

```bash
# Créer un nouveau dossier
mkdir /tmp/test-links

# Copier tout avec liens absolus
cp -r ~/links-exercise /tmp/test-links-abs
cd /tmp/test-links-abs/links-exercise

# Tester le lien absolu
cat app-prod-abs/public/index.html
# [X] Erreur : pointe toujours vers /home/user/...

# Tester le lien relatif
cat app-prod-rel/public/index.html
# [OK] Fonctionne !
```

**Recommandation : Utiliser des liens relatifs quand possible.**

---

### ÉTAPE 7 : Commande `readlink` - Suivre les liens

**`readlink` affiche la cible d'un lien symbolique.**

**Syntaxe :**

```bash
readlink LIEN

# Options
readlink -f LIEN   # Canonique (chemin absolu final)
readlink -e LIEN   # Comme -f mais vérifie l'existence
```

---

**Exemples :**

```bash
cd ~/links-exercise

# Créer une chaîne de liens
ln -s app-v1.0 link1
ln -s link1 link2
ln -s link2 link3

# Voir la cible directe
readlink link3
# link2

# Voir la cible finale (-f = canonical)
readlink -f link3
# /home/user/links-exercise/app-v1.0
```

**Explication de `-f` :**

`-f` = Follow (suivre) tous les liens jusqu'à la cible finale

**Chaîne de liens :**
```
link3 -> link2 -> link1 -> app-v1.0 -> (dossier réel)
```

`readlink -f link3` retourne directement `/home/.../app-v1.0`

---

**Vérifier si un lien pointe vers une cible spécifique :**

```bash
# Créer un lien
ln -s app-v2.0 current

# Vérifier vers quoi il pointe
if [ "$(readlink current)" = "app-v2.0" ]; then
    echo "[OK] Pointe vers v2.0"
else
    echo "[X] Pointe ailleurs"
fi
```

---

**Script pour afficher tous les liens d'un dossier :**

```bash
#!/bin/bash
# show-links.sh

echo "[LISTE] Liens symboliques dans $PWD :"
echo ""

find . -type l | while read link; do
    target=$(readlink "$link")
    real=$(readlink -f "$link" 2>/dev/null)
    
    if [ -e "$link" ]; then
        status="[OK]"
    else
        status="[X] BRISÉ"
    fi
    
    echo "$status $link -> $target"
    if [ -n "$real" ]; then
        echo "      Cible finale : $real"
    fi
    echo ""
done
```

**Utilisation :**

```bash
chmod +x show-links.sh
./show-links.sh
```

**Résultat :**

```
[LISTE] Liens symboliques dans /home/user/links-exercise :

[OK] ./app-production -> app-v3.0
      Cible finale : /home/user/links-exercise/app-v3.0

[OK] ./link1 -> app-v1.0
      Cible finale : /home/user/links-exercise/app-v1.0

[X] BRISÉ ./broken-link -> fichier-inexistant
```

---

### ÉTAPE 8 : Trouver et réparer les liens brisés

**Trouver tous les liens brisés :**

```bash
# Méthode 1 : find avec -L
find . -xtype l

# Méthode 2 : find avec test
find . -type l ! -exec test -e {} \; -print

# Méthode 3 : avec readlink
find . -type l -exec sh -c '! readlink -e "$1" >/dev/null' _ {} \; -print
```

**Explication de `find . -xtype l` :**

`-xtype l` = Suit les liens et teste le type

**Résultat :**
- Trouve les liens symboliques dont la cible n'existe pas
- Liens brisés = liens de type `l` qui ne peuvent pas être résolus

---

**Créer des liens brisés pour tester :**

```bash
cd ~/links-exercise

# Créer un fichier et un lien
echo "test" > temp.txt
ln -s temp.txt link-to-temp

# Supprimer la cible
rm temp.txt

# Le lien est maintenant brisé
ls -l link-to-temp
# lrwxrwxrwx 1 user user 8 Dec 30 16:00 link-to-temp -> temp.txt (en rouge)

# Trouver les liens brisés
find . -xtype l
# ./link-to-temp
```

---

**Script pour réparer automatiquement les liens brisés :**

```bash
#!/bin/bash
# fix-broken-links.sh

echo "[RECHERCHE] Recherche de liens brisés..."

find . -xtype l | while read broken_link; do
    target=$(readlink "$broken_link")
    echo ""
    echo "[X] Lien brisé trouvé : $broken_link"
    echo "   Cible actuelle : $target"
    
    # Demander quoi faire
    echo "Options :"
    echo "  1) Supprimer le lien"
    echo "  2) Changer la cible"
    echo "  3) Ignorer"
    read -p "Choix (1/2/3) : " choice
    
    case $choice in
        1)
            rm "$broken_link"
            echo "[OK] Lien supprimé"
            ;;
        2)
            read -p "Nouvelle cible : " new_target
            rm "$broken_link"
            ln -s "$new_target" "$broken_link"
            echo "[OK] Lien réparé : $broken_link -> $new_target"
            ;;
        3)
            echo "[BLACK_RIGHT-POINTING_DOUBLE_TRIANGLE_WITH_VERTICAL_BAR]  Ignoré"
            ;;
    esac
done

echo ""
echo "[OK] Terminé"
```

---

### ÉTAPE 9 : Liens vers des dossiers

**Créer un lien symbolique vers un dossier :**

```bash
cd ~/links-exercise

# Créer un dossier profond
mkdir -p data/projects/web/2024/site-ecommerce

# Créer un lien raccourci
ln -s data/projects/web/2024/site-ecommerce ecommerce

# Naviguer via le lien
cd ecommerce
pwd
# /home/user/links-exercise/ecommerce

# Voir le vrai chemin
pwd -P
# /home/user/links-exercise/data/projects/web/2024/site-ecommerce
```

**Explication de `pwd -P` :**

`-P` = Physical (chemin physique, sans suivre les liens)

**Sans `-P` :**
```bash
pwd
# /home/user/links-exercise/ecommerce (chemin logique)
```

**Avec `-P` :**
```bash
pwd -P
# /home/user/links-exercise/data/projects/web/2024/site-ecommerce (chemin réel)
```

---

**[ATTENTION] Attention avec `cd ..` dans un lien symbolique :**

```bash
cd ~/links-exercise

# Créer un lien vers un dossier profond
ln -s data/projects/web shortcut

# Entrer dans le lien
cd shortcut
pwd
# /home/user/links-exercise/shortcut

# Remonter d'un niveau
cd ..
pwd
# /home/user/links-exercise

# [ATTENTION] On remonte dans le dossier LOGIQUE (là où est le lien)
# Pas dans le dossier physique (data/projects)
```

**Pour remonter physiquement :**

```bash
cd ~/links-exercise/shortcut
cd -P ..
pwd
# /home/user/links-exercise/data/projects
```

---

### ÉTAPE 10 : Cas d'usage avancés

**Cas 1 : Partager une configuration entre plusieurs projets**

```bash
# Structure
mkdir -p project1 project2 project3
mkdir shared-config

# Créer un fichier de config partagé
cat > shared-config/database.ini << EOF
host=localhost
port=3306
user=admin
EOF

# Créer des liens dans chaque projet
ln -s ../shared-config/database.ini project1/database.ini
ln -s ../shared-config/database.ini project2/database.ini
ln -s ../shared-config/database.ini project3/database.ini

# Modifier la config partagée
echo "password=secret123" >> shared-config/database.ini

# Tous les projets voient la modification
cat project1/database.ini
cat project2/database.ini
cat project3/database.ini
# Tous identiques !
```

---

**Cas 2 : Créer des alias de commandes**

```bash
# Créer un dossier bin personnel
mkdir -p ~/bin

# Créer des liens vers des commandes
ln -s /usr/bin/python3 ~/bin/python
ln -s /usr/bin/pip3 ~/bin/pip

# Ajouter ~/bin au PATH
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Maintenant "python" pointe vers python3
python --version
# Python 3.10.0
```

---

**Cas 3 : Gérer des fichiers de logs rotatifs**

```bash
# Structure de logs
mkdir logs
touch logs/app.log.1
touch logs/app.log.2
touch logs/app.log.3

# Créer un lien vers le log courant
ln -s logs/app.log.1 current.log

# Rotation manuelle
mv current.log old.log
ln -s logs/app.log.2 current.log

# L'application écrit toujours dans "current.log"
# Mais physiquement, ça change de fichier
```

---

### [OK] TESTS DE VALIDATION

**1. Vérifier qu'un lien dur partage l'inode**

```bash
# Créer un fichier et un lien dur
echo "test" > file.txt
ln file.txt hardlink.txt

# Vérifier les inodes
INODE1=$(ls -i file.txt | cut -d' ' -f1)
INODE2=$(ls -i hardlink.txt | cut -d' ' -f1)

if [ "$INODE1" = "$INODE2" ]; then
    echo "[OK] Même inode : $INODE1"
else
    echo "[X] Inodes différents"
fi
```

---

**2. Vérifier qu'un lien symbolique a un inode différent**

```bash
# Créer un fichier et un lien symbolique
echo "test" > file.txt
ln -s file.txt symlink.txt

# Vérifier les inodes
INODE1=$(ls -i file.txt | cut -d' ' -f1)
INODE2=$(ls -i symlink.txt | cut -d' ' -f1)

if [ "$INODE1" != "$INODE2" ]; then
    echo "[OK] Inodes différents : $INODE1 vs $INODE2"
else
    echo "[X] Même inode (erreur)"
fi
```

---

**3. Tester la suppression avec lien dur**

```bash
# Créer un fichier et un lien dur
echo "important" > original.txt
ln original.txt backup.txt

# Supprimer l'original
rm original.txt

# Vérifier que backup existe toujours
if [ -f backup.txt ]; then
    echo "[OK] Fichier préservé via lien dur"
    cat backup.txt
else
    echo "[X] Fichier perdu"
fi
```

---

**4. Tester la détection de liens brisés**

```bash
# Créer un lien et briser la cible
echo "test" > target.txt
ln -s target.txt link.txt
rm target.txt

# Vérifier si le lien est brisé
if [ ! -e link.txt ]; then
    echo "[OK] Lien brisé détecté correctement"
else
    echo "[X] Lien devrait être brisé"
fi
```

---

**5. Vérifier readlink**

```bash
# Créer une chaîne de liens
echo "data" > original.txt
ln -s original.txt link1
ln -s link1 link2
ln -s link2 link3

# Vérifier la cible finale
FINAL=$(readlink -f link3)
EXPECTED=$(readlink -f original.txt)

if [ "$FINAL" = "$EXPECTED" ]; then
    echo "[OK] readlink -f fonctionne : $FINAL"
else
    echo "[X] Erreur readlink"
fi
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Too many levels of symbolic links"

**Symptôme :**

```bash
ln -s link1 link2
ln -s link2 link1

cat link1
# cat: link1: Too many levels of symbolic links
```

**Cause :** Liens circulaires (A -> B -> A)

**Solution :**

```bash
# Supprimer un des liens
rm link1

# Ou recréer correctement
rm link1 link2
ln -s original.txt link1
```

---

#### Erreur 2 : Lien relatif incorrect

**Symptôme :**

```bash
# Dans /home/user/project/
ln -s config.ini data/config.ini

cd data/
cat config.ini
# cat: config.ini: No such file or directory
```

**Cause :** Le lien relatif est évalué depuis l'emplacement du lien, pas depuis le répertoire courant lors de la création.

**Solution :**

```bash
# Créer le lien avec le bon chemin relatif
cd data/
ln -s ../config.ini config.ini

# Ou utiliser un chemin absolu
ln -s /home/user/project/config.ini /home/user/project/data/config.ini
```

---

#### Erreur 3 : "Invalid cross-device link" (liens durs)

**Symptôme :**

```bash
ln /home/user/file.txt /mnt/usb/link.txt
# ln: failed to create hard link: Invalid cross-device link
```

**Cause :** Tentative de créer un lien dur entre deux systèmes de fichiers.

**Solution :**

```bash
# Utiliser un lien symbolique à la place
ln -s /home/user/file.txt /mnt/usb/link.txt
```

---

#### Erreur 4 : Écrasement accidentel

**Symptôme :**

```bash
# Un fichier important existe
echo "important" > data.txt

# Créer un lien avec le même nom
ln -s other.txt data.txt
# ln: failed to create symbolic link 'data.txt': File exists
```

**Solution :**

```bash
# Utiliser -f pour forcer (ATTENTION : supprime l'existant)
ln -sf other.txt data.txt

# Ou choisir un autre nom
ln -s other.txt data-link.txt
```

---

#### Erreur 5 : Permissions insuffisantes

**Symptôme :**

```bash
ln -s /etc/nginx/nginx.conf /var/www/nginx.conf
# ln: failed to create symbolic link: Permission denied
```

**Solution :**

```bash
# Utiliser sudo
sudo ln -s /etc/nginx/nginx.conf /var/www/nginx.conf

# Ou créer dans ton home
ln -s /etc/nginx/nginx.conf ~/nginx.conf
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Inodes**
- Structure de données contenant les métadonnées
- Numéro unique par système de fichiers
- Le nom du fichier n'est PAS dans l'inode

**2. Liens durs**
- Nom supplémentaire pour le même inode
- Partage les données (pas de duplication)
- Ne peut pas pointer vers répertoire
- Ne traverse pas les systèmes de fichiers
- Fichier supprimé quand Links = 0

**3. Liens symboliques**
- Fichier spécial contenant un chemin
- Propre inode (différent de la cible)
- Peut pointer vers répertoire
- Traverse les systèmes de fichiers
- Peut être brisé si cible supprimée

**4. Commandes**
- `ln CIBLE LIEN` : Créer lien dur
- `ln -s CIBLE LIEN` : Créer lien symbolique
- `ln -sf` : Forcer remplacement
- `readlink` : Voir la cible
- `readlink -f` : Voir la cible finale

**5. Bonnes pratiques**
- Préférer liens relatifs (portabilité)
- Utiliser `-f` avec précaution
- Documenter les liens importants
- Vérifier régulièrement les liens brisés
- Utiliser liens symboliques pour versions

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Script de migration avec liens**

```bash
#!/bin/bash
# deploy-new-version.sh

VERSION=$1
LINK_NAME="production"

if [ -z "$VERSION" ]; then
    echo "Usage: $0 <version>"
    exit 1
fi

if [ ! -d "$VERSION" ]; then
    echo "[X] Version $VERSION n'existe pas"
    exit 1
fi

# Backup de l'ancien lien
if [ -L "$LINK_NAME" ]; then
    OLD_VERSION=$(readlink "$LINK_NAME")
    echo "[PACKAGE] Backup : $OLD_VERSION"
fi

# Créer le nouveau lien
ln -sfn "$VERSION" "$LINK_NAME"

# Vérifier
NEW_VERSION=$(readlink "$LINK_NAME")
echo "[OK] Déploiement terminé : $LINK_NAME -> $NEW_VERSION"

# Test basique
if [ -f "$LINK_NAME/index.html" ]; then
    echo "[OK] Fichiers accessibles"
else
    echo "[X] Erreur : fichiers non trouvés"
    
    # Rollback
    if [ -n "$OLD_VERSION" ]; then
        echo "[SYNC] Rollback vers $OLD_VERSION"
        ln -sfn "$OLD_VERSION" "$LINK_NAME"
    fi
fi
```

---

**2. Trouver tous les liens pointant vers un fichier**

```bash
#!/bin/bash
# find-links-to.sh

TARGET=$1

if [ -z "$TARGET" ]; then
    echo "Usage: $0 <target-file>"
    exit 1
fi

# Obtenir l'inode de la cible
INODE=$(stat -c %i "$TARGET")

echo "[RECHERCHE] Recherche de tous les liens vers $TARGET (inode: $INODE)"
echo ""

# Trouver tous les fichiers avec le même inode (liens durs)
echo "Liens durs :"
find . -inum "$INODE"

echo ""

# Trouver tous les liens symboliques pointant vers ce fichier
echo "Liens symboliques :"
find . -type l -exec sh -c '
    target=$(readlink -f "$1" 2>/dev/null)
    if [ "$target" = "$2" ]; then
        echo "$1 -> $(readlink "$1")"
    fi
' _ {} "$(readlink -f "$TARGET")" \;
```

---

**3. Créer un système de cache avec liens**

```bash
# Cache distribué avec liens symboliques
mkdir -p cache/{original,by-hash}

# Fonction pour ajouter au cache
cache_add() {
    local file=$1
    local hash=$(md5sum "$file" | cut -d' ' -f1)
    
    # Copier dans le cache original
    cp "$file" "cache/original/$hash"
    
    # Créer un lien nommé
    ln -s "../original/$hash" "cache/by-hash/$(basename "$file")"
    
    echo "[OK] Ajouté au cache : $file ($hash)"
}

# Utilisation
cache_add myfile.txt
```

---

**4. Automatiser le nettoyage des liens brisés**

```bash
#!/bin/bash
# clean-broken-links.sh

LOG_FILE="broken-links-$(date +%Y%m%d).log"

echo "[SUPPRIMER]  Nettoyage des liens brisés..." | tee "$LOG_FILE"

find . -xtype l | while read broken; do
    target=$(readlink "$broken")
    echo "Suppression : $broken -> $target" | tee -a "$LOG_FILE"
    rm "$broken"
done

echo "[OK] Terminé" | tee -a "$LOG_FILE"
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1.4

**[OK] Félicitations ! Tu maîtrises maintenant les liens sous Linux !**

**Ce que tu as appris :**
- Concept d'inode
- Différence entre liens durs et symboliques
- Créer des liens (`ln`, `ln -s`)
- Gérer des versions avec liens
- Détecter et réparer les liens brisés
- Utiliser `readlink`

**Compétences acquises :**
- [OK] Liens Linux (niveau avancé)
- [OK] Gestion de versions
- [OK] Organisation professionnelle
- [OK] Maintenance de liens

**Temps moyen de réalisation :** 1h - 1h30

**Prochaine étape :** Exercice 1.5 - Navigation avancée et organisation professionnelle ! [OBJECTIF]

---

Je continue avec l'exercice 1.5 (le dernier de la Section 1) ?

# [ROUGE] EXERCICE 1.5 : ORGANISATION PROFESSIONNELLE ET NAVIGATION AVANCÉE (EXPERT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es Lead DevOps dans une entreprise de développement logiciel qui gère :
- 15 projets actifs en simultané
- 5 environnements (dev, test, staging, preprod, prod)
- 3 équipes de développeurs (Frontend, Backend, DevOps)
- Des backups quotidiens, hebdomadaires et mensuels
- Des logs de plusieurs applications
- Des artifacts de build et releases

**Problématique actuelle :**
- Chaos dans l'arborescence des fichiers
- Difficulté à retrouver les fichiers
- Backups désorganisés
- Pas de convention de nommage
- Équipes qui créent des dossiers n'importe où

**Ta mission :** Créer une structure professionnelle, maintenable et scalable.

### Cahier des charges

**Structure à créer :**
1. Organisation par environnement et projet
2. Séparation code source / builds / deployments
3. Système de backups structuré par date
4. Logs centralisés avec rotation
5. Documentation technique accessible
6. Scripts d'automatisation
7. Convention de nommage stricte
8. Système de liens pour accès rapides
9. Mécanismes de recherche efficaces
10. Monitoring de l'utilisation disque

**Livrables :**
- Arborescence complète (100+ dossiers)
- Scripts d'automatisation (création, maintenance)
- Documentation (README dans chaque dossier clé)
- Système de navigation rapide (alias, liens)
- Scripts de validation et monitoring

### Contraintes

- Respect des conventions FHS (Filesystem Hierarchy Standard)
- Scalabilité (ajout facile de nouveaux projets)
- Maintenabilité (structure claire et documentée)
- Performance (accès rapide aux fichiers fréquents)
- Sécurité (permissions appropriées)
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Concevoir une arborescence professionnelle scalable
- [OK] Implémenter des conventions de nommage
- [OK] Créer des systèmes de navigation rapide
- [OK] Automatiser la création et maintenance de structures
- [OK] Utiliser des variables d'environnement efficacement
- [OK] Mettre en place des mécanismes de recherche avancés
- [OK] Gérer les permissions de manière professionnelle
- [OK] Créer des scripts de validation
- [OK] Monitorer l'utilisation des ressources
- [OK] Documenter une architecture de fichiers

---

## [DOCS] PRÉREQUIS

- Tous les exercices 1.1 à 1.4 terminés
- Maîtrise de bash scripting (niveau intermédiaire)
- Compréhension des permissions Linux
- Notions d'organisation de projet

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Analyser et concevoir l'architecture

**Avant de créer quoi que ce soit, planifions !**

**Principes de conception :**

1. **Séparation des préoccupations**
   - Code source séparé des builds
   - Logs séparés des données
   - Backups dans une zone dédiée

2. **Hiérarchie claire**
   - Max 4-5 niveaux de profondeur
   - Noms explicites
   - Structure prévisible

3. **Scalabilité**
   - Facile d'ajouter de nouveaux projets
   - Structure réutilisable
   - Pas de hard-coding

4. **Conventions**
   - Nommage cohérent
   - Dates au format ISO (YYYY-MM-DD)
   - Lowercase avec tirets (kebab-case)

---

**Architecture proposée :**

```
/opt/company/
├── projects/               # Code source de tous les projets
│   ├── frontend/
│   │   ├── web-app/
│   │   ├── mobile-app/
│   │   └── admin-panel/
│   ├── backend/
│   │   ├── api-gateway/
│   │   ├── auth-service/
│   │   └── payment-service/
│   └── infrastructure/
│       ├── terraform/
│       ├── ansible/
│       └── kubernetes/
│
├── environments/           # Déploiements par environnement
│   ├── dev/
│   ├── test/
│   ├── staging/
│   ├── preprod/
│   └── production/
│
├── builds/                 # Artifacts de build
│   ├── {project-name}/
│   │   └── {version}/
│   │       └── {timestamp}/
│
├── releases/               # Releases taggées
│   ├── {project-name}/
│   │   └── {semantic-version}/
│
├── backups/                # Sauvegardes
│   ├── daily/
│   │   └── {YYYY-MM-DD}/
│   ├── weekly/
│   │   └── {YYYY-Www}/      # Ex: 2024-W52
│   └── monthly/
│       └── {YYYY-MM}/
│
├── logs/                   # Logs centralisés
│   ├── applications/
│   │   └── {app-name}/
│   │       └── {YYYY-MM}/
│   ├── systems/
│   │   ├── nginx/
│   │   ├── apache/
│   │   └── mysql/
│   └── archived/           # Logs > 90 jours
│
├── data/                   # Données persistantes
│   ├── databases/
│   ├── uploads/
│   └── cache/
│
├── shared/                 # Ressources partagées
│   ├── config/
│   ├── libraries/
│   └── templates/
│
├── docs/                   # Documentation
│   ├── architecture/
│   ├── api/
│   ├── deployment/
│   └── runbooks/
│
├── scripts/                # Scripts utilitaires
│   ├── deployment/
│   ├── backup/
│   ├── monitoring/
│   └── maintenance/
│
└── tmp/                    # Fichiers temporaires
    └── {timestamp}/
```

---

### ÉTAPE 2 : Créer le script d'initialisation de la structure

```bash
#!/bin/bash
# init-company-structure.sh
# Crée la structure complète de l'entreprise

set -euo pipefail  # Exit on error, undefined vars, pipe failures

# ═══════════════════════════════════════════════════════════════
# CONFIGURATION
# ═══════════════════════════════════════════════════════════════

BASE_DIR="/opt/company"
COMPANY_NAME="MyCompany"
CREATED_BY=$(whoami)
CREATED_AT=$(date +"%Y-%m-%d %H:%M:%S")

# Couleurs pour output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# ═══════════════════════════════════════════════════════════════
# FONCTIONS UTILITAIRES
# ═══════════════════════════════════════════════════════════════

log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}

warn() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1"
    exit 1
}

# Créer un dossier avec documentation
create_dir_with_readme() {
    local dir="$1"
    local description="$2"
    
    mkdir -p "$dir"
    
    cat > "$dir/README.md" << EOF
# $(basename "$dir")

**Description:** $description

**Créé le:** $CREATED_AT  
**Créé par:** $CREATED_BY  
**Structure:** $COMPANY_NAME

## Utilisation

$3

## Maintenance

- Nettoyage automatique: ${4:-"Manuel"}
- Backups: ${5:-"Non configuré"}

---
*Dernière mise à jour: $(date +"%Y-%m-%d")*
EOF
    
    log "[OK] Créé: $dir"
}

# ═══════════════════════════════════════════════════════════════
# VÉRIFICATIONS PRE-INSTALLATION
# ═══════════════════════════════════════════════════════════════

log "[RECHERCHE] Vérifications préliminaires..."

# Vérifier les droits
if [ ! -w "$(dirname "$BASE_DIR")" ]; then
    error "Pas de droits d'écriture sur $(dirname "$BASE_DIR"). Utilisez sudo."
fi

# Vérifier si la structure existe déjà
if [ -d "$BASE_DIR" ]; then
    warn "La structure $BASE_DIR existe déjà."
    read -p "Écraser ? (y/N) " confirm
    if [ "$confirm" != "y" ]; then
        error "Installation annulée."
    fi
    rm -rf "$BASE_DIR"
fi

log "[OK] Vérifications OK"

# ═══════════════════════════════════════════════════════════════
# CRÉATION DE LA STRUCTURE
# ═══════════════════════════════════════════════════════════════

log "[CONSTRUCTION]  Création de la structure..."

# Dossier racine
mkdir -p "$BASE_DIR"
cd "$BASE_DIR"

# ───────────────────────────────────────────────────────────────
# PROJECTS
# ───────────────────────────────────────────────────────────────

log "[DOSSIER] Création de projects/..."

create_dir_with_readme "projects/frontend/web-app" \
    "Application web principale (React)" \
    "Code source du front-end web" \
    "Quotidien" \
    "Inclus dans backups/daily"

create_dir_with_readme "projects/frontend/mobile-app" \
    "Application mobile (React Native)" \
    "Code source de l'app mobile"

create_dir_with_readme "projects/frontend/admin-panel" \
    "Panel d'administration" \
    "Interface d'admin"

create_dir_with_readme "projects/backend/api-gateway" \
    "Gateway API principal" \
    "Point d'entrée unique pour toutes les APIs"

create_dir_with_readme "projects/backend/auth-service" \
    "Service d'authentification" \
    "Gestion auth, JWT, OAuth"

create_dir_with_readme "projects/backend/payment-service" \
    "Service de paiement" \
    "Intégration Stripe, PayPal"

create_dir_with_readme "projects/backend/notification-service" \
    "Service de notifications" \
    "Emails, SMS, push notifications"

create_dir_with_readme "projects/infrastructure/terraform" \
    "Infrastructure as Code" \
    "Provisioning AWS/Azure/GCP"

create_dir_with_readme "projects/infrastructure/ansible" \
    "Configuration management" \
    "Playbooks Ansible"

create_dir_with_readme "projects/infrastructure/kubernetes" \
    "Orchestration containers" \
    "Manifests K8s, Helm charts"

# ───────────────────────────────────────────────────────────────
# ENVIRONMENTS
# ───────────────────────────────────────────────────────────────

log "[MONDE] Création de environments/..."

for env in dev test staging preprod production; do
    create_dir_with_readme "environments/$env" \
        "Environnement $env" \
        "Déploiements pour l'environnement $env"
    
    # Sous-structure par environnement
    mkdir -p "environments/$env"/{web,api,workers,databases}
done

# ───────────────────────────────────────────────────────────────
# BUILDS
# ───────────────────────────────────────────────────────────────

log "[OUTIL] Création de builds/..."

create_dir_with_readme "builds" \
    "Artifacts de build" \
    "Stocke les builds compilés" \
    "Automatique (>30 jours)" \
    "Non"

# Exemples de structure builds
mkdir -p builds/web-app/v1.0.0/$(date +%Y%m%d_%H%M%S)
mkdir -p builds/api-gateway/v2.1.0/$(date +%Y%m%d_%H%M%S)

# ───────────────────────────────────────────────────────────────
# RELEASES
# ───────────────────────────────────────────────────────────────

log "[PACKAGE] Création de releases/..."

create_dir_with_readme "releases" \
    "Releases officielles" \
    "Versions taggées et validées" \
    "Jamais supprimé" \
    "Oui"

mkdir -p releases/{web-app,api-gateway,mobile-app}

# ───────────────────────────────────────────────────────────────
# BACKUPS
# ───────────────────────────────────────────────────────────────

log "[SAUVEGARDE] Création de backups/..."

create_dir_with_readme "backups" \
    "Sauvegardes système" \
    "Backups quotidiens, hebdo, mensuels" \
    "Automatique (rotation)" \
    "N/A"

# Structure de backups avec dates
mkdir -p "backups/daily/$(date +%Y-%m-%d)"
mkdir -p "backups/weekly/$(date +%Y-W%V)"
mkdir -p "backups/monthly/$(date +%Y-%m)"

# ───────────────────────────────────────────────────────────────
# LOGS
# ───────────────────────────────────────────────────────────────

log "[LISTE] Création de logs/..."

create_dir_with_readme "logs/applications" \
    "Logs applicatifs" \
    "Logs de toutes les applications" \
    "Rotation quotidienne" \
    "Oui (7 jours)"

create_dir_with_readme "logs/systems" \
    "Logs système" \
    "Nginx, Apache, MySQL, etc." \
    "Rotation quotidienne"

mkdir -p logs/applications/{web-app,api-gateway,auth-service}
mkdir -p logs/systems/{nginx,apache,mysql,redis}
mkdir -p logs/archived

# ───────────────────────────────────────────────────────────────
# DATA
# ───────────────────────────────────────────────────────────────

log "[DISQUE] Création de data/..."

create_dir_with_readme "data/databases" \
    "Bases de données" \
    "Fichiers de bases de données" \
    "Selon politique" \
    "Oui (quotidien)"

mkdir -p data/{uploads,cache,sessions}

# ───────────────────────────────────────────────────────────────
# SHARED
# ───────────────────────────────────────────────────────────────

log "[LIEN] Création de shared/..."

create_dir_with_readme "shared/config" \
    "Configurations partagées" \
    "Config commune à tous les projets"

mkdir -p shared/{libraries,templates,assets}

# ───────────────────────────────────────────────────────────────
# DOCS
# ───────────────────────────────────────────────────────────────

log "[DOCS] Création de docs/..."

create_dir_with_readme "docs" \
    "Documentation technique" \
    "Toute la documentation du système"

mkdir -p docs/{architecture,api,deployment,runbooks,diagrams}

# ───────────────────────────────────────────────────────────────
# SCRIPTS
# ───────────────────────────────────────────────────────────────

log "[CONFIG] Création de scripts/..."

create_dir_with_readme "scripts" \
    "Scripts utilitaires" \
    "Automatisation et outils"

mkdir -p scripts/{deployment,backup,monitoring,maintenance,utils}

# ───────────────────────────────────────────────────────────────
# TMP
# ───────────────────────────────────────────────────────────────

log "[SUPPRIMER] Création de tmp/..."

create_dir_with_readme "tmp" \
    "Fichiers temporaires" \
    "Nettoyé automatiquement chaque jour" \
    "Quotidien (>24h)"

mkdir -p "tmp/$(date +%Y%m%d)"

# ═══════════════════════════════════════════════════════════════
# CRÉATION DES LIENS SYMBOLIQUES
# ═══════════════════════════════════════════════════════════════

log "[LIEN] Création des liens symboliques..."

# Lien "current" vers l'environnement de production
ln -s environments/production current-production

# Lien vers les logs du jour
ln -s "logs/applications" logs-today

# Lien vers le dernier backup
ln -s "backups/daily/$(date +%Y-%m-%d)" latest-backup

log "[OK] Liens symboliques créés"

# ═══════════════════════════════════════════════════════════════
# PERMISSIONS
# ═══════════════════════════════════════════════════════════════

log "[SECURISE] Configuration des permissions..."

# Dossiers en lecture/exécution pour tous
find . -type d -exec chmod 755 {} \;

# Fichiers en lecture pour tous
find . -type f -exec chmod 644 {} \;

# Scripts exécutables
find scripts/ -type f -name "*.sh" -exec chmod 755 {} \;

# Données sensibles : accès restreint
chmod 700 data/databases
chmod 700 backups

log "[OK] Permissions configurées"

# ═══════════════════════════════════════════════════════════════
# CRÉATION DU README PRINCIPAL
# ═══════════════════════════════════════════════════════════════

cat > README.md << 'EOF'
# Structure Système - MyCompany

**Créé le:** $CREATED_AT  
**Créé par:** $CREATED_BY

## [CONSTRUCTION] Architecture

Cette structure suit les meilleures pratiques de l'industrie :

- **FHS compliant** (Filesystem Hierarchy Standard)
- **Scalable** (facile d'ajouter de nouveaux projets)
- **Maintenable** (structure claire et documentée)
- **Sécurisé** (permissions appropriées)

## [DOSSIER] Structure Principale

```
/opt/company/
├── projects/        # Code source
├── environments/    # Déploiements
├── builds/          # Artifacts
├── releases/        # Versions
├── backups/         # Sauvegardes
├── logs/            # Logs
├── data/            # Données
├── shared/          # Ressources partagées
├── docs/            # Documentation
├── scripts/         # Automatisation
└── tmp/             # Temporaire
```

## [RAPIDE] Quick Start

### Navigation rapide

```bash
# Aller à la production
cd /opt/company/current-production

# Voir les logs du jour
cd /opt/company/logs-today

# Accéder au dernier backup
cd /opt/company/latest-backup
```

### Ajouter un nouveau projet

```bash
/opt/company/scripts/utils/add-project.sh <nom-du-projet>
```

### Créer un backup

```bash
/opt/company/scripts/backup/create-backup.sh
```

## [DOCS] Documentation

Voir `docs/` pour la documentation complète :

- `docs/architecture/` : Diagrammes et spécifications
- `docs/api/` : Documentation API
- `docs/deployment/` : Guides de déploiement
- `docs/runbooks/` : Procédures d'exploitation

## [OUTIL] Maintenance

### Nettoyage automatique

Les scripts de maintenance s'exécutent via cron :

- **Quotidien** : Nettoyage tmp/ et logs anciens
- **Hebdomadaire** : Consolidation backups
- **Mensuel** : Archivage et reporting

### Monitoring

```bash
# Vérifier l'utilisation disque
/opt/company/scripts/monitoring/disk-usage.sh

# Vérifier les liens brisés
/opt/company/scripts/monitoring/check-links.sh

# Audit de structure
/opt/company/scripts/monitoring/validate-structure.sh
```

## 🆘 Support

En cas de problème, consulter :

1. `docs/runbooks/troubleshooting.md`
2. Logs dans `logs/`
3. Contacter l'équipe DevOps

---

**[ATTENTION] IMPORTANT**

- Ne jamais modifier manuellement `data/databases/`
- Les backups sont automatiques (pas besoin d'action manuelle)
- Toujours créer une branche dans `projects/` avant modifications

---

*Généré automatiquement par init-company-structure.sh*
EOF

log "[OK] README principal créé"

# ═══════════════════════════════════════════════════════════════
# RAPPORT FINAL
# ═══════════════════════════════════════════════════════════════

log ""
log "╔════════════════════════════════════════════════════════╗"
log "║                                                        ║"
log "║     [OK] STRUCTURE CRÉÉE AVEC SUCCÈS                     ║"
log "║                                                        ║"
log "╚════════════════════════════════════════════════════════╝"
log ""

# Statistiques
TOTAL_DIRS=$(find "$BASE_DIR" -type d | wc -l)
TOTAL_FILES=$(find "$BASE_DIR" -type f | wc -l)
TOTAL_LINKS=$(find "$BASE_DIR" -type l | wc -l)
DISK_USAGE=$(du -sh "$BASE_DIR" | cut -f1)

log "[GRAPHIQUE] Statistiques :"
log "   - Dossiers créés : $TOTAL_DIRS"
log "   - Fichiers créés : $TOTAL_FILES"
log "   - Liens symboliques : $TOTAL_LINKS"
log "   - Espace utilisé : $DISK_USAGE"
log ""
log "[IMPORTANT] Emplacement : $BASE_DIR"
log ""
log "[RAPIDE] Prochaines étapes :"
log "   1. cd $BASE_DIR"
log "   2. cat README.md"
log "   3. tree -L 2 (pour visualiser)"
log ""
log "[OK] Installation terminée !"
```

**Rendre exécutable et lancer :**

```bash
chmod +x init-company-structure.sh
sudo ./init-company-structure.sh
```

---

### ÉTAPE 3 : Créer les scripts utilitaires

**Script 1 : Ajouter un nouveau projet**

```bash
#!/bin/bash
# scripts/utils/add-project.sh

set -euo pipefail

PROJECT_NAME=$1
PROJECT_TYPE=${2:-"backend"}  # backend, frontend, infrastructure

if [ -z "$PROJECT_NAME" ]; then
    echo "Usage: $0 <project-name> [backend|frontend|infrastructure]"
    exit 1
fi

BASE_DIR="/opt/company"
PROJECT_DIR="$BASE_DIR/projects/$PROJECT_TYPE/$PROJECT_NAME"

if [ -d "$PROJECT_DIR" ]; then
    echo "[X] Le projet $PROJECT_NAME existe déjà"
    exit 1
fi

echo "[DOSSIER] Création du projet : $PROJECT_NAME ($PROJECT_TYPE)"

# Créer la structure du projet
mkdir -p "$PROJECT_DIR"/{src,tests,docs,config}

# Créer le README
cat > "$PROJECT_DIR/README.md" << EOF
# $PROJECT_NAME

**Type:** $PROJECT_TYPE  
**Créé le:** $(date +"%Y-%m-%d")  
**Status:** [JAUNE] En développement

## Description

[Description du projet]

## Installation

\`\`\`bash
# Instructions d'installation
\`\`\`

## Utilisation

\`\`\`bash
# Exemples d'utilisation
\`\`\`

## Configuration

Voir \`config/\` pour les fichiers de configuration.

## Tests

\`\`\`bash
# Lancer les tests
\`\`\`

## Déploiement

Voir \`docs/deployment.md\`

## Maintenance

- **Responsable:** [Nom]
- **Repository:** [URL Git]
- **CI/CD:** [URL Pipeline]

---
EOF

# Créer un fichier .gitignore
cat > "$PROJECT_DIR/.gitignore" << EOF
# Dependencies
node_modules/
vendor/

# Build
build/
dist/
*.o
*.so

# Environment
.env
.env.local

# IDE
.vscode/
.idea/
*.swp

# OS
.DS_Store
Thumbs.db

# Logs
*.log
logs/

# Temporary
tmp/
temp/
EOF

# Créer les dossiers dans builds/ et logs/
mkdir -p "$BASE_DIR/builds/$PROJECT_NAME"
mkdir -p "$BASE_DIR/logs/applications/$PROJECT_NAME"

# Créer un lien rapide depuis la racine
ln -s "projects/$PROJECT_TYPE/$PROJECT_NAME" "$BASE_DIR/quick-$PROJECT_NAME"

echo "[OK] Projet créé : $PROJECT_DIR"
echo "[LIEN] Accès rapide : cd /opt/company/quick-$PROJECT_NAME"
echo ""
echo "[NOTE] Prochaines étapes :"
echo "   1. Éditer $PROJECT_DIR/README.md"
echo "   2. Initialiser git : cd $PROJECT_DIR && git init"
echo "   3. Commencer à coder dans $PROJECT_DIR/src/"
```

---

**Script 2 : Navigation rapide avec menu interactif**

```bash
#!/bin/bash
# scripts/utils/nav.sh - Navigation rapide interactive

BASE_DIR="/opt/company"

# Fonction pour afficher un menu
show_menu() {
    local title=$1
    shift
    local options=("$@")
    
    echo "╔══════════════════════════════════════╗"
    echo "║  $title"
    echo "╠══════════════════════════════════════╣"
    
    for i in "${!options[@]}"; do
        printf "║  %2d. %-32s ║\n" $((i+1)) "${options[$i]}"
    done
    
    echo "║   0. Quitter                         ║"
    echo "╚══════════════════════════════════════╝"
}

# Menu principal
while true; do
    clear
    show_menu "[COMPASS] NAVIGATION RAPIDE" \
        "[DOSSIER] Projets" \
        "[MONDE] Environnements" \
        "[PACKAGE] Releases" \
        "[SAUVEGARDE] Backups" \
        "[LISTE] Logs" \
        "[DOCS] Documentation" \
        "[CONFIG]  Scripts"
    
    read -p "Choix : " choice
    
    case $choice in
        1)
            # Sous-menu Projets
            clear
            show_menu "[DOSSIER] PROJETS" \
                "Frontend" \
                "Backend" \
                "Infrastructure"
            
            read -p "Choix : " subchoice
            case $subchoice in
                1) cd "$BASE_DIR/projects/frontend" ;;
                2) cd "$BASE_DIR/projects/backend" ;;
                3) cd "$BASE_DIR/projects/infrastructure" ;;
                0) continue ;;
            esac
            ;;
        2)
            # Environnements
            clear
            show_menu "[MONDE] ENVIRONNEMENTS" \
                "Development" \
                "Test" \
                "Staging" \
                "Pre-production" \
                "Production"
            
            read -p "Choix : " subchoice
            case $subchoice in
                1) cd "$BASE_DIR/environments/dev" ;;
                2) cd "$BASE_DIR/environments/test" ;;
                3) cd "$BASE_DIR/environments/staging" ;;
                4) cd "$BASE_DIR/environments/preprod" ;;
                5) cd "$BASE_DIR/environments/production" ;;
                0) continue ;;
            esac
            ;;
        3) cd "$BASE_DIR/releases" ;;
        4) cd "$BASE_DIR/backups" ;;
        5) cd "$BASE_DIR/logs" ;;
        6) cd "$BASE_DIR/docs" ;;
        7) cd "$BASE_DIR/scripts" ;;
        0) exit 0 ;;
    esac
    
    # Si on a changé de dossier, ouvrir un shell
    if [ "$PWD" != "$BASE_DIR" ]; then
        echo ""
        echo "[IMPORTANT] Position actuelle : $PWD"
        echo ""
        $SHELL
    fi
done
```

---

**Script 3 : Monitoring de l'utilisation disque**

```bash
#!/bin/bash
# scripts/monitoring/disk-usage.sh

BASE_DIR="/opt/company"

echo "[DISQUE] Utilisation Disque - MyCompany"
echo "════════════════════════════════════════"
echo ""

# Utilisation globale
echo "[GRAPHIQUE] Utilisation globale :"
du -sh "$BASE_DIR"
echo ""

# Par dossier principal
echo "[DOSSIER] Par dossier :"
du -sh "$BASE_DIR"/* | sort -hr | head -10
echo ""

# Détails sur les plus gros dossiers
echo "[RECHERCHE] Top 10 des fichiers les plus volumineux :"
find "$BASE_DIR" -type f -exec du -h {} + | sort -rh | head -10
echo ""

# Utilisation par projet
echo "[PACKAGE] Utilisation par projet :"
for project in "$BASE_DIR/projects"/*/*; do
    if [ -d "$project" ]; then
        size=$(du -sh "$project" | cut -f1)
        name=$(basename "$project")
        echo "  $name: $size"
    fi
done
echo ""

# Avertissements
USAGE=$(df -h "$BASE_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')

if [ "$USAGE" -gt 80 ]; then
    echo "[ATTENTION]  AVERTISSEMENT : Disque à ${USAGE}% !"
    echo "   Actions recommandées :"
    echo "   - Nettoyer tmp/ : rm -rf $BASE_DIR/tmp/*"
    echo "   - Archiver vieux logs : $BASE_DIR/scripts/maintenance/archive-logs.sh"
    echo "   - Supprimer vieux builds : $BASE_DIR/scripts/maintenance/clean-builds.sh"
elif [ "$USAGE" -gt 90 ]; then
    echo "[ALERTE] CRITIQUE : Disque à ${USAGE}% !"
    echo "   Action immédiate requise !"
else
    echo "[OK] Utilisation disque normale (${USAGE}%)"
fi
```

---

**Script 4 : Validation de la structure**

```bash
#!/bin/bash
# scripts/monitoring/validate-structure.sh

BASE_DIR="/opt/company"
ERRORS=0

echo "[RECHERCHE] Validation de la structure..."
echo ""

# Fonction de vérification
check_dir() {
    local dir="$1"
    local desc="$2"
    
    if [ -d "$dir" ]; then
        echo "[OK] $desc"
    else
        echo "[X] MANQUANT: $desc ($dir)"
        ((ERRORS++))
    fi
}

# Vérifier les dossiers principaux
check_dir "$BASE_DIR/projects" "Dossier projects"
check_dir "$BASE_DIR/environments" "Dossier environments"
check_dir "$BASE_DIR/builds" "Dossier builds"
check_dir "$BASE_DIR/releases" "Dossier releases"
check_dir "$BASE_DIR/backups" "Dossier backups"
check_dir "$BASE_DIR/logs" "Dossier logs"
check_dir "$BASE_DIR/data" "Dossier data"
check_dir "$BASE_DIR/shared" "Dossier shared"
check_dir "$BASE_DIR/docs" "Dossier docs"
check_dir "$BASE_DIR/scripts" "Dossier scripts"

echo ""

# Vérifier les liens symboliques
echo "[LIEN] Vérification des liens symboliques..."

check_link() {
    local link="$1"
    local desc="$2"
    
    if [ -L "$link" ]; then
        if [ -e "$link" ]; then
            echo "[OK] $desc (OK)"
        else
            echo "[X] BRISÉ: $desc"
            ((ERRORS++))
        fi
    else
        echo "[ATTENTION]  MANQUANT: $desc"
    fi
}

check_link "$BASE_DIR/current-production" "Lien current-production"
check_link "$BASE_DIR/logs-today" "Lien logs-today"
check_link "$BASE_DIR/latest-backup" "Lien latest-backup"

echo ""

# Vérifier les permissions
echo "[SECURISE] Vérification des permissions..."

PERMS_OK=0

check_perms() {
    local path="$1"
    local expected="$2"
    
    if [ -e "$path" ]; then
        actual=$(stat -c "%a" "$path")
        if [ "$actual" = "$expected" ]; then
            PERMS_OK=$((PERMS_OK + 1))
        else
            echo "[ATTENTION]  $path: $actual (attendu: $expected)"
        fi
    fi
}

check_perms "$BASE_DIR/data/databases" "700"
check_perms "$BASE_DIR/backups" "700"

echo "[OK] $PERMS_OK permissions critiques correctes"
echo ""

# Rapport final
echo "════════════════════════════════════════"
if [ $ERRORS -eq 0 ]; then
    echo "[OK] Structure valide ! Aucune erreur."
else
    echo "[X] $ERRORS erreur(s) trouvée(s)"
    exit 1
fi
```

---

### ÉTAPE 4 : Créer un système d'alias et variables d'environnement

```bash
#!/bin/bash
# scripts/utils/setup-env.sh
# À sourcer dans ~/.bashrc

# ═══════════════════════════════════════════════════════════════
# VARIABLES D'ENVIRONNEMENT
# ═══════════════════════════════════════════════════════════════

export COMPANY_ROOT="/opt/company"
export COMPANY_PROJECTS="$COMPANY_ROOT/projects"
export COMPANY_ENVS="$COMPANY_ROOT/environments"
export COMPANY_LOGS="$COMPANY_ROOT/logs"
export COMPANY_SCRIPTS="$COMPANY_ROOT/scripts"

# ═══════════════════════════════════════════════════════════════
# ALIAS NAVIGATION
# ═══════════════════════════════════════════════════════════════

# Navigation rapide
alias cdcompany='cd $COMPANY_ROOT'
alias cdprojects='cd $COMPANY_PROJECTS'
alias cdprod='cd $COMPANY_ENVS/production'
alias cddev='cd $COMPANY_ENVS/dev'
alias cdlogs='cd $COMPANY_LOGS'
alias cdscripts='cd $COMPANY_SCRIPTS'

# Alias par type de projet
alias cdfrontend='cd $COMPANY_PROJECTS/frontend'
alias cdbackend='cd $COMPANY_PROJECTS/backend'
alias cdinfra='cd $COMPANY_PROJECTS/infrastructure'

# ═══════════════════════════════════════════════════════════════
# ALIAS UTILITAIRES
# ═══════════════════════════════════════════════════════════════

# Listing amélioré
alias lsc='ls -lh --color=auto'
alias lst='tree -L 2'
alias lsproj='ls -lh $COMPANY_PROJECTS/*/*'

# Navigation interactive
alias nav='$COMPANY_SCRIPTS/utils/nav.sh'

# Monitoring
alias diskusage='$COMPANY_SCRIPTS/monitoring/disk-usage.sh'
alias validate='$COMPANY_SCRIPTS/monitoring/validate-structure.sh'
alias checklinks='find $COMPANY_ROOT -xtype l'

# Logs
alias loglive='tail -f $COMPANY_LOGS/applications/*/latest.log'
alias logerrors='grep -r ERROR $COMPANY_LOGS/applications/'

# Backup
alias backup-now='$COMPANY_SCRIPTS/backup/create-backup.sh'
alias backup-list='ls -lh $COMPANY_ROOT/backups/daily/'

# ═══════════════════════════════════════════════════════════════
# FONCTIONS UTILITAIRES
# ═══════════════════════════════════════════════════════════════

# Trouver un projet par nom
findproject() {
    find "$COMPANY_PROJECTS" -type d -name "*$1*" | head -5
}

# Aller directement à un projet
gotoproject() {
    local project=$(find "$COMPANY_PROJECTS" -type d -name "$1" | head -1)
    if [ -n "$project" ]; then
        cd "$project"
    else
        echo "[X] Projet '$1' non trouvé"
    fi
}

# Créer un nouveau projet rapidement
newproject() {
    $COMPANY_SCRIPTS/utils/add-project.sh "$@"
}

# Voir le statut d'un projet
projectstatus() {
    local project=$1
    if [ -z "$project" ]; then
        echo "Usage: projectstatus <nom-projet>"
        return 1
    fi
    
    echo "[PACKAGE] Statut de $project"
    echo "════════════════════════════════════════"
    
    # Taille
    local size=$(du -sh "$COMPANY_PROJECTS"/*/"$project" 2>/dev/null | cut -f1)
    echo "[SAUVEGARDE] Taille: $size"
    
    # Dernière modification
    local lastmod=$(find "$COMPANY_PROJECTS"/*/"$project" -type f -printf '%T+ %p\n' 2>/dev/null | sort -r | head -1 | cut -d' ' -f1)
    echo "[CALENDRIER] Dernière modification: $lastmod"
    
    # Nombre de fichiers
    local filecount=$(find "$COMPANY_PROJECTS"/*/"$project" -type f 2>/dev/null | wc -l)
    echo "[FICHIER] Fichiers: $filecount"
    
    # Logs récents
    echo ""
    echo "[LISTE] Logs récents:"
    tail -5 "$COMPANY_LOGS/applications/$project/latest.log" 2>/dev/null || echo "   Aucun log"
}

# ═══════════════════════════════════════════════════════════════
# PROMPT PERSONNALISÉ
# ═══════════════════════════════════════════════════════════════

# Afficher le contexte dans le prompt
update_company_prompt() {
    # Si on est dans la structure company
    if [[ $PWD == $COMPANY_ROOT* ]]; then
        # Extraire la partie relative
        local rel_path=${PWD#$COMPANY_ROOT/}
        export PS1="\[\033[01;32m\][company:$rel_path]\[\033[00m\] \$ "
    else
        export PS1="\[\033[01;34m\]\w\[\033[00m\] \$ "
    fi
}

# Activer le prompt personnalisé
PROMPT_COMMAND="update_company_prompt"

# ═══════════════════════════════════════════════════════════════
# COMPLETION
# ═══════════════════════════════════════════════════════════════

# Auto-complétion pour gotoproject
_gotoproject_completion() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local projects=$(find "$COMPANY_PROJECTS" -mindepth 2 -maxdepth 2 -type d -exec basename {} \;)
    COMPREPLY=($(compgen -W "$projects" -- "$cur"))
}
complete -F _gotoproject_completion gotoproject

# ═══════════════════════════════════════════════════════════════
# MESSAGE DE BIENVENUE
# ═══════════════════════════════════════════════════════════════

echo "[OK] Environnement MyCompany chargé"
echo "   [IDEE] Tapez 'nav' pour navigation interactive"
echo "   [IDEE] Tapez 'cdcompany' pour aller à la racine"
```

**Ajouter à ~/.bashrc :**

```bash
echo "source /opt/company/scripts/utils/setup-env.sh" >> ~/.bashrc
source ~/.bashrc
```

---

### ÉTAPE 5 : Créer un système de recherche avancé

```bash
#!/bin/bash
# scripts/utils/search.sh - Recherche multi-critères

COMPANY_ROOT="/opt/company"

# Fonction d'aide
show_help() {
    cat << EOF
[RECHERCHE] Recherche Avancée MyCompany

Usage: $0 [OPTIONS] PATTERN

OPTIONS:
    -t, --type TYPE          Type de recherche (file, dir, link)
    -p, --project NAME       Chercher dans un projet spécifique
    -e, --env ENV            Chercher dans un environnement
    -d, --days N             Fichiers modifiés dans les N derniers jours
    -s, --size SIZE          Taille (ex: +1M, -100k)
    --content TEXT           Chercher dans le contenu des fichiers
    --ext EXT                Extension (ex: .js, .py)
    
EXEMPLES:
    # Trouver tous les fichiers .js
    $0 --ext .js "component"
    
    # Fichiers modifiés dans les 7 derniers jours
    $0 -d 7
    
    # Chercher "TODO" dans le code
    $0 --content "TODO"
    
    # Fichiers >1MB dans production
    $0 -e production -s +1M
EOF
}

# Variables par défaut
TYPE=""
PROJECT=""
ENV=""
DAYS=""
SIZE=""
CONTENT=""
EXT=""
PATTERN=""

# Parser les arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -t|--type)
            TYPE="$2"
            shift 2
            ;;
        -p|--project)
            PROJECT="$2"
            shift 2
            ;;
        -e|--env)
            ENV="$2"
            shift 2
            ;;
        -d|--days)
            DAYS="$2"
            shift 2
            ;;
        -s|--size)
            SIZE="$2"
            shift 2
            ;;
        --content)
            CONTENT="$2"
            shift 2
            ;;
        --ext)
            EXT="$2"
            shift 2
            ;;
        *)
            PATTERN="$1"
            shift
            ;;
    esac
done

# Construire la commande find
FIND_CMD="find $COMPANY_ROOT"

# Définir le point de départ
if [ -n "$PROJECT" ]; then
    FIND_CMD="find $COMPANY_ROOT/projects/*/$PROJECT"
elif [ -n "$ENV" ]; then
    FIND_CMD="find $COMPANY_ROOT/environments/$ENV"
fi

# Ajouter les critères
if [ -n "$TYPE" ]; then
    FIND_CMD="$FIND_CMD -type $TYPE"
fi

if [ -n "$DAYS" ]; then
    FIND_CMD="$FIND_CMD -mtime -$DAYS"
fi

if [ -n "$SIZE" ]; then
    FIND_CMD="$FIND_CMD -size $SIZE"
fi

if [ -n "$EXT" ]; then
    FIND_CMD="$FIND_CMD -name '*$EXT'"
fi

if [ -n "$PATTERN" ]; then
    FIND_CMD="$FIND_CMD -name '*$PATTERN*'"
fi

# Exécuter la recherche
echo "[RECHERCHE] Recherche en cours..."
echo ""

if [ -n "$CONTENT" ]; then
    # Recherche dans le contenu
    grep -r "$CONTENT" "$COMPANY_ROOT" --include="*$EXT" 2>/dev/null
else
    # Recherche par nom/critères
    eval "$FIND_CMD" 2>/dev/null | while read result; do
        # Afficher avec des infos supplémentaires
        size=$(du -sh "$result" 2>/dev/null | cut -f1)
        date=$(stat -c "%y" "$result" 2>/dev/null | cut -d' ' -f1)
        echo "$result"
        echo "  └─ [PACKAGE] $size | [CALENDRIER] $date"
    done
fi
```

---

### ÉTAPE 6 : Tests de validation

```bash
#!/bin/bash
# scripts/monitoring/run-tests.sh

echo "[TEST] Suite de tests de validation"
echo "════════════════════════════════════════"
echo ""

PASSED=0
FAILED=0

# Fonction de test
test_case() {
    local name="$1"
    local command="$2"
    
    echo -n "Test: $name ... "
    
    if eval "$command" &>/dev/null; then
        echo "[OK] PASS"
        ((PASSED++))
    else
        echo "[X] FAIL"
        ((FAILED++))
    fi
}

# Tests de structure
test_case "Structure racine existe" "[ -d /opt/company ]"
test_case "Dossier projects existe" "[ -d /opt/company/projects ]"
test_case "Dossier environments existe" "[ -d /opt/company/environments ]"
test_case "README principal existe" "[ -f /opt/company/README.md ]"

# Tests de liens
test_case "Lien current-production existe" "[ -L /opt/company/current-production ]"
test_case "Lien logs-today existe" "[ -L /opt/company/logs-today ]"

# Tests de permissions
test_case "Permissions sur databases = 700" "[ $(stat -c '%a' /opt/company/data/databases) = '700' ]"

# Tests de scripts
test_case "Script nav.sh exécutable" "[ -x /opt/company/scripts/utils/nav.sh ]"

# Tests de recherche
test_case "Recherche fonctionne" "/opt/company/scripts/utils/search.sh README"

echo ""
echo "════════════════════════════════════════"
echo "Résultats: $PASSED passés, $FAILED échoués"

if [ $FAILED -eq 0 ]; then
    echo "[OK] Tous les tests sont passés !"
    exit 0
else
    echo "[X] Certains tests ont échoué"
    exit 1
fi
```

---

### [OK] TESTS DE VALIDATION COMPLETS

**1. Vérifier la structure complète**

```bash
cd /opt/company
tree -L 3 | head -50
```

---

**2. Tester les alias**

```bash
source ~/.bashrc

# Test navigation
cdcompany && pwd
cdprojects && pwd
cdprod && pwd

# Test listing
lsproj
```

---

**3. Tester la recherche**

```bash
/opt/company/scripts/utils/search.sh --ext .md README
/opt/company/scripts/utils/search.sh -d 1
/opt/company/scripts/utils/search.sh --content "TODO"
```

---

**4. Tester l'ajout de projet**

```bash
/opt/company/scripts/utils/add-project.sh test-app frontend
ls -l /opt/company/projects/frontend/test-app/
```

---

**5. Vérifier le monitoring**

```bash
/opt/company/scripts/monitoring/disk-usage.sh
/opt/company/scripts/monitoring/validate-structure.sh
/opt/company/scripts/monitoring/run-tests.sh
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Architecture professionnelle**
- Séparation des préoccupations
- Structure scalable
- Conventions de nommage
- Documentation intégrée

**2. Automatisation**
- Scripts de création
- Scripts de maintenance
- Scripts de monitoring
- Tests automatisés

**3. Navigation efficace**
- Alias pertinents
- Variables d'environnement
- Menu interactif
- Auto-complétion

**4. Recherche avancée**
- Multi-critères
- Par contenu
- Par métadonnées
- Filtres combinables

**5. Maintenance**
- Validation automatique
- Monitoring disque
- Détection de problèmes
- Reporting

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Intégration avec Git**

Créer un script qui initialise automatiquement Git dans les nouveaux projets :

```bash
# Dans add-project.sh, ajouter :
cd "$PROJECT_DIR"
git init
git add .
git commit -m "Initial commit - $PROJECT_NAME"
```

---

**2. Notifications automatiques**

Envoyer des alertes quand l'espace disque est faible :

```bash
# Dans disk-usage.sh, ajouter :
if [ "$USAGE" -gt 90 ]; then
    echo "Disque critique !" | mail -s "ALERT: Disque" admin@company.com
fi
```

---

**3. Dashboard web**

Créer une interface web pour visualiser la structure :

```bash
# Générer un HTML avec tree
tree -H . -o /var/www/structure.html /opt/company
```

---

**4. Synchronisation avec le cloud**

Automatiser les backups vers S3/GCS :

```bash
# Dans backup script
aws s3 sync /opt/company/backups/ s3://company-backups/
```

---

**5. Métriques et analytics**

Collecter des métriques sur l'utilisation :

```bash
# Prometheus exporter
echo "company_projects_count $(find /opt/company/projects -mindepth 2 -maxdepth 2 -type d | wc -l)" > /var/lib/node_exporter/company.prom
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1.5

**[OK] BRAVO ! Tu maîtrises maintenant l'organisation professionnelle sous Linux !**

**Ce que tu as appris :**
- Concevoir une architecture scalable
- Automatiser la création de structures
- Créer des systèmes de navigation avancés
- Implémenter des conventions professionnelles
- Développer des outils de monitoring
- Écrire des scripts de maintenance
- Documenter une architecture

**Compétences acquises :**
- [OK] Architecture de systèmes (niveau expert)
- [OK] Automatisation bash (niveau avancé)
- [OK] Conventions professionnelles
- [OK] DevOps best practices
- [OK] Maintenance et monitoring

**Temps moyen de réalisation :** 2-3 heures

---

# [BRAVO] CONCLUSION DE LA SECTION 1 COMPLÈTE

**[TROPHEE] FÉLICITATIONS ! Tu as terminé TOUS les exercices de Navigation & Système de fichiers !**

**Récapitulatif des 5 exercices :**

| Exercice | Niveau | Compétences |
|----------|--------|-------------|
| 1.1 | [VERT] Débutant | Structure de projet, navigation de base |
| 1.2 | [JAUNE] Intermédiaire | Copie, déplacement, backups |
| 1.3 | [JAUNE] Intermédiaire | Suppression sécurisée, corbeille |
| 1.4 | [ROUGE] Avancé | Liens symboliques et durs, gestion versions |
| 1.5 | [ROUGE] Expert | Organisation professionnelle, automatisation |

**Niveau atteint : EXPERT en Navigation & Fichiers Linux ! [RAPIDE]**

---

Veux-tu que je continue avec la **Section 2 : Affichage & Manipulation** (cat, grep, sed, awk, etc.) ?

# [DOCS] SECTION 2 : AFFICHAGE & MANIPULATION DE TEXTE

# [VERT] EXERCICE 2.1 : AFFICHAGE DE FICHIERS (DÉBUTANT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois analyser quotidiennement :
- Des fichiers de logs d'applications (milliers de lignes)
- Des fichiers de configuration
- Des fichiers de données CSV
- Des fichiers de code source
- Des rapports de monitoring

**Problématique :** Visualiser efficacement des fichiers sans les ouvrir dans un éditeur.

### Cahier des charges

- Afficher rapidement le contenu de fichiers
- Voir le début ou la fin d'un fichier
- Suivre les logs en temps réel
- Naviguer dans de gros fichiers
- Combiner plusieurs fichiers
- Compter les lignes/mots/caractères
- Gérer différents formats de fichiers

### Contraintes

- Commandes : `cat`, `less`, `more`, `head`, `tail`, `wc`, `nl`, `tac`
- Pas d'éditeur de texte (vim, nano)
- Temps estimé : 45 minutes - 1 heure

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Afficher le contenu d'un fichier (`cat`)
- [OK] Naviguer dans un fichier (`less`, `more`)
- [OK] Voir le début d'un fichier (`head`)
- [OK] Voir la fin d'un fichier (`tail`)
- [OK] Suivre un fichier en temps réel (`tail -f`)
- [OK] Compter lignes/mots/caractères (`wc`)
- [OK] Numéroter les lignes (`nl`)
- [OK] Inverser l'ordre des lignes (`tac`)
- [OK] Combiner plusieurs fichiers

---

## [DOCS] PRÉREQUIS

- Section 1 complète (Navigation & Fichiers)
- Terminal Linux fonctionnel

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer des fichiers de test réalistes

**Créer un environnement de test :**

```bash
# Créer le dossier de travail
mkdir -p ~/text-display-exercise
cd ~/text-display-exercise

# Créer des sous-dossiers
mkdir -p {logs,config,data,scripts}
```

---

**Créer un fichier de log réaliste :**

```bash
cat > logs/application.log << 'EOF'
2024-12-30 08:00:01 [INFO] Application started
2024-12-30 08:00:02 [INFO] Database connection established
2024-12-30 08:00:03 [INFO] Loading configuration from /etc/app/config.yml
2024-12-30 08:00:05 [INFO] Starting web server on port 8080
2024-12-30 08:00:10 [INFO] Server ready to accept connections
2024-12-30 08:05:23 [INFO] New connection from 192.168.1.100
2024-12-30 08:05:24 [INFO] User 'john.doe' logged in successfully
2024-12-30 08:10:45 [WARNING] Slow query detected (2.3s): SELECT * FROM users
2024-12-30 08:15:12 [ERROR] Failed to connect to payment gateway
2024-12-30 08:15:13 [ERROR] Retry attempt 1/3
2024-12-30 08:15:15 [INFO] Payment gateway connection restored
2024-12-30 08:20:33 [INFO] Processing order #12345
2024-12-30 08:20:34 [INFO] Order #12345 completed successfully
2024-12-30 08:25:11 [WARNING] Memory usage at 85%
2024-12-30 08:30:00 [INFO] Hourly backup started
2024-12-30 08:32:15 [INFO] Backup completed (2.4GB)
2024-12-30 08:35:22 [INFO] User 'jane.smith' logged in successfully
2024-12-30 08:40:01 [ERROR] Database connection lost
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect to database
2024-12-30 08:40:03 [ERROR] Application shutting down due to critical error
2024-12-30 08:40:05 [INFO] Application stopped
EOF
```

---

**Créer un fichier de configuration :**

```bash
cat > config/database.conf << 'EOF'
# Database Configuration
# Last updated: 2024-12-30

[database]
host = localhost
port = 5432
name = production_db
user = db_admin
max_connections = 100
timeout = 30

[backup]
enabled = true
frequency = daily
retention_days = 30
destination = /backups/database/

[monitoring]
enabled = true
alert_threshold = 80
email = admin@company.com

# Security settings
[security]
ssl_enabled = true
encryption = AES256
audit_log = true
EOF
```

---

**Créer un fichier CSV de données :**

```bash
cat > data/sales.csv << 'EOF'
date,product,quantity,price,customer
2024-12-01,Laptop,1,999.99,John Doe
2024-12-01,Mouse,2,29.99,Jane Smith
2024-12-02,Keyboard,1,79.99,Bob Johnson
2024-12-02,Monitor,2,299.99,Alice Williams
2024-12-03,Laptop,3,999.99,Charlie Brown
2024-12-03,Mouse,5,29.99,Diana Prince
2024-12-04,Keyboard,2,79.99,Eve Davis
2024-12-05,Monitor,1,299.99,Frank Miller
2024-12-05,Laptop,1,999.99,Grace Lee
2024-12-06,Mouse,10,29.99,Henry Wilson
2024-12-07,Keyboard,3,79.99,Ivy Chen
2024-12-08,Monitor,2,299.99,Jack Robinson
2024-12-09,Laptop,2,999.99,Karen White
2024-12-10,Mouse,4,29.99,Leo Martinez
EOF
```

---

**Créer un gros fichier de log pour les tests :**

```bash
# Générer 1000 lignes de log
for i in {1..1000}; do
    timestamp=$(date -d "2024-12-30 08:00:00 +$i seconds" +"%Y-%m-%d %H:%M:%S")
    level=$((RANDOM % 4))
    
    case $level in
        0) level_name="INFO" ;;
        1) level_name="WARNING" ;;
        2) level_name="ERROR" ;;
        3) level_name="DEBUG" ;;
    esac
    
    echo "$timestamp [$level_name] Log entry number $i"
done > logs/large.log

echo "[OK] $(wc -l < logs/large.log) lignes générées dans large.log"
```

---

**Créer un fichier multi-sections :**

```bash
cat > scripts/deploy.sh << 'EOF'
#!/bin/bash
# Deployment Script v2.0
# Author: DevOps Team
# Last updated: 2024-12-30

set -euo pipefail

# ═══════════════════════════════════════════════════════════════
# CONFIGURATION
# ═══════════════════════════════════════════════════════════════

APP_NAME="myapp"
DEPLOY_DIR="/var/www/myapp"
BACKUP_DIR="/backups/app"
GIT_REPO="git@github.com:company/myapp.git"
GIT_BRANCH="main"

# ═══════════════════════════════════════════════════════════════
# FUNCTIONS
# ═══════════════════════════════════════════════════════════════

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

backup() {
    log "Creating backup..."
    tar -czf "$BACKUP_DIR/backup-$(date +%Y%m%d_%H%M%S).tar.gz" "$DEPLOY_DIR"
}

deploy() {
    log "Starting deployment..."
    cd "$DEPLOY_DIR"
    git pull origin "$GIT_BRANCH"
    npm install
    npm run build
    log "Deployment completed"
}

# ═══════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════

log "Deployment started for $APP_NAME"
backup
deploy
log "All done!"
EOF
```

---

### ÉTAPE 2 : Commande `cat` - Affichage simple

**Syntaxe de base :**

```
cat [OPTIONS] [FICHIERS...]
```

**`cat` = conCATenate (concaténer)**

---

**Test 1 : Afficher un fichier**

```bash
cd ~/text-display-exercise

# Afficher le fichier de config
cat config/database.conf
```

**Résultat :**
```
# Database Configuration
# Last updated: 2024-12-30

[database]
host = localhost
...
```

**`cat` affiche TOUT le contenu du fichier.**

---

**Test 2 : Afficher plusieurs fichiers**

```bash
# Afficher deux fichiers à la suite
cat logs/application.log config/database.conf
```

**`cat` concatène (met bout à bout) les fichiers.**

---

**Test 3 : Numéroter les lignes avec `-n`**

```bash
cat -n config/database.conf
```

**Résultat :**
```
     1	# Database Configuration
     2	# Last updated: 2024-12-30
     3	
     4	[database]
     5	host = localhost
     6	port = 5432
...
```

**`-n` = Number (numéroter toutes les lignes)**

---

**Test 4 : Numéroter seulement les lignes non-vides avec `-b`**

```bash
cat -b config/database.conf
```

**Résultat :**
```
     1	# Database Configuration
     2	# Last updated: 2024-12-30

     3	[database]
     4	host = localhost
...
```

**Les lignes vides ne sont pas numérotées.**

---

**Test 5 : Afficher les caractères non-imprimables avec `-A`**

```bash
# Créer un fichier avec tabulations et fins de lignes
echo -e "Hello\tWorld\n\nTest" > test-special.txt

# Afficher avec -A
cat -A test-special.txt
```

**Résultat :**
```
Hello^IWorld$
$
Test$
```

**Explication :**
- `^I` = Tabulation (TAB)
- `$` = Fin de ligne
- `^M` = Retour chariot (Windows)

**Très utile pour déboguer les problèmes de formatage !**

---

**Test 6 : Supprimer les lignes vides successives avec `-s`**

```bash
# Créer un fichier avec beaucoup de lignes vides
cat > test-blank.txt << 'EOF'
Ligne 1


Ligne 2



Ligne 3
EOF

# Sans -s
cat test-blank.txt

# Avec -s (squeeze)
cat -s test-blank.txt
```

**Résultat avec `-s` :**
```
Ligne 1

Ligne 2

Ligne 3
```

**Les lignes vides multiples sont réduites à une seule.**

---

**Options principales de `cat` :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-n` | Numéroter toutes les lignes | `cat -n file.txt` |
| `-b` | Numéroter lignes non-vides | `cat -b file.txt` |
| `-s` | Supprimer lignes vides multiples | `cat -s file.txt` |
| `-A` | Afficher tous les caractères | `cat -A file.txt` |
| `-E` | Afficher $ en fin de ligne | `cat -E file.txt` |
| `-T` | Afficher ^I pour TAB | `cat -T file.txt` |
| `-v` | Afficher caractères non-imprimables | `cat -v file.txt` |

---

**Cas d'usage professionnels :**

**1. Combiner plusieurs fichiers de config :**

```bash
# Créer une config globale
cat config/*.conf > config/global.conf
```

---

**2. Ajouter à un fichier existant :**

```bash
# Ajouter des logs à la fin
cat new-logs.txt >> logs/application.log
```

---

**3. Créer un fichier avec heredoc :**

```bash
cat > new-config.txt << 'EOF'
Setting1=value1
Setting2=value2
EOF
```

---

### ÉTAPE 3 : Commandes `head` et `tail` - Début et fin de fichier

**`head` = Afficher le DÉBUT d'un fichier**

**Syntaxe :**

```
head [OPTIONS] [FICHIER]
```

---

**Test 1 : Afficher les 10 premières lignes (par défaut)**

```bash
head logs/large.log
```

**Affiche les 10 premières lignes.**

---

**Test 2 : Afficher N lignes avec `-n`**

```bash
# Afficher les 5 premières lignes
head -n 5 logs/large.log

# Forme courte (sans le "n")
head -5 logs/large.log
```

---

**Test 3 : Afficher les N premiers octets avec `-c`**

```bash
# Afficher les 100 premiers octets
head -c 100 logs/large.log
```

---

**Test 4 : Afficher plusieurs fichiers**

```bash
head -n 3 logs/*.log
```

**Résultat :**
```
==> logs/application.log <==
2024-12-30 08:00:01 [INFO] Application started
2024-12-30 08:00:02 [INFO] Database connection established
2024-12-30 08:00:03 [INFO] Loading configuration

==> logs/large.log <==
2024-12-30 08:00:01 [INFO] Log entry number 1
2024-12-30 08:00:02 [DEBUG] Log entry number 2
2024-12-30 08:00:03 [INFO] Log entry number 3
```

**head affiche un en-tête pour chaque fichier.**

---

**`tail` = Afficher la FIN d'un fichier**

**Syntaxe :**

```
tail [OPTIONS] [FICHIER]
```

---

**Test 1 : Afficher les 10 dernières lignes**

```bash
tail logs/large.log
```

---

**Test 2 : Afficher N dernières lignes**

```bash
# Les 20 dernières lignes
tail -n 20 logs/large.log

# Forme courte
tail -20 logs/large.log
```

---

**Test 3 : Afficher à partir de la ligne N avec `+N`**

```bash
# Afficher à partir de la ligne 50 jusqu'à la fin
tail -n +50 logs/large.log
```

**`+50` = Commencer à la ligne 50 et afficher jusqu'à la fin**

---

**Test 4 : SUIVRE un fichier en temps réel avec `-f`**

```bash
# Suivre le fichier en temps réel
tail -f logs/application.log
```

**`-f` = Follow (suivre)**

**La commande reste active et affiche les nouvelles lignes en temps réel !**

**Pour tester :**

```bash
# Terminal 1 : Suivre le fichier
tail -f logs/application.log

# Terminal 2 : Ajouter des lignes
echo "$(date +"%Y-%m-%d %H:%M:%S") [INFO] New log entry" >> logs/application.log
```

**Terminal 1 affichera immédiatement la nouvelle ligne !**

**Arrêter `tail -f` : `Ctrl+C`**

---

**Test 5 : Suivre avec retry (fichier qui n'existe pas encore)**

```bash
# Suivre un fichier même s'il n'existe pas encore
tail -f --retry logs/future.log

# Dans un autre terminal, créer le fichier
echo "Hello" > logs/future.log
# tail -f le détectera et commencera à afficher !
```

---

**Test 6 : Suivre plusieurs fichiers**

```bash
# Suivre deux fichiers en même temps
tail -f logs/application.log logs/large.log
```

**Affiche les nouvelles lignes des deux fichiers avec un en-tête.**

---

**Options principales de `head` et `tail` :**

| Option | Description | Commande | Exemple |
|--------|-------------|----------|---------|
| `-n N` | N lignes | `head`/`tail` | `head -n 20 file.txt` |
| `-c N` | N octets | `head`/`tail` | `tail -c 500 file.txt` |
| `-n +N` | À partir de ligne N | `tail` | `tail -n +50 file.txt` |
| `-f` | Suivre en temps réel | `tail` | `tail -f log.txt` |
| `--retry` | Retry si fichier inexistant | `tail` | `tail -f --retry log.txt` |
| `-q` | Quiet (pas d'en-têtes) | `head`/`tail` | `tail -q -n 5 *.log` |

---

**Cas d'usage professionnels :**

**1. Voir les dernières erreurs :**

```bash
tail -n 100 logs/application.log | grep ERROR
```

---

**2. Surveiller les logs en temps réel :**

```bash
tail -f /var/log/nginx/access.log
```

---

**3. Extraire les lignes 50 à 100 :**

```bash
# head -100 prend les 100 premières
# tail -50 prend les 50 dernières de ces 100
# Résultat : lignes 51 à 100
head -100 file.txt | tail -50
```

---

**4. Voir les 10 premières commandes de l'historique :**

```bash
head -10 ~/.bash_history
```

---

### ÉTAPE 4 : Commande `less` - Navigateur de fichier

**`less` = Lecteur interactif de fichiers**

**Plus puissant que `cat` pour les gros fichiers !**

**Syntaxe :**

```
less [OPTIONS] [FICHIER]
```

---

**Test 1 : Ouvrir un fichier**

```bash
less logs/large.log
```

**Résultat : Interface interactive**

**Navigation dans `less` :**

| Touche | Action |
|--------|--------|
| `Espace` ou `PageDown` | Page suivante |
| `b` ou `PageUp` | Page précédente |
| `v` ou `Enter` | Ligne suivante |
| `^` | Ligne précédente |
| `g` ou `Home` | Début du fichier |
| `G` ou `End` | Fin du fichier |
| `/pattern` | Rechercher "pattern" |
| `n` | Résultat suivant |
| `N` | Résultat précédent |
| `q` | Quitter |
| `h` | Aide |

---

**Test 2 : Rechercher dans un fichier**

```bash
less logs/application.log

# Dans less :
# Taper : /ERROR
# Appuyer sur Enter

# Les mots ERROR sont surlignés
# Appuyer sur "n" pour aller au suivant
```

---

**Test 3 : Naviguer vers une ligne spécifique**

```bash
less logs/large.log

# Dans less :
# Taper : 500g
# Va directement à la ligne 500
```

---

**Test 4 : Afficher les numéros de ligne**

```bash
# Lancer less avec numéros de ligne
less -N logs/large.log

# Ou dans less :
# Taper : -N (toggle les numéros)
```

---

**Test 5 : Suivre un fichier (comme tail -f)**

```bash
# Ouvrir en mode "follow"
less +F logs/application.log

# Équivalent à tail -f mais dans less !
# Ctrl+C pour arrêter le suivi
# Shift+F pour reprendre le suivi
```

---

**Test 6 : Ouvrir plusieurs fichiers**

```bash
less logs/*.log

# Navigation entre fichiers :
# :n - Fichier suivant
# :p - Fichier précédent
# :e nom_fichier - Ouvrir un autre fichier
```

---

**Options principales de `less` :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-N` | Afficher numéros de ligne | `less -N file.txt` |
| `-S` | Ne pas wrapper les lignes longues | `less -S file.txt` |
| `-i` | Recherche insensible à la casse | `less -i file.txt` |
| `-I` | Comme -i mais smart | `less -I file.txt` |
| `+F` | Mode follow (tail -f) | `less +F log.txt` |
| `+G` | Aller à la fin | `less +G file.txt` |
| `-X` | Ne pas effacer l'écran en quittant | `less -X file.txt` |

---

**Commandes avancées dans `less` :**

| Commande | Action |
|----------|--------|
| `&pattern` | Afficher seulement les lignes contenant pattern |
| `!command` | Exécuter une commande shell |
| `v` | Ouvrir dans l'éditeur ($EDITOR) |
| `m + lettre` | Marquer une position |
| `' + lettre` | Revenir à une marque |
| `=` | Afficher infos (position, %) |

---

**Test 7 : Filtrer les lignes affichées**

```bash
less logs/application.log

# Dans less :
# Taper : &ERROR
# Affiche seulement les lignes contenant ERROR
```

---

**Test 8 : Comparer avec `more` (ancien, moins puissant)**

```bash
more logs/large.log
```

**`more` est un ancien lecteur, moins puissant que `less`.**

**Différences :**

| Fonctionnalité | `less` | `more` |
|----------------|--------|--------|
| **Retour en arrière** | [OK] Oui | [X] Non |
| **Recherche** | [OK] Avancée | [JAUNE] Basique |
| **Édition** | [OK] Oui (v) | [X] Non |
| **Rapidité** | [RAPIDE] Rapide | [LENT] Plus lent |
| **Fichiers multiples** | [OK] Oui | [JAUNE] Limité |

**Proverbe Unix : "less is more than more"** [SMILING_FACE_WITH_OPEN_MOUTH_AND_SMILING_EYES]

---

### ÉTAPE 5 : Commande `wc` - Compter (Word Count)

**`wc` = Word Count (compter mots, lignes, caractères)**

**Syntaxe :**

```
wc [OPTIONS] [FICHIERS...]
```

---

**Test 1 : Statistiques complètes**

```bash
wc logs/application.log
```

**Résultat :**
```
  21  147  985 logs/application.log
  ^   ^   ^   ^
lignes mots octets nom
```

---

**Test 2 : Compter seulement les lignes avec `-l`**

```bash
wc -l logs/application.log
# 21 logs/application.log

# Sans le nom du fichier
wc -l < logs/application.log
# 21
```

**`-l` = Lines (lignes)**

---

**Test 3 : Compter les mots avec `-w`**

```bash
wc -w logs/application.log
# 147 logs/application.log
```

**`-w` = Words (mots)**

---

**Test 4 : Compter les caractères avec `-c` ou `-m`**

```bash
# Octets
wc -c logs/application.log
# 985 logs/application.log

# Caractères (important pour UTF-8)
wc -m logs/application.log
# 985 logs/application.log
```

**`-c` = Bytes (octets)**  
**`-m` = Characters (caractères, gère UTF-8)**

---

**Test 5 : Compter plusieurs fichiers**

```bash
wc -l logs/*.log
```

**Résultat :**
```
   21 logs/application.log
 1000 logs/large.log
 1021 total
```

**`wc` additionne les totaux !**

---

**Test 6 : Compter uniquement le total**

```bash
# Compter toutes les lignes de tous les logs
cat logs/*.log | wc -l
```

---

**Options principales de `wc` :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-l` | Lignes | `wc -l file.txt` |
| `-w` | Mots | `wc -w file.txt` |
| `-c` | Octets | `wc -c file.txt` |
| `-m` | Caractères | `wc -m file.txt` |
| `-L` | Longueur ligne la plus longue | `wc -L file.txt` |

---

**Cas d'usage professionnels :**

**1. Compter les erreurs dans un log :**

```bash
grep ERROR logs/application.log | wc -l
```

---

**2. Vérifier combien de lignes ont été ajoutées :**

```bash
# Avant modification
wc -l file.txt > before.txt

# Après modification
wc -l file.txt > after.txt

# Comparer
diff before.txt after.txt
```

---

**3. Trouver le fichier avec le plus de lignes :**

```bash
wc -l logs/*.log | sort -n | tail -1
```

---

**4. Compter les utilisateurs uniques dans un log :**

```bash
cut -d' ' -f4 logs/application.log | sort -u | wc -l
```

---

### ÉTAPE 6 : Commande `nl` - Numérotation avancée

**`nl` = Number Lines (numéroter les lignes)**

**Plus puissant que `cat -n` !**

**Syntaxe :**

```
nl [OPTIONS] [FICHIER]
```

---

**Test 1 : Numérotation basique**

```bash
nl config/database.conf
```

**Résultat :**
```
     1	# Database Configuration
     2	# Last updated: 2024-12-30
     
     3	[database]
     4	host = localhost
...
```

**Par défaut, `nl` ne numérote PAS les lignes vides.**

---

**Test 2 : Numéroter TOUTES les lignes avec `-ba`**

```bash
nl -ba config/database.conf
```

**`-ba` = Body, All (toutes les lignes du corps)**

---

**Test 3 : Changer le format des numéros**

```bash
# Aligner à droite avec 3 chiffres
nl -w 3 -nrz config/database.conf
```

**Options de format :**
- `-w N` : Largeur des numéros
- `-n FORMAT` : Format
  - `ln` : Aligné à gauche
  - `rn` : Aligné à droite
  - `rz` : Aligné à droite avec zéros

---

**Test 4 : Changer le séparateur**

```bash
# Séparateur personnalisé
nl -s ': ' config/database.conf
```

**Résultat :**
```
1: # Database Configuration
2: # Last updated: 2024-12-30
```

**`-s` = Separator (séparateur entre numéro et texte)**

---

**Test 5 : Commencer à un numéro spécifique**

```bash
# Commencer à 100
nl -v 100 config/database.conf
```

**`-v N` = Starting value (valeur de départ)**

---

**Test 6 : Incrément personnalisé**

```bash
# Incrémenter de 10 en 10
nl -i 10 config/database.conf
```

**Résultat :**
```
     1	# Database Configuration
    11	# Last updated: 2024-12-30
    
    21	[database]
    31	host = localhost
```

**`-i N` = Increment (incrément)**

---

### ÉTAPE 7 : Commande `tac` - Inverser l'ordre

**`tac` = cat à l'envers !**

**Affiche un fichier de la fin vers le début.**

```bash
tac logs/application.log
```

**Résultat :**
```
2024-12-30 08:40:05 [INFO] Application stopped
2024-12-30 08:40:03 [ERROR] Application shutting down
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect
...
2024-12-30 08:00:01 [INFO] Application started
```

**Dernière ligne en premier !**

---

**Cas d'usage :**

**Voir les logs les plus récents en premier :**

```bash
tac logs/application.log | less
```

---

### ÉTAPE 8 : Cas d'usage avancés et combinaisons

**Scénario 1 : Extraire une plage de lignes**

```bash
# Lignes 50 à 100
sed -n '50,100p' logs/large.log

# Ou avec head/tail
head -100 logs/large.log | tail -51
```

---

**Scénario 2 : Afficher un fichier avec pagination**

```bash
# Afficher par pages de 20 lignes
cat logs/large.log | less -p 20
```

---

**Scénario 3 : Comparer le début et la fin**

```bash
# Voir le début et la fin côte à côte
echo "=== DÉBUT ===" && head -5 logs/large.log
echo "=== FIN ===" && tail -5 logs/large.log
```

---

**Scénario 4 : Surveiller plusieurs logs en temps réel**

```bash
# Avec multitail (à installer)
sudo apt install multitail
multitail logs/application.log logs/large.log

# Ou avec tail natif
tail -f logs/*.log
```

---

**Scénario 5 : Créer un fichier de log avec horodatage**

```bash
# Script qui log avec timestamp
log_with_timestamp() {
    while IFS= read -r line; do
        echo "$(date '+%Y-%m-%d %H:%M:%S') $line"
    done
}

# Utilisation
echo "Test message" | log_with_timestamp >> logs/timestamped.log
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : Affichage correct**

```bash
# Vérifier que cat affiche tout
LINES_CAT=$(cat logs/application.log | wc -l)
LINES_WC=$(wc -l < logs/application.log)

if [ "$LINES_CAT" -eq "$LINES_WC" ]; then
    echo "[OK] cat affiche toutes les lignes"
else
    echo "[X] Problème avec cat"
fi
```

---

**Test 2 : head et tail cohérents**

```bash
# Les 10 premières + les 10 dernières = toutes si ≤20 lignes
HEAD_COUNT=$(head logs/application.log | wc -l)
TAIL_COUNT=$(tail logs/application.log | wc -l)

echo "Head: $HEAD_COUNT, Tail: $TAIL_COUNT"
```

---

**Test 3 : less fonctionne**

```bash
# Tester less avec une commande automatique
echo "g
q" | less logs/application.log
# Si pas d'erreur, less fonctionne
```

---

**Test 4 : wc compte correctement**

```bash
# Créer un fichier test
echo -e "Line1\nLine2\nLine3" > test-wc.txt

# Compter
COUNT=$(wc -l < test-wc.txt)

if [ "$COUNT" -eq 3 ]; then
    echo "[OK] wc compte correctement"
else
    echo "[X] wc a compté $COUNT au lieu de 3"
fi
```

---

**Test 5 : tail -f fonctionne**

```bash
# Lancer tail -f en background
tail -f logs/test-follow.log &
TAIL_PID=$!

# Écrire dans le fichier
echo "Test line" >> logs/test-follow.log

sleep 1

# Tuer tail
kill $TAIL_PID

echo "[OK] tail -f fonctionne"
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : cat affiche trop d'infos (fichier trop gros)

**Symptôme :**
```bash
cat logs/large.log
# Défile trop vite, impossible de lire
```

**Solution :**
```bash
# Utiliser less pour naviguer
less logs/large.log

# Ou afficher seulement le début
head logs/large.log
```

---

#### Erreur 2 : tail -f ne se met pas à jour

**Symptôme :**
```bash
tail -f logs/app.log
# Pas de nouvelles lignes affichées
```

**Causes possibles :**
1. Le fichier n'est pas modifié
2. Le processus écrit dans un autre fichier
3. Problème de bufferisation

**Solution :**
```bash
# Vérifier que le fichier change
stat logs/app.log

# Utiliser --retry si fichier recréé
tail -f --retry logs/app.log

# Forcer l'affichage avec -s 0.1
tail -f -s 0.1 logs/app.log
```

---

#### Erreur 3 : wc compte mal les lignes

**Symptôme :**
```bash
wc -l file.txt
# 9 file.txt
# Mais le fichier a 10 lignes visuellement
```

**Cause :** La dernière ligne n'a pas de `\n` (nouvelle ligne).

**Solution :**
```bash
# Vérifier avec cat -A
cat -A file.txt
# Si la dernière ligne n'a pas de $, c'est le problème

# Ajouter un \n à la fin
echo "" >> file.txt
```

---

#### Erreur 4 : less bloque le terminal

**Symptôme :**
```bash
# Après avoir quitté less, le terminal est bizarre
```

**Solution :**
```bash
# Réinitialiser le terminal
reset

# Ou utiliser less avec -X
less -X file.txt
```

---

#### Erreur 5 : Fichier binaire affiché avec cat

**Symptôme :**
```bash
cat image.png
# Caractères bizarres, terminal cassé
```

**Solution :**
```bash
# Vérifier le type avant
file image.png

# Si binaire, ne pas utiliser cat
# Utiliser hexdump ou xxd
hexdump -C image.png | less
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Commandes d'affichage**
- `cat` : Affichage complet (petits fichiers)
- `less` : Navigation interactive (gros fichiers)
- `head` : Début du fichier
- `tail` : Fin du fichier
- `tail -f` : Suivi en temps réel

**2. Comptage**
- `wc -l` : Nombre de lignes
- `wc -w` : Nombre de mots
- `wc -c` : Nombre d'octets

**3. Numérotation**
- `cat -n` : Numérotation simple
- `nl` : Numérotation avancée

**4. Ordre**
- `tac` : Inverser l'ordre des lignes

**5. Bonnes pratiques**
- Utiliser `less` pour les gros fichiers
- `tail -f` pour surveiller les logs
- Combiner avec pipes pour plus de puissance
- Toujours vérifier le type de fichier avant affichage

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Coloration syntaxique avec `bat`**

```bash
# Installer bat (meilleur que cat)
sudo apt install bat

# Utiliser
bat config/database.conf
# Coloration syntaxique automatique !
```

---

**2. Surveillance avancée avec `watch`**

```bash
# Rafraîchir toutes les 2 secondes
watch -n 2 'tail -5 logs/application.log'
```

---

**3. Affichage hexadécimal**

```bash
# Afficher en hexadécimal
hexdump -C file.bin

# Ou avec xxd
xxd file.bin
```

---

**4. Pagination avancée avec `most`**

```bash
# Installer most
sudo apt install most

# Utiliser (plus de couleurs que less)
most logs/large.log
```

---

**5. Logs en couleur avec `ccze`**

```bash
# Installer ccze
sudo apt install ccze

# Coloriser les logs
tail -f logs/application.log | ccze -A
```

---

## [COURS] CONCLUSION DE L'EXERCICE 2.1

**[OK] Félicitations ! Tu maîtrises maintenant l'affichage de fichiers !**

**Ce que tu as appris :**
- Afficher des fichiers avec `cat`
- Naviguer avec `less`
- Voir début/fin avec `head`/`tail`
- Suivre en temps réel avec `tail -f`
- Compter avec `wc`
- Numéroter avec `nl`
- Inverser avec `tac`

**Compétences acquises :**
- [OK] Affichage de fichiers (niveau intermédiaire)
- [OK] Navigation dans les fichiers
- [OK] Surveillance de logs
- [OK] Comptage et statistiques

**Temps moyen de réalisation :** 45 min - 1h

**Prochaine étape :** Exercice 2.2 - Recherche et filtrage avec `grep` ! [RECHERCHE]

---

Je continue avec l'exercice 2.2 ?


# [JAUNE] EXERCICE 2.2 : RECHERCHE ET FILTRAGE AVEC `grep` (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es DevOps/SRE dans une entreprise et tu dois analyser quotidiennement :
- Des logs d'applications (millions de lignes)
- Des fichiers de configuration
- Du code source
- Des résultats de tests
- Des rapports de monitoring

**Problématique :** Trouver rapidement des informations spécifiques dans des milliers de fichiers sans les ouvrir un par un.

### Cahier des charges

- Rechercher un mot/pattern dans un fichier
- Rechercher dans plusieurs fichiers/dossiers
- Filtrer les logs par niveau (ERROR, WARNING, INFO)
- Trouver des patterns complexes (IP, emails, URLs)
- Compter les occurrences
- Extraire du contexte (lignes avant/après)
- Recherches insensibles à la casse
- Utiliser des expressions régulières
- Inverser les recherches
- Colorer les résultats

### Contraintes

- Commande principale : `grep`
- Variantes : `egrep`, `fgrep`, `zgrep`
- Expressions régulières (basiques et étendues)
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Rechercher un pattern simple
- [OK] Utiliser les expressions régulières
- [OK] Rechercher récursivement dans des dossiers
- [OK] Afficher le contexte (lignes avant/après)
- [OK] Compter les occurrences
- [OK] Inverser une recherche
- [OK] Rechercher dans des fichiers compressés
- [OK] Combiner plusieurs patterns
- [OK] Utiliser grep pour le monitoring
- [OK] Extraire des données structurées

---

## [DOCS] PRÉREQUIS

- Exercice 2.1 terminé (Affichage de fichiers)
- Notions de base sur les expressions régulières (on va approfondir)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer des fichiers de test réalistes

```bash
# Créer le dossier de travail
mkdir -p ~/grep-exercise
cd ~/grep-exercise

# Créer des sous-dossiers
mkdir -p {logs,config,code,data,archives}
```

---

**Créer un fichier de log complexe :**

```bash
cat > logs/app.log << 'EOF'
2024-12-30 08:00:01 [INFO] Application started on port 8080
2024-12-30 08:00:02 [INFO] Database connection: postgresql://db.company.com:5432
2024-12-30 08:00:03 [INFO] Loading config from /etc/app/config.yml
2024-12-30 08:05:15 [INFO] User john.doe@company.com logged in from 192.168.1.100
2024-12-30 08:10:23 [WARNING] Slow query detected (2.3s): SELECT * FROM users WHERE status='active'
2024-12-30 08:10:24 [INFO] Cache hit rate: 85%
2024-12-30 08:15:30 [ERROR] Failed to connect to payment gateway at https://api.payment.com
2024-12-30 08:15:31 [ERROR] Connection timeout after 30s
2024-12-30 08:15:32 [INFO] Retry attempt 1/3
2024-12-30 08:15:35 [INFO] Payment gateway connection restored
2024-12-30 08:20:10 [INFO] Processing order #12345 for customer jane.smith@company.com
2024-12-30 08:20:11 [INFO] Payment of $299.99 processed successfully
2024-12-30 08:20:12 [INFO] Order #12345 completed
2024-12-30 08:25:45 [WARNING] Memory usage: 85% (3.4GB / 4GB)
2024-12-30 08:30:00 [INFO] Starting hourly backup
2024-12-30 08:32:15 [INFO] Backup completed: /backups/2024-12-30_0830.tar.gz (2.4GB)
2024-12-30 08:35:20 [INFO] User admin@company.com logged in from 10.0.0.50
2024-12-30 08:40:01 [ERROR] Database connection lost: Connection refused
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect to database after 3 attempts
2024-12-30 08:40:03 [ERROR] Application shutting down due to critical error
2024-12-30 08:40:04 [INFO] Cleanup completed
2024-12-30 08:40:05 [INFO] Application stopped
EOF
```

---

**Créer un fichier de log web (access.log) :**

```bash
cat > logs/access.log << 'EOF'
192.168.1.100 - - [30/Dec/2024:08:00:15] "GET /index.html HTTP/1.1" 200 1234
192.168.1.101 - - [30/Dec/2024:08:01:23] "POST /api/login HTTP/1.1" 200 89
10.0.0.50 - - [30/Dec/2024:08:02:45] "GET /admin/dashboard HTTP/1.1" 200 5678
192.168.1.100 - - [30/Dec/2024:08:03:12] "GET /api/users HTTP/1.1" 200 2345
192.168.1.102 - - [30/Dec/2024:08:04:56] "POST /api/orders HTTP/1.1" 201 456
192.168.1.100 - - [30/Dec/2024:08:05:34] "GET /products.html HTTP/1.1" 200 8901
10.0.0.51 - - [30/Dec/2024:08:06:12] "GET /api/reports HTTP/1.1" 403 78
192.168.1.103 - - [30/Dec/2024:08:07:45] "GET /contact.html HTTP/1.1" 200 890
192.168.1.100 - - [30/Dec/2024:08:08:23] "POST /api/feedback HTTP/1.1" 500 123
10.0.0.50 - - [30/Dec/2024:08:09:01] "GET /admin/users HTTP/1.1" 200 3456
192.168.1.104 - - [30/Dec/2024:08:10:15] "GET /about.html HTTP/1.1" 404 234
192.168.1.100 - - [30/Dec/2024:08:11:34] "GET /api/products HTTP/1.1" 200 6789
EOF
```

---

**Créer des fichiers de configuration :**

```bash
cat > config/database.yml << 'EOF'
# Database Configuration
production:
  host: db.company.com
  port: 5432
  database: prod_db
  username: prod_user
  password: ${DB_PASSWORD}
  pool: 25
  timeout: 5000

staging:
  host: db-staging.company.com
  port: 5432
  database: staging_db
  username: staging_user
  password: ${DB_PASSWORD}
  pool: 10
  timeout: 3000

development:
  host: localhost
  port: 5432
  database: dev_db
  username: dev_user
  password: dev123
  pool: 5
  timeout: 1000
EOF
```

```bash
cat > config/servers.conf << 'EOF'
# Server Configuration
[web]
server1.company.com:8080
server2.company.com:8080
server3.company.com:8080

[api]
api1.company.com:9090
api2.company.com:9090

[database]
db-master.company.com:5432
db-replica1.company.com:5432
db-replica2.company.com:5432

[cache]
redis1.company.com:6379
redis2.company.com:6379
EOF
```

---

**Créer des fichiers de code :**

```bash
cat > code/app.py << 'EOF'
#!/usr/bin/env python3
"""
Main application module
"""
import os
import sys
from datetime import datetime

# Configuration
DEBUG = True
PORT = 8080
HOST = '0.0.0.0'

def main():
    """Main entry point"""
    print("Starting application...")
    
    # TODO: Add error handling
    try:
        start_server(HOST, PORT)
    except Exception as e:
        print(f"ERROR: {e}")
        sys.exit(1)
    
    print("Application started successfully")

def start_server(host, port):
    """Start the web server"""
    # FIXME: Implement proper server
    print(f"Server running on {host}:{port}")
    
    # BUG: Memory leak in this function
    while True:
        pass

if __name__ == "__main__":
    main()
EOF
```

```bash
cat > code/utils.js << 'EOF'
// Utility functions
const crypto = require('crypto');

// TODO: Refactor this function
function hashPassword(password) {
    // FIXME: Use bcrypt instead
    return crypto.createHash('md5').update(password).digest('hex');
}

function validateEmail(email) {
    const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    return regex.test(email);
}

// BUG: This function fails for edge cases
function parseDate(dateString) {
    return new Date(dateString);
}

module.exports = {
    hashPassword,
    validateEmail,
    parseDate
};
EOF
```

---

**Créer un fichier CSV :**

```bash
cat > data/users.csv << 'EOF'
id,name,email,phone,country,status
1,John Doe,john.doe@company.com,+1-555-0101,USA,active
2,Jane Smith,jane.smith@company.com,+1-555-0102,USA,active
3,Bob Johnson,bob.johnson@example.com,+44-20-1234-5678,UK,inactive
4,Alice Williams,alice.williams@company.com,+1-555-0103,USA,active
5,Charlie Brown,charlie.brown@example.com,+33-1-23-45-67-89,France,active
6,Diana Prince,diana.prince@company.com,+1-555-0104,USA,suspended
7,Eve Davis,eve.davis@example.com,+49-30-12345678,Germany,active
8,Frank Miller,frank.miller@company.com,+1-555-0105,USA,active
9,Grace Lee,grace.lee@example.com,+81-3-1234-5678,Japan,active
10,Henry Wilson,henry.wilson@company.com,+1-555-0106,USA,inactive
EOF
```

---

**Créer un fichier compressé :**

```bash
# Créer un log et le compresser
echo "2024-12-30 [INFO] This is a compressed log" > logs/old.log
echo "2024-12-30 [ERROR] Error in compressed file" >> logs/old.log
gzip logs/old.log
# Créé : logs/old.log.gz
```

---

### ÉTAPE 2 : Commande `grep` - Recherche basique

**Syntaxe de base :**

```
grep [OPTIONS] PATTERN [FICHIERS...]
```

**`grep` = Global Regular Expression Print**

---

**Test 1 : Recherche simple**

```bash
cd ~/grep-exercise

# Rechercher le mot "ERROR" dans app.log
grep ERROR logs/app.log
```

**Résultat :**
```
2024-12-30 08:15:30 [ERROR] Failed to connect to payment gateway
2024-12-30 08:15:31 [ERROR] Connection timeout after 30s
2024-12-30 08:40:01 [ERROR] Database connection lost
2024-12-30 08:40:03 [ERROR] Application shutting down
```

**grep affiche toutes les lignes contenant "ERROR".**

---

**Test 2 : Recherche insensible à la casse avec `-i`**

```bash
# Rechercher "error" (minuscule ou majuscule)
grep -i error logs/app.log
```

**Trouve "ERROR", "Error", "error", etc.**

**`-i` = Ignore case (ignorer la casse)**

---

**Test 3 : Afficher les numéros de ligne avec `-n`**

```bash
grep -n ERROR logs/app.log
```

**Résultat :**
```
7:2024-12-30 08:15:30 [ERROR] Failed to connect to payment gateway
8:2024-12-30 08:15:31 [ERROR] Connection timeout after 30s
18:2024-12-30 08:40:01 [ERROR] Database connection lost
20:2024-12-30 08:40:03 [ERROR] Application shutting down
```

**`-n` = Line number (numéro de ligne)**

**Très utile pour localiser précisément !**

---

**Test 4 : Compter les occurrences avec `-c`**

```bash
# Compter combien de fois "ERROR" apparaît
grep -c ERROR logs/app.log
# 4
```

**`-c` = Count (compter les lignes correspondantes)**

**[ATTENTION] Attention : `-c` compte les LIGNES, pas les occurrences du mot !**

```bash
# Si une ligne contient 2 fois "ERROR", compte quand même 1
```

---

**Test 5 : Afficher seulement les noms de fichiers avec `-l`**

```bash
# Quels fichiers contiennent "ERROR" ?
grep -l ERROR logs/*
```

**Résultat :**
```
logs/app.log
```

**`-l` = Files with matches (fichiers correspondants)**

---

**Test 6 : Afficher les fichiers qui NE contiennent PAS le pattern avec `-L`**

```bash
# Quels fichiers ne contiennent pas "ERROR" ?
grep -L ERROR logs/*
```

**Résultat :**
```
logs/access.log
```

**`-L` = Files without matches**

---

**Test 7 : Inverser la recherche avec `-v`**

```bash
# Afficher toutes les lignes SAUF celles avec "INFO"
grep -v INFO logs/app.log
```

**Résultat :**
```
2024-12-30 08:10:23 [WARNING] Slow query detected
2024-12-30 08:15:30 [ERROR] Failed to connect to payment gateway
2024-12-30 08:15:31 [ERROR] Connection timeout after 30s
2024-12-30 08:25:45 [WARNING] Memory usage: 85%
2024-12-30 08:40:01 [ERROR] Database connection lost
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect to database
2024-12-30 08:40:03 [ERROR] Application shutting down
```

**`-v` = Invert match (inverser)**

**Très utile pour filtrer ce qu'on ne veut PAS voir !**

---

**Test 8 : Colorer les résultats avec `--color`**

```bash
# Colorer les correspondances
grep --color=auto ERROR logs/app.log

# Ou définir un alias permanent
alias grep='grep --color=auto'
```

**Le mot "ERROR" sera surligné en couleur !**

---

### ÉTAPE 3 : Contexte (lignes avant/après)

**Afficher le CONTEXTE autour des correspondances.**

---

**Test 1 : Afficher N lignes APRÈS avec `-A`**

```bash
# Afficher la ligne + 2 lignes après
grep -A 2 "Failed to connect" logs/app.log
```

**Résultat :**
```
2024-12-30 08:15:30 [ERROR] Failed to connect to payment gateway
2024-12-30 08:15:31 [ERROR] Connection timeout after 30s
2024-12-30 08:15:32 [INFO] Retry attempt 1/3
```

**`-A N` = After (N lignes après)**

---

**Test 2 : Afficher N lignes AVANT avec `-B`**

```bash
# Afficher 2 lignes avant + la ligne
grep -B 2 CRITICAL logs/app.log
```

**Résultat :**
```
2024-12-30 08:40:01 [ERROR] Database connection lost
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect to database
```

**`-B N` = Before (N lignes avant)**

---

**Test 3 : Afficher lignes avant ET après avec `-C`**

```bash
# Afficher 2 lignes avant + la ligne + 2 lignes après
grep -C 2 CRITICAL logs/app.log
```

**Résultat :**
```
2024-12-30 08:35:20 [INFO] User admin@company.com logged in
2024-12-30 08:40:01 [ERROR] Database connection lost
2024-12-30 08:40:02 [CRITICAL] Unable to reconnect to database
2024-12-30 08:40:03 [ERROR] Application shutting down
2024-12-30 08:40:04 [INFO] Cleanup completed
```

**`-C N` = Context (N lignes avant ET après)**

---

**Cas d'usage professionnel :**

**Voir ce qui s'est passé autour d'une erreur critique :**

```bash
grep -C 5 CRITICAL logs/app.log
```

**Donne le contexte complet !**

---

### ÉTAPE 4 : Recherche récursive dans des dossiers

**Rechercher dans TOUS les fichiers d'un dossier.**

---

**Test 1 : Recherche récursive avec `-r`**

```bash
# Rechercher "ERROR" dans tous les fichiers de logs/
grep -r ERROR logs/
```

**Résultat :**
```
logs/app.log:2024-12-30 08:15:30 [ERROR] Failed to connect
logs/app.log:2024-12-30 08:15:31 [ERROR] Connection timeout
...
```

**`-r` = Recursive (récursif)**

---

**Test 2 : Recherche récursive avec `-R` (suit les liens symboliques)**

```bash
# -R suit aussi les liens symboliques
grep -R ERROR logs/
```

**Différence :**
- `-r` : Ne suit PAS les liens symboliques
- `-R` : Suit les liens symboliques

---

**Test 3 : Limiter par extension de fichier**

```bash
# Chercher seulement dans les fichiers .log
grep -r --include="*.log" ERROR .
```

**`--include=PATTERN` : Inclure seulement certains fichiers**

---

**Test 4 : Exclure certains fichiers**

```bash
# Chercher partout SAUF dans les .gz
grep -r --exclude="*.gz" ERROR .
```

**`--exclude=PATTERN` : Exclure certains fichiers**

---

**Test 5 : Exclure des dossiers**

```bash
# Chercher partout SAUF dans node_modules et .git
grep -r --exclude-dir={node_modules,.git} TODO .
```

**`--exclude-dir=DIR` : Exclure des dossiers**

---

**Cas d'usage professionnel :**

**Chercher tous les TODO/FIXME/BUG dans le code :**

```bash
grep -rn --include="*.py" --include="*.js" -E "TODO|FIXME|BUG" code/
```

**Résultat :**
```
code/app.py:11:    # TODO: Add error handling
code/app.py:19:    # FIXME: Implement proper server
code/app.py:23:    # BUG: Memory leak in this function
code/utils.js:4:// TODO: Refactor this function
code/utils.js:6:    // FIXME: Use bcrypt instead
code/utils.js:14:// BUG: This function fails for edge cases
```

**`-E` permet d'utiliser `|` (OU logique)**

---

### ÉTAPE 5 : Expressions régulières

**grep supporte les expressions régulières (regex) !**

---

**Deux modes :**

1. **BRE** (Basic Regular Expressions) - Par défaut
2. **ERE** (Extended Regular Expressions) - Avec `-E`

---

**Métacaractères de base :**

| Caractère | Signification | Exemple |
|-----------|---------------|---------|
| `.` | N'importe quel caractère | `a.c` -> "abc", "a1c" |
| `*` | 0 ou plusieurs fois | `ab*c` -> "ac", "abc", "abbc" |
| `^` | Début de ligne | `^ERROR` -> ligne commence par ERROR |
| `$` | Fin de ligne | `ERROR$` -> ligne finit par ERROR |
| `[...]` | Un caractère parmi | `[aeiou]` -> voyelle |
| `[^...]` | PAS un caractère parmi | `[^0-9]` -> pas un chiffre |

---

**Test 1 : Ligne commençant par un pattern**

```bash
# Lignes commençant par une date
grep "^2024-12-30" logs/app.log
```

**`^` = Début de ligne**

---

**Test 2 : Ligne finissant par un pattern**

```bash
# Lignes finissant par "successfully"
grep "successfully$" logs/app.log
```

**`$` = Fin de ligne**

---

**Test 3 : N'importe quel caractère avec `.`**

```bash
# Rechercher "s.rver" -> "server", "sErver", "s1rver"
grep "s.rver" config/servers.conf
```

**`.` = N'importe quel caractère**

---

**Test 4 : Répétition avec `*`**

```bash
# Rechercher "Erro*r" -> "Eror", "Error", "Errror"
grep "Erro*r" logs/app.log
```

**`*` = 0 ou plusieurs fois le caractère précédent**

---

**Test 5 : Classes de caractères `[...]`**

```bash
# Rechercher les mots commençant par une voyelle
grep "^[aeiouAEIOU]" data/users.csv
```

**`[aeiou]` = Un caractère parmi a, e, i, o, u**

---

**Test 6 : Plages avec `[a-z]`**

```bash
# Trouver les lignes avec des chiffres
grep "[0-9]" logs/app.log

# Trouver les lignes avec des lettres minuscules
grep "[a-z]" logs/app.log

# Trouver les lignes avec des majuscules
grep "[A-Z]" logs/app.log
```

---

**Test 7 : Négation avec `[^...]`**

```bash
# Lignes ne contenant PAS de chiffres
grep "^[^0-9]*$" config/database.yml
```

**`[^0-9]` = PAS un chiffre**

---

**Mode étendu avec `-E` (ou `egrep`) :**

Avec `-E`, on peut utiliser :

| Métacaractère | Signification | Exemple |
|---------------|---------------|---------|
| `+` | 1 ou plusieurs fois | `ab+c` -> "abc", "abbc" (PAS "ac") |
| `?` | 0 ou 1 fois | `ab?c` -> "ac", "abc" (PAS "abbc") |
| `\|` | OU logique | `cat\|dog` -> "cat" ou "dog" |
| `(...)` | Groupement | `(ab)+` -> "ab", "abab" |
| `{n}` | Exactement n fois | `a{3}` -> "aaa" |
| `{n,}` | Au moins n fois | `a{2,}` -> "aa", "aaa", "aaaa" |
| `{n,m}` | Entre n et m fois | `a{2,4}` -> "aa", "aaa", "aaaa" |

---

**Test 8 : OU logique avec `-E`**

```bash
# Chercher ERROR ou WARNING ou CRITICAL
grep -E "ERROR|WARNING|CRITICAL" logs/app.log
```

**`|` = OU logique (avec `-E`)**

---

**Test 9 : Répétition avec `+`**

```bash
# Au moins 1 chiffre
grep -E "[0-9]+" logs/app.log
```

**`+` = 1 ou plusieurs fois (avec `-E`)**

---

**Test 10 : Optionnel avec `?`**

```bash
# "https" ou "http" (le 's' est optionnel)
grep -E "https?" logs/app.log
```

**`?` = 0 ou 1 fois (avec `-E`)**

---

**Test 11 : Groupement avec `(...)`**

```bash
# Trouver des adresses IP (simplifié)
grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" logs/access.log
```

**Explication :**
- `[0-9]{1,3}` : 1 à 3 chiffres
- `\.` : Point littéral (échappé)
- `{3}` : Répété 3 fois
- `[0-9]{1,3}` : 1 à 3 chiffres finaux

**Trouve : "192.168.1.100", "10.0.0.50", etc.**

---

**Test 12 : Adresses email**

```bash
# Trouver des adresses email
grep -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" logs/app.log
```

**Résultat :**
```
john.doe@company.com
jane.smith@company.com
admin@company.com
```

---

**Test 13 : URLs**

```bash
# Trouver des URLs
grep -Eo "https?://[a-zA-Z0-9./?=_-]*" logs/app.log
```

**`-o` = Afficher seulement la partie qui correspond (pas toute la ligne)**

**Résultat :**
```
https://api.payment.com
```

---

### ÉTAPE 6 : Options avancées

**Test 1 : Afficher seulement les correspondances avec `-o`**

```bash
# Extraire seulement les adresses IP
grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" logs/access.log
```

**Résultat :**
```
192.168.1.100
192.168.1.101
10.0.0.50
192.168.1.100
...
```

**`-o` = Only matching (seulement la correspondance)**

---

**Test 2 : Recherche de mots entiers avec `-w`**

```bash
# Chercher le mot "port" (mot entier)
grep -w port config/database.yml
```

**Sans `-w` :**
```bash
grep port config/database.yml
# Trouve aussi "report", "support", etc.
```

**Avec `-w` :**
```bash
grep -w port config/database.yml
# Trouve seulement "port" comme mot séparé
```

**`-w` = Word (mot entier)**

---

**Test 3 : Recherche de ligne exacte avec `-x`**

```bash
# Chercher une ligne exacte
grep -x "DEBUG = True" code/app.py
```

**La ligne ENTIÈRE doit correspondre.**

**`-x` = Exact line (ligne exacte)**

---

**Test 4 : Nombre maximum de correspondances avec `-m`**

```bash
# Afficher seulement les 3 premières correspondances
grep -m 3 INFO logs/app.log
```

**`-m N` = Max count (maximum N correspondances)**

**Utile pour les très gros fichiers !**

---

**Test 5 : Recherche silencieuse avec `-q`**

```bash
# Vérifier si un pattern existe (pour scripts)
if grep -q ERROR logs/app.log; then
    echo "Des erreurs ont été trouvées !"
else
    echo "Aucune erreur"
fi
```

**`-q` = Quiet (silencieux, juste le code de retour)**

**Code de retour :**
- 0 = Trouvé
- 1 = Pas trouvé
- 2 = Erreur

---

**Test 6 : Afficher le nom du fichier avec `-H` (ou cacher avec `-h`)**

```bash
# Forcer l'affichage du nom de fichier
grep -H ERROR logs/app.log

# Cacher le nom de fichier
grep -h ERROR logs/*.log
```

**`-H` = With filename (avec nom)**  
**`-h` = No filename (sans nom)**

---

### ÉTAPE 7 : Recherche dans fichiers compressés

**`zgrep` = grep pour fichiers .gz**

```bash
# Rechercher dans un fichier compressé
zgrep ERROR logs/old.log.gz
```

**Résultat :**
```
2024-12-30 [ERROR] Error in compressed file
```

**Variantes :**
- `zgrep` : Fichiers .gz
- `bzgrep` : Fichiers .bz2
- `xzgrep` : Fichiers .xz

---

### ÉTAPE 8 : Cas d'usage professionnels avancés

**Scénario 1 : Analyser les erreurs par type**

```bash
# Compter chaque type d'erreur
grep -o "\[.*\]" logs/app.log | sort | uniq -c | sort -rn
```

**Résultat :**
```
     14 [INFO]
      4 [ERROR]
      2 [WARNING]
      1 [CRITICAL]
```

**Explication :**
- `grep -o "\[.*\]"` : Extraire seulement [INFO], [ERROR], etc.
- `sort` : Trier
- `uniq -c` : Compter les occurrences uniques
- `sort -rn` : Trier par nombre (décroissant)

---

**Scénario 2 : Extraire les adresses IP uniques**

```bash
# Trouver toutes les IPs uniques
grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" logs/access.log | sort -u
```

**Résultat :**
```
10.0.0.50
10.0.0.51
192.168.1.100
192.168.1.101
192.168.1.102
192.168.1.103
192.168.1.104
```

---

**Scénario 3 : Trouver les requêtes avec erreur HTTP**

```bash
# Codes HTTP 4xx et 5xx (erreurs client et serveur)
grep -E "HTTP/[0-9.]+ [45][0-9]{2}" logs/access.log
```

**Résultat :**
```
10.0.0.51 - - [30/Dec/2024:08:06:12] "GET /api/reports HTTP/1.1" 403 78
192.168.1.100 - - [30/Dec/2024:08:08:23] "POST /api/feedback HTTP/1.1" 500 123
192.168.1.104 - - [30/Dec/2024:08:10:15] "GET /about.html HTTP/1.1" 404 234
```

---

**Scénario 4 : Alertes sur patterns critiques**

```bash
# Script de monitoring
#!/bin/bash

CRITICAL_PATTERNS="CRITICAL|FATAL|PANIC"

if grep -Eq "$CRITICAL_PATTERNS" logs/app.log; then
    echo "[ALERTE] ALERTE: Erreurs critiques détectées !"
    grep -E "$CRITICAL_PATTERNS" logs/app.log
    
    # Envoyer une notification (exemple)
    # mail -s "ALERT" admin@company.com < alert.txt
fi
```

---

**Scénario 5 : Extraire les emails d'un fichier**

```bash
# Extraire tous les emails uniques
grep -Eoh "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" data/users.csv | sort -u
```

---

**Scénario 6 : Trouver les lignes longues (potentiels problèmes)**

```bash
# Lignes de plus de 200 caractères
grep -E "^.{200,}$" logs/app.log
```

---

**Scénario 7 : Recherche multi-patterns avec fichier**

```bash
# Créer un fichier de patterns
cat > patterns.txt << EOF
ERROR
CRITICAL
FATAL
PANIC
EOF

# Rechercher tous ces patterns
grep -f patterns.txt logs/app.log
```

**`-f FILE` = Patterns from file (patterns depuis un fichier)**

---

**Scénario 8 : Statistiques de logs par heure**

```bash
# Compter les logs par heure
grep -o "[0-9]{2}:[0-9]{2}:" logs/app.log | cut -d: -f1 | sort | uniq -c
```

**Résultat :**
```
      4 08
```

---

**Scénario 9 : Trouver les mots de passe en dur (sécurité)**

```bash
# Rechercher des patterns de mots de passe
grep -rn -E "(password|passwd|pwd).*=.*['\"][^'\"]{3,}" config/
```

**Trouve des lignes comme :**
```
config/database.yml:12:  password: dev123
```

---

**Scénario 10 : Valider la syntaxe de fichiers**

```bash
# Trouver les lignes sans ':' dans un fichier de config
grep -v ":" config/servers.conf | grep -v "^#" | grep -v "^$"
```

---

### ÉTAPE 9 : Combinaisons puissantes avec pipes

**Grep est souvent utilisé avec d'autres commandes !**

```bash
# Voir les processus Python
ps aux | grep python

# Trouver les gros fichiers logs
ls -lh /var/log/*.log | grep -E "[0-9]+M"

# Voir l'historique de commandes avec grep
history | grep git

# Surveiller les logs en temps réel avec filtrage
tail -f logs/app.log | grep ERROR

# Compter les connexions par IP
grep "logged in" logs/app.log | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort | uniq -c

# Trouver les fichiers contenant un texte puis les éditer
grep -l "TODO" code/*.py | xargs vim
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : grep trouve les patterns**

```bash
# Test simple
echo "Hello World" | grep "World" && echo "[OK] Test 1 OK"
```

---

**Test 2 : grep compte correctement**

```bash
# Créer un fichier test
echo -e "ERROR\nINFO\nERROR\nWARNING" > test.txt

COUNT=$(grep -c ERROR test.txt)

if [ "$COUNT" -eq 2 ]; then
    echo "[OK] Test 2 OK"
else
    echo "[X] Test 2 FAIL: $COUNT au lieu de 2"
fi

rm test.txt
```

---

**Test 3 : Regex fonctionne**

```bash
# Tester une IP
echo "192.168.1.100" | grep -Eq "([0-9]{1,3}\.){3}[0-9]{1,3}" && echo "[OK] Test 3 OK"
```

---

**Test 4 : Récursif fonctionne**

```bash
# Créer structure test
mkdir -p test/{a,b}
echo "ERROR" > test/a/file1.txt
echo "ERROR" > test/b/file2.txt

COUNT=$(grep -r ERROR test/ | wc -l)

if [ "$COUNT" -eq 2 ]; then
    echo "[OK] Test 4 OK"
else
    echo "[X] Test 4 FAIL"
fi

rm -rf test/
```

---

**Test 5 : zgrep fonctionne**

```bash
# Tester sur fichier compressé
if zgrep -q ERROR logs/old.log.gz; then
    echo "[OK] Test 5 OK"
else
    echo "[X] Test 5 FAIL"
fi
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Regex ne fonctionne pas

**Symptôme :**
```bash
grep "test+" file.txt
# Ne trouve rien, alors qu'il devrait
```

**Cause :** Oubli du `-E` pour regex étendues.

**Solution :**
```bash
grep -E "test+" file.txt
# Ou utiliser egrep
egrep "test+" file.txt
```

---

#### Erreur 2 : Caractères spéciaux non échappés

**Symptôme :**
```bash
grep "192.168.1.100" file.txt
# Trouve "192x168x1x100" aussi !
```

**Cause :** `.` signifie "n'importe quel caractère" en regex.

**Solution :**
```bash
# Échapper les points
grep "192\.168\.1\.100" file.txt
```

---

#### Erreur 3 : grep trouve trop de choses

**Symptôme :**
```bash
grep "port" config.yml
# Trouve aussi "report", "support", etc.
```

**Solution :**
```bash
# Utiliser -w pour mot entier
grep -w "port" config.yml
```

---

#### Erreur 4 : Recherche sensible à la casse par erreur

**Symptôme :**
```bash
grep "error" logs/app.log
# Ne trouve pas "ERROR"
```

**Solution :**
```bash
# Utiliser -i
grep -i "error" logs/app.log
```

---

#### Erreur 5 : Fichier binaire corrompt le terminal

**Symptôme :**
```bash
grep "text" binary-file
# Terminal affiche des caractères bizarres
```

**Solution :**
```bash
# Ignorer les fichiers binaires
grep -I "text" *

# Ou traiter comme texte
grep -a "text" binary-file
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Options essentielles**
- `-i` : Ignorer la casse
- `-n` : Numéros de ligne
- `-r` : Récursif
- `-v` : Inverser
- `-c` : Compter
- `-l` : Nom des fichiers

**2. Contexte**
- `-A N` : N lignes après
- `-B N` : N lignes avant
- `-C N` : N lignes avant ET après

**3. Regex**
- Basiques : `.` `*` `^` `$` `[...]`
- Étendues (avec `-E`) : `+` `?` `|` `{n,m}`

**4. Extraction**
- `-o` : Seulement la correspondance
- Combiné avec `-E` pour extraction de données

**5. Bonnes pratiques**
- Toujours utiliser `--color=auto`
- Utiliser `-w` pour mots entiers
- Utiliser `-q` dans les scripts
- Combiner avec `sort | uniq -c` pour statistiques

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. ripgrep (`rg`) - Alternative moderne**

```bash
# Installer
sudo apt install ripgrep

# Utiliser (beaucoup plus rapide !)
rg ERROR logs/

# Ignore automatiquement .git, node_modules, etc.
```

---

**2. ag (The Silver Searcher)**

```bash
# Installer
sudo apt install silversearcher-ag

# Utiliser
ag ERROR logs/
```

---

**3. Grep avec Perl regex (-P)**

```bash
# Utiliser les regex Perl (plus puissantes)
grep -P "\d{3}-\d{3}-\d{4}" file.txt
# Trouve des numéros de téléphone US
```

---

**4. Création de scripts de monitoring**

```bash
#!/bin/bash
# monitor-errors.sh

LOG_FILE="/var/log/app.log"
THRESHOLD=10

ERROR_COUNT=$(grep -c ERROR "$LOG_FILE")

if [ "$ERROR_COUNT" -gt "$THRESHOLD" ]; then
    echo "ALERT: $ERROR_COUNT errors found (threshold: $THRESHOLD)"
    grep ERROR "$LOG_FILE" | tail -5
fi
```

---

**5. Dashboard en temps réel**

```bash
#!/bin/bash
# live-dashboard.sh

while true; do
    clear
    echo "=== Live Log Dashboard ==="
    echo ""
    echo "Errors: $(grep -c ERROR logs/app.log)"
    echo "Warnings: $(grep -c WARNING logs/app.log)"
    echo "Info: $(grep -c INFO logs/app.log)"
    echo ""
    echo "=== Last 5 errors ==="
    grep ERROR logs/app.log | tail -5
    sleep 5
done
```

---

## [COURS] CONCLUSION DE L'EXERCICE 2.2

**[OK] Félicitations ! Tu maîtrises maintenant grep !**

**Ce que tu as appris :**
- Recherche simple et avancée avec grep
- Expressions régulières (basiques et étendues)
- Recherche récursive dans dossiers
- Extraction de contexte
- Filtrage et inversion
- Extraction de données structurées
- Cas d'usage professionnels

**Compétences acquises :**
- [OK] Recherche et filtrage (niveau avancé)
- [OK] Expressions régulières
- [OK] Analyse de logs
- [OK] Extraction de données

**Temps moyen de réalisation :** 1h30 - 2h

**Prochaine étape :** Exercice 2.3 - Manipulation de texte avec `sed` et `awk` ! [BLACK_SCISSORS]

---

Je continue avec l'exercice 2.3 (sed et awk) ?

# [ROUGE] EXERCICE 2.3 : MANIPULATION DE TEXTE AVEC `sed` ET `awk` (AVANCÉ)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es DevOps/Data Engineer et tu dois quotidiennement :
- Transformer des fichiers de configuration
- Nettoyer et formater des données CSV
- Modifier des fichiers de logs pour analyse
- Extraire des statistiques de fichiers texte
- Automatiser la modification de code source
- Générer des rapports à partir de données brutes
- Convertir des formats de données

**Problématique :** Modifier et transformer du texte de manière automatisée et efficace.

### Cahier des charges

**Avec `sed` (Stream Editor) :**
- Remplacer du texte dans des fichiers
- Supprimer des lignes
- Insérer/ajouter du contenu
- Modifier des fichiers en place
- Utiliser des regex avancées

**Avec `awk` :**
- Extraire des colonnes spécifiques
- Calculer des statistiques (sommes, moyennes)
- Filtrer des lignes selon conditions
- Formater des rapports
- Traiter des fichiers CSV/TSV

### Contraintes

- Commandes : `sed`, `awk`
- Scripts complexes et automatisation
- Performance sur gros fichiers
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

**sed :**
- [OK] Substituer du texte (s///)
- [OK] Supprimer des lignes (d)
- [OK] Insérer du texte (i, a)
- [OK] Modifier en place (-i)
- [OK] Utiliser des regex avancées
- [OK] Créer des scripts sed

**awk :**
- [OK] Comprendre la structure awk
- [OK] Utiliser les variables prédéfinies
- [OK] Extraire et manipuler des colonnes
- [OK] Faire des calculs et statistiques
- [OK] Filtrer selon conditions
- [OK] Formater des sorties
- [OK] Créer des scripts awk

---

## [DOCS] PRÉREQUIS

- Exercices 2.1 et 2.2 terminés
- Maîtrise de grep et regex
- Compréhension des pipes

---

## [OK] SOLUTION COMPLÈTE

### PARTIE 1 : `sed` - STREAM EDITOR

### ÉTAPE 1 : Créer des fichiers de test

```bash
# Créer le dossier de travail
mkdir -p ~/sed-awk-exercise
cd ~/sed-awk-exercise

# Créer des sous-dossiers
mkdir -p {config,data,logs,scripts}
```

---

**Créer un fichier de configuration :**

```bash
cat > config/app.conf << 'EOF'
# Application Configuration
app_name=MyApplication
version=1.0.0
environment=development
debug=true
port=8080
host=localhost

# Database settings
db_host=localhost
db_port=5432
db_name=myapp_dev
db_user=dev_user
db_password=dev123

# Cache settings
cache_enabled=true
cache_ttl=3600
cache_host=localhost
cache_port=6379
EOF
```

---

**Créer un fichier de données CSV :**

```bash
cat > data/employees.csv << 'EOF'
id,name,department,salary,email
1,John Doe,Engineering,85000,john.doe@company.com
2,Jane Smith,Marketing,75000,jane.smith@company.com
3,Bob Johnson,Engineering,90000,bob.johnson@company.com
4,Alice Williams,HR,65000,alice.williams@company.com
5,Charlie Brown,Engineering,95000,charlie.brown@company.com
6,Diana Prince,Marketing,70000,diana.prince@company.com
7,Eve Davis,Engineering,88000,eve.davis@company.com
8,Frank Miller,Sales,72000,frank.miller@company.com
9,Grace Lee,Engineering,92000,grace.lee@company.com
10,Henry Wilson,HR,68000,henry.wilson@company.com
EOF
```

---

**Créer un fichier de log :**

```bash
cat > logs/server.log << 'EOF'
2024-12-30 08:00:01 INFO Server started on port 8080
2024-12-30 08:00:05 INFO Connected to database at localhost:5432
2024-12-30 08:05:12 DEBUG Processing request from 192.168.1.100
2024-12-30 08:05:13 DEBUG Query execution time: 45ms
2024-12-30 08:10:23 WARNING Slow query detected: SELECT * FROM users
2024-12-30 08:15:30 ERROR Connection timeout to external API
2024-12-30 08:15:31 INFO Retrying connection (attempt 1/3)
2024-12-30 08:15:35 INFO Connection restored
2024-12-30 08:20:45 DEBUG Cache hit rate: 87%
2024-12-30 08:25:10 WARNING High memory usage: 85%
2024-12-30 08:30:00 INFO Backup started
2024-12-30 08:32:15 INFO Backup completed successfully
EOF
```

---

**Créer un fichier HTML simple :**

```bash
cat > config/template.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>TITLE_PLACEHOLDER</title>
</head>
<body>
    <h1>HEADING_PLACEHOLDER</h1>
    <p>CONTENT_PLACEHOLDER</p>
    <footer>VERSION_PLACEHOLDER</footer>
</body>
</html>
EOF
```

---

### ÉTAPE 2 : `sed` - Substitution de texte (commande `s`)

**Syntaxe de base de `sed` :**

```
sed 's/pattern/replacement/flags' fichier
```

**Structure de la commande `s` :**
- `s` = Substitute (substituer)
- `/pattern/` = Ce qu'on cherche (regex)
- `/replacement/` = Par quoi on remplace
- `flags` = Options (g, i, p, etc.)

---

**Test 1 : Substitution simple**

```bash
# Remplacer "development" par "production"
sed 's/development/production/' config/app.conf
```

**Résultat :**
```
environment=production
```

**[ATTENTION] Par défaut, sed affiche le résultat SANS modifier le fichier !**

---

**Test 2 : Remplacer TOUTES les occurrences avec `g`**

```bash
# Remplacer tous les "localhost"
sed 's/localhost/127.0.0.1/' config/app.conf
```

**Sans `g` :**
```
host=127.0.0.1          # [OK] Remplacé
db_host=localhost       # [X] Pas remplacé (déjà un remplacement sur cette ligne)
cache_host=localhost    # [X] Pas remplacé
```

**Avec `g` (global) :**

```bash
sed 's/localhost/127.0.0.1/g' config/app.conf
```

**Résultat :**
```
host=127.0.0.1
db_host=127.0.0.1
cache_host=127.0.0.1
```

**`g` = Global (toutes les occurrences sur chaque ligne)**

---

**Test 3 : Remplacement insensible à la casse avec `i`**

```bash
# Remplacer "info" peu importe la casse
sed 's/info/DEBUG/gi' logs/server.log
```

**Trouve "INFO", "info", "Info", etc.**

**`i` = Ignore case (après le dernier `/`)**

---

**Test 4 : Afficher seulement les lignes modifiées avec `p`**

```bash
# Afficher seulement les lignes où un remplacement a eu lieu
sed -n 's/localhost/127.0.0.1/gp' config/app.conf
```

**`-n` = Suppress (ne pas afficher toutes les lignes)**  
**`p` = Print (afficher les lignes modifiées)**

---

**Test 5 : Modifier le fichier en place avec `-i`**

```bash
# [ATTENTION] ATTENTION : Modifie le fichier directement !

# Créer une copie d'abord
cp config/app.conf config/app.conf.bak

# Modifier en place
sed -i 's/development/production/' config/app.conf

# Vérifier
grep environment config/app.conf
# environment=production

# Restaurer
mv config/app.conf.bak config/app.conf
```

**`-i` = In-place (modification directe)**

**Créer un backup automatique :**

```bash
# Créer un backup avec extension .bak
sed -i.bak 's/development/production/' config/app.conf

# Fichiers créés :
# config/app.conf      (modifié)
# config/app.conf.bak  (original)
```

---

**Test 6 : Utiliser un délimiteur différent**

```bash
# Remplacer des chemins (avec /)
# Problème avec / comme délimiteur :
sed 's//usr/local/bin//opt/bin/' file.txt  # [X] Confus !

# Solution : Utiliser un autre délimiteur
sed 's|/usr/local/bin|/opt/bin|' file.txt  # [OK] Clair !

# Ou avec #
sed 's#/usr/local/bin#/opt/bin#' file.txt
```

**On peut utiliser n'importe quel caractère comme délimiteur !**

---

**Test 7 : Références arrière (backreferences)**

```bash
# Capturer des groupes et les réutiliser
# Exemple : Inverser prénom et nom

echo "John Doe" | sed 's/\(.*\) \(.*\)/\2, \1/'
# Résultat : Doe, John
```

**Explication :**
- `\(.*\)` : Capture le premier mot -> `\1`
- ` ` : Espace
- `\(.*\)` : Capture le deuxième mot -> `\2`
- `\2, \1` : Réutilise groupe 2, virgule, groupe 1

---

**Test 8 : Remplacements multiples (enchaînement)**

```bash
# Plusieurs substitutions à la suite
sed 's/development/production/; s/8080/80/; s/localhost/0.0.0.0/' config/app.conf
```

**`;` sépare les commandes**

**Ou sur plusieurs lignes :**

```bash
sed '
s/development/production/
s/8080/80/
s/localhost/0.0.0.0/
' config/app.conf
```

---

**Test 9 : Remplacer seulement sur certaines lignes**

```bash
# Remplacer seulement sur les lignes 5 à 10
sed '5,10s/localhost/127.0.0.1/' config/app.conf

# Remplacer seulement sur la ligne 3
sed '3s/localhost/127.0.0.1/' config/app.conf

# Remplacer à partir de la ligne 5 jusqu'à la fin
sed '5,$s/localhost/127.0.0.1/' config/app.conf
```

**Syntaxe d'adresses :**
- `5` : Ligne 5
- `5,10` : Lignes 5 à 10
- `5,$` : Ligne 5 jusqu'à la fin
- `$` : Dernière ligne

---

**Test 10 : Remplacer seulement sur les lignes qui matchent un pattern**

```bash
# Remplacer "localhost" seulement sur les lignes contenant "db_"
sed '/db_/s/localhost/192.168.1.50/' config/app.conf
```

**Syntaxe :** `/pattern/s/old/new/`

---

### ÉTAPE 3 : `sed` - Suppression de lignes (commande `d`)

**Test 1 : Supprimer une ligne spécifique**

```bash
# Supprimer la ligne 3
sed '3d' config/app.conf

# Supprimer les lignes 5 à 8
sed '5,8d' config/app.conf

# Supprimer la dernière ligne
sed '$d' config/app.conf
```

**`d` = Delete**

---

**Test 2 : Supprimer les lignes qui matchent un pattern**

```bash
# Supprimer toutes les lignes contenant "debug"
sed '/debug/d' config/app.conf

# Supprimer les lignes vides
sed '/^$/d' config/app.conf

# Supprimer les commentaires (lignes commençant par #)
sed '/^#/d' config/app.conf
```

---

**Test 3 : Supprimer les lignes entre deux patterns**

```bash
# Supprimer de "Database settings" à "Cache settings"
sed '/Database settings/,/Cache settings/d' config/app.conf
```

---

**Test 4 : Garder seulement certaines lignes (inverse de `d`)**

```bash
# Garder seulement les lignes 5 à 10
sed -n '5,10p' config/app.conf

# Garder seulement les lignes contenant "ERROR"
sed -n '/ERROR/p' logs/server.log
```

**`-n` supprime l'affichage par défaut, `p` affiche ce qu'on veut.**

---

### ÉTAPE 4 : `sed` - Insertion et ajout (commandes `i`, `a`, `c`)

**Test 1 : Insérer une ligne AVANT avec `i`**

```bash
# Insérer une ligne avant la ligne 2
sed '2i\# This is a new comment' config/app.conf
```

**Résultat :**
```
# Application Configuration
# This is a new comment
app_name=MyApplication
```

**`i` = Insert (insérer avant)**

---

**Test 2 : Ajouter une ligne APRÈS avec `a`**

```bash
# Ajouter une ligne après la ligne 1
sed '1a\# Added comment' config/app.conf
```

**`a` = Append (ajouter après)**

---

**Test 3 : Remplacer une ligne entière avec `c`**

```bash
# Remplacer la ligne 2 par un nouveau texte
sed '2c\app_name=NewApplication' config/app.conf
```

**`c` = Change (changer)**

---

**Test 4 : Insérer avant un pattern**

```bash
# Insérer une ligne avant toute ligne contenant "Database"
sed '/Database/i\# === Database Configuration ===' config/app.conf
```

---

**Test 5 : Ajouter à la fin du fichier**

```bash
# Ajouter une ligne à la fin
sed '$a\# End of configuration' config/app.conf
```

---

### ÉTAPE 5 : `sed` - Cas d'usage avancés

**Cas 1 : Remplacer des placeholders dans un template**

```bash
# Créer un script qui remplace les placeholders
sed '
s/TITLE_PLACEHOLDER/Mon Site Web/
s/HEADING_PLACEHOLDER/Bienvenue sur mon site/
s/CONTENT_PLACEHOLDER/Ceci est le contenu principal/
s/VERSION_PLACEHOLDER/Version 1.0.0/
' config/template.html > output.html

cat output.html
```

---

**Cas 2 : Nettoyer un fichier de config**

```bash
# Supprimer commentaires et lignes vides
sed '
/^#/d       # Supprimer commentaires
/^$/d       # Supprimer lignes vides
' config/app.conf
```

---

**Cas 3 : Convertir Windows -> Unix (CRLF -> LF)**

```bash
# Supprimer les \r (carriage return)
sed 's/\r$//' windows-file.txt > unix-file.txt
```

---

**Cas 4 : Extraire une section spécifique**

```bash
# Extraire tout entre deux patterns
sed -n '/Database settings/,/Cache settings/p' config/app.conf
```

---

**Cas 5 : Numéroter les lignes**

```bash
# Ajouter numéros de ligne
sed = config/app.conf | sed 'N;s/\n/ /'
```

**Explication :**
- `sed =` : Affiche les numéros de ligne
- `N` : Lit la ligne suivante dans le buffer
- `s/\n/ /` : Remplace le saut de ligne par un espace

---

**Cas 6 : Changer le format de dates**

```bash
# De YYYY-MM-DD à DD/MM/YYYY
echo "2024-12-30" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
# Résultat : 30/12/2024
```

---

**Cas 7 : Remplacer les variables d'environnement**

```bash
# Remplacer ${VAR} par la valeur de $VAR
export DB_PASSWORD="secret123"

sed "s/\${DB_PASSWORD}/$DB_PASSWORD/" config/app.conf
```

**[ATTENTION] Utiliser des guillemets doubles `"` pour permettre l'expansion de variables !**

---

**Cas 8 : Créer un script sed réutilisable**

```bash
# Créer un fichier de commandes sed
cat > scripts/cleanup.sed << 'EOF'
# Nettoyer un fichier de config

# Supprimer les commentaires
/^#/d

# Supprimer les lignes vides
/^$/d

# Supprimer les espaces en fin de ligne
s/[[:space:]]*$//

# Convertir les tabulations en espaces
s/\t/    /g
EOF

# Utiliser le script
sed -f scripts/cleanup.sed config/app.conf
```

**`-f FILE` : Lire les commandes depuis un fichier**

---

### PARTIE 2 : `awk` - TEXT PROCESSING

### ÉTAPE 6 : `awk` - Introduction et structure

**`awk` = Aho, Weinberger, Kernighan (créateurs)**

**Syntaxe de base :**

```
awk 'pattern { action }' fichier
```

**Structure d'un programme awk :**

```
BEGIN { actions avant traitement }
pattern { actions pour chaque ligne }
END { actions après traitement }
```

---

**Variables prédéfinies importantes :**

| Variable | Description | Exemple |
|----------|-------------|---------|
| `$0` | Ligne entière | Toute la ligne |
| `$1, $2, $3...` | Colonnes (fields) | 1ère, 2ème, 3ème colonne |
| `NF` | Number of Fields | Nombre de colonnes |
| `NR` | Number of Records | Numéro de ligne |
| `FS` | Field Separator | Séparateur de colonnes (défaut: espace) |
| `OFS` | Output Field Separator | Séparateur en sortie |
| `RS` | Record Separator | Séparateur de lignes (défaut: `\n`) |
| `ORS` | Output Record Separator | Séparateur de lignes en sortie |
| `FILENAME` | Nom du fichier | Nom du fichier en cours |

---

**Test 1 : Afficher toutes les lignes**

```bash
# Équivalent à cat
awk '{ print }' config/app.conf

# Ou plus explicite
awk '{ print $0 }' config/app.conf
```

**`$0` = Ligne entière**

---

**Test 2 : Afficher une colonne spécifique**

```bash
# Afficher la 2ème colonne du CSV
awk -F, '{ print $2 }' data/employees.csv
```

**Résultat :**
```
name
John Doe
Jane Smith
Bob Johnson
...
```

**`-F,` = Field separator = `,` (virgule)**

---

**Test 3 : Afficher plusieurs colonnes**

```bash
# Afficher nom et département
awk -F, '{ print $2, $3 }' data/employees.csv
```

**Résultat :**
```
name department
John Doe Engineering
Jane Smith Marketing
...
```

---

**Test 4 : Afficher avec formatage personnalisé**

```bash
# Afficher avec un format précis
awk -F, '{ print $2 " travaille au département " $3 }' data/employees.csv
```

**Résultat :**
```
name travaille au département department
John Doe travaille au département Engineering
...
```

---

**Test 5 : Ignorer l'en-tête avec `NR`**

```bash
# Afficher toutes les lignes SAUF la première
awk -F, 'NR > 1 { print $2, $3 }' data/employees.csv
```

**`NR > 1` = Pattern (condition)**  
**`{ print }` = Action**

---

**Test 6 : Afficher le numéro de ligne**

```bash
# Afficher numéro de ligne + contenu
awk '{ print NR, $0 }' config/app.conf
```

**Résultat :**
```
1 # Application Configuration
2 app_name=MyApplication
3 version=1.0.0
...
```

---

**Test 7 : Afficher le nombre de colonnes**

```bash
# Afficher le nombre de colonnes de chaque ligne
awk -F, '{ print NF }' data/employees.csv
```

**`NF` = Number of Fields (colonnes)**

---

**Test 8 : Afficher la dernière colonne**

```bash
# Afficher la dernière colonne (peu importe le nombre de colonnes)
awk -F, '{ print $NF }' data/employees.csv
```

**`$NF` = Dernière colonne**

---

### ÉTAPE 7 : `awk` - Patterns et conditions

**Test 1 : Filtrer selon une condition**

```bash
# Afficher seulement les ingénieurs
awk -F, '$3 == "Engineering" { print $2 }' data/employees.csv
```

**Résultat :**
```
John Doe
Bob Johnson
Charlie Brown
Eve Davis
Grace Lee
```

---

**Test 2 : Comparaisons numériques**

```bash
# Afficher les employés avec salaire > 80000
awk -F, 'NR > 1 && $4 > 80000 { print $2, $4 }' data/employees.csv
```

**Opérateurs de comparaison :**
- `==` : Égal
- `!=` : Différent
- `>` : Plus grand
- `<` : Plus petit
- `>=` : Plus grand ou égal
- `<=` : Plus petit ou égal

---

**Test 3 : Opérateurs logiques**

```bash
# Engineering ET salaire > 85000
awk -F, '$3 == "Engineering" && $4 > 85000 { print $2, $4 }' data/employees.csv

# Engineering OU Marketing
awk -F, '$3 == "Engineering" || $3 == "Marketing" { print $2, $3 }' data/employees.csv

# PAS Engineering
awk -F, '$3 != "Engineering" { print $2, $3 }' data/employees.csv
```

**Opérateurs logiques :**
- `&&` : ET
- `||` : OU
- `!` : NON

---

**Test 4 : Recherche de pattern avec `~`**

```bash
# Lignes contenant "ERROR"
awk '/ERROR/ { print }' logs/server.log

# Colonne 3 contient "Eng"
awk -F, '$3 ~ /Eng/ { print $2, $3 }' data/employees.csv

# Email se termine par company.com
awk -F, '$5 ~ /@company\.com$/ { print $2, $5 }' data/employees.csv
```

**`~` : Match (correspond à)**  
**`!~` : Ne correspond pas à**

---

**Test 5 : Plages de lignes**

```bash
# Lignes 3 à 7
awk 'NR >= 3 && NR <= 7' config/app.conf

# Entre deux patterns
awk '/Database settings/,/Cache settings/' config/app.conf
```

---

### ÉTAPE 8 : `awk` - Calculs et statistiques

**Test 1 : Calculer une somme**

```bash
# Somme de tous les salaires
awk -F, 'NR > 1 { sum += $4 } END { print "Total salaires:", sum }' data/employees.csv
```

**Résultat :**
```
Total salaires: 800000
```

**Explication :**
- `NR > 1` : Ignorer l'en-tête
- `sum += $4` : Additionner la colonne 4 (salaire)
- `END { }` : Exécuter après toutes les lignes
- `print` : Afficher le résultat

---

**Test 2 : Calculer une moyenne**

```bash
# Moyenne des salaires
awk -F, '
NR > 1 { 
    sum += $4
    count++
} 
END { 
    print "Moyenne:", sum / count 
}' data/employees.csv
```

**Résultat :**
```
Moyenne: 80000
```

---

**Test 3 : Trouver le maximum**

```bash
# Salaire maximum
awk -F, '
NR > 1 { 
    if ($4 > max) {
        max = $4
        person = $2
    }
} 
END { 
    print "Salaire max:", max, "(" person ")" 
}' data/employees.csv
```

**Résultat :**
```
Salaire max: 95000 (Charlie Brown)
```

---

**Test 4 : Compter par catégorie**

```bash
# Compter les employés par département
awk -F, '
NR > 1 { 
    count[$3]++ 
} 
END { 
    for (dept in count) {
        print dept ":", count[dept]
    }
}' data/employees.csv
```

**Résultat :**
```
Engineering: 5
Marketing: 2
HR: 2
Sales: 1
```

**`count[$3]++` = Tableau associatif (hash/dict)**

---

**Test 5 : Statistiques complètes**

```bash
# Statistiques par département
awk -F, '
NR > 1 { 
    count[$3]++
    sum[$3] += $4
} 
END { 
    print "Département | Employés | Salaire total | Moyenne"
    print "----------|----------|---------------|--------"
    for (dept in count) {
        avg = sum[dept] / count[dept]
        printf "%-12s | %8d | %13d | %7.0f\n", dept, count[dept], sum[dept], avg
    }
}' data/employees.csv
```

**Résultat :**
```
Département | Employés | Salaire total | Moyenne
----------|----------|---------------|--------
Engineering  |        5 |        450000 |   90000
Marketing    |        2 |        145000 |   72500
HR           |        2 |        133000 |   66500
Sales        |        1 |         72000 |   72000
```

---

### ÉTAPE 9 : `awk` - Formatage avancé avec `printf`

**`printf` = Format précis de la sortie**

**Syntaxe :**

```
printf "format", valeur1, valeur2, ...
```

**Spécificateurs de format :**

| Spécificateur | Description | Exemple |
|---------------|-------------|---------|
| `%s` | String (texte) | `"Hello"` |
| `%d` | Integer (entier) | `42` |
| `%f` | Float (décimal) | `3.14159` |
| `%e` | Scientific notation | `1.23e+10` |
| `%x` | Hexadécimal | `ff` |
| `%%` | Symbole % littéral | `%` |

**Modificateurs :**
- `%10s` : Largeur 10, aligné à droite
- `%-10s` : Largeur 10, aligné à gauche
- `%.2f` : 2 décimales
- `%10.2f` : Largeur 10, 2 décimales

---

**Test 1 : Alignement de colonnes**

```bash
# Tableau bien formaté
awk -F, '
NR > 1 { 
    printf "%-20s | %-12s | %8d\n", $2, $3, $4 
}' data/employees.csv
```

**Résultat :**
```
John Doe             | Engineering  |    85000
Jane Smith           | Marketing    |    75000
Bob Johnson          | Engineering  |    90000
...
```

---

**Test 2 : Nombres avec 2 décimales**

```bash
# Calculer des pourcentages
awk -F, '
NR > 1 { 
    total += $4 
} 
END { 
    print "Total:", total
}' data/employees.csv | awk '{ printf "Total: $%\047.2f\n", $2 }'
```

---

**Test 3 : Tableau avec bordures**

```bash
awk -F, '
BEGIN { 
    print "+----------------------+--------+"
    print "| Nom                  | Salaire|"
    print "+----------------------+--------+"
}
NR > 1 { 
    printf "| %-20s | %6d |\n", $2, $4 
}
END {
    print "+----------------------+--------+"
}' data/employees.csv
```

---

### ÉTAPE 10 : `awk` - Fonctions intégrées

**Fonctions sur les chaînes :**

| Fonction | Description | Exemple |
|----------|-------------|---------|
| `length(s)` | Longueur | `length("hello")` -> 5 |
| `substr(s,pos,len)` | Sous-chaîne | `substr("hello",2,3)` -> "ell" |
| `tolower(s)` | Minuscules | `tolower("HELLO")` -> "hello" |
| `toupper(s)` | Majuscules | `toupper("hello")` -> "HELLO" |
| `split(s,arr,sep)` | Découper | `split("a:b:c",a,":")` |
| `gsub(pat,rep,s)` | Remplacer global | `gsub(/a/,"X","banana")` -> "bXnXnX" |
| `sub(pat,rep,s)` | Remplacer 1ère | `sub(/a/,"X","banana")` -> "bXnana" |
| `match(s,pat)` | Position du match | `match("hello",/ll/)` -> 3 |

---

**Test 1 : Convertir en majuscules**

```bash
# Noms en majuscules
awk -F, 'NR > 1 { print toupper($2) }' data/employees.csv
```

---

**Test 2 : Extraire une sous-chaîne**

```bash
# Extraire le prénom (avant l'espace)
awk -F, 'NR > 1 { print substr($2, 1, index($2, " ")-1) }' data/employees.csv
```

---

**Test 3 : Remplacer dans une chaîne**

```bash
# Remplacer @company.com par @newcompany.com
awk -F, '{ gsub(/@company\.com/, "@newcompany.com", $5); print $5 }' data/employees.csv
```

---

**Fonctions mathématiques :**

| Fonction | Description |
|----------|-------------|
| `int(x)` | Partie entière |
| `sqrt(x)` | Racine carrée |
| `sin(x)`, `cos(x)`, `tan(x)` | Trigonométrie |
| `log(x)` | Logarithme naturel |
| `exp(x)` | Exponentielle |
| `rand()` | Nombre aléatoire [0,1) |
| `srand(x)` | Initialiser générateur aléatoire |

---

**Test 4 : Arrondir des nombres**

```bash
# Arrondir les salaires aux milliers
awk -F, 'NR > 1 { print $2, int($4/1000 + 0.5) * 1000 }' data/employees.csv
```

---

### ÉTAPE 11 : `awk` - Cas d'usage professionnels avancés

**Cas 1 : Générer un rapport de logs**

```bash
# Analyser les logs par niveau
awk '
BEGIN {
    print "=== Rapport d'\''analyse de logs ==="
    print ""
}
{
    # Extraire le niveau (INFO, ERROR, etc.)
    match($0, /\[(.*)\]/, level)
    if (level[1] != "") {
        count[level[1]]++
    }
}
END {
    print "Répartition par niveau :"
    for (lvl in count) {
        printf "  %-10s : %3d occurrences\n", lvl, count[lvl]
    }
    print ""
    print "Total de lignes :", NR
}' logs/server.log
```

---

**Cas 2 : Convertir CSV en JSON**

```bash
# Convertir employees.csv en JSON
awk -F, '
BEGIN { 
    print "["
}
NR > 1 { 
    printf "  {\n"
    printf "    \"id\": %d,\n", $1
    printf "    \"name\": \"%s\",\n", $2
    printf "    \"department\": \"%s\",\n", $3
    printf "    \"salary\": %d,\n", $4
    printf "    \"email\": \"%s\"\n", $5
    if (NR < 11) printf "  },\n"
    else printf "  }\n"
}
END { 
    print "]"
}' data/employees.csv > data/employees.json
```

---

**Cas 3 : Générer des requêtes SQL**

```bash
# Générer des INSERT SQL
awk -F, '
BEGIN {
    print "-- Generated SQL"
    print "BEGIN TRANSACTION;"
}
NR > 1 {
    printf "INSERT INTO employees (id, name, department, salary, email) VALUES (%d, '\''%s'\'', '\''%s'\'', %d, '\''%s'\'');\n", 
           $1, $2, $3, $4, $5
}
END {
    print "COMMIT;"
}' data/employees.csv
```

---

**Cas 4 : Analyser les performances (access.log)**

```bash
# Créer un log d'accès web factice
cat > logs/web-access.log << 'EOF'
192.168.1.100 - - [30/Dec/2024:08:00:15] "GET /api/users HTTP/1.1" 200 1234 0.045
192.168.1.101 - - [30/Dec/2024:08:01:23] "POST /api/login HTTP/1.1" 200 89 0.123
192.168.1.100 - - [30/Dec/2024:08:02:45] "GET /api/products HTTP/1.1" 200 5678 0.234
192.168.1.102 - - [30/Dec/2024:08:03:12] "POST /api/orders HTTP/1.1" 201 456 0.567
192.168.1.100 - - [30/Dec/2024:08:04:56] "GET /api/users HTTP/1.1" 500 123 2.345
EOF

# Analyser les temps de réponse
awk '
{
    time = $NF
    sum += time
    count++
    
    if (time > max) {
        max = time
        max_line = $0
    }
    
    if (count == 1 || time < min) {
        min = time
    }
}
END {
    printf "Temps de réponse moyen : %.3f secondes\n", sum/count
    printf "Minimum : %.3f secondes\n", min
    printf "Maximum : %.3f secondes\n", max
    print "Requête la plus lente :", max_line
}' logs/web-access.log
```

---

**Cas 5 : Créer un dashboard en temps réel**

```bash
# Script de monitoring en temps réel
#!/bin/bash
# monitor.sh

while true; do
    clear
    echo "=== Dashboard Temps Réel ==="
    echo "Mis à jour : $(date '+%Y-%m-%d %H:%M:%S')"
    echo ""
    
    # Statistiques des logs
    tail -100 logs/server.log | awk '
    {
        match($0, /\[(.*)\]/, level)
        count[level[1]]++
    }
    END {
        print "Répartition (dernières 100 lignes) :"
        for (lvl in count) {
            printf "  %-10s : %3d\n", lvl, count[lvl]
        }
    }'
    
    sleep 5
done
```

---

**Cas 6 : Extraire et valider des données**

```bash
# Valider les emails
awk -F, '
NR > 1 {
    if ($5 !~ /@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/) {
        print "Email invalide à la ligne", NR ":", $5
    }
}' data/employees.csv
```

---

**Cas 7 : Transformer un format de date**

```bash
# De format log à ISO
awk '
{
    # Extraire la date du log
    match($0, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2})\]/, date_arr)
    if (date_arr[1] != "") {
        # Conversion simplifiée (exemple)
        print date_arr[1]
    }
}' logs/web-access.log
```

---

### ÉTAPE 12 : Scripts `awk` standalone

**Créer un script awk réutilisable :**

```bash
cat > scripts/analyze-csv.awk << 'EOF'
#!/usr/bin/awk -f
# Script d'analyse CSV

BEGIN {
    FS = ","
    print "=== Analyse CSV ==="
    print ""
}

NR == 1 {
    # Sauvegarder les en-têtes
    for (i = 1; i <= NF; i++) {
        headers[i] = $i
    }
    next
}

{
    # Compter les lignes
    total++
    
    # Analyser chaque colonne
    for (i = 1; i <= NF; i++) {
        if ($i != "") {
            non_empty[i]++
        }
    }
}

END {
    print "Nombre de lignes (sans en-tête) :", total
    print ""
    print "Colonnes :"
    for (i = 1; i <= length(headers); i++) {
        empty = total - non_empty[i]
        printf "  %-15s : %3d remplies, %3d vides\n", 
               headers[i], non_empty[i], empty
    }
}
EOF

# Rendre exécutable
chmod +x scripts/analyze-csv.awk

# Utiliser
./scripts/analyze-csv.awk data/employees.csv
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : sed substitue correctement**

```bash
echo "Hello World" | sed 's/World/Universe/' | grep -q "Universe" && echo "[OK] Test 1 OK"
```

---

**Test 2 : sed supprime correctement**

```bash
echo -e "Line1\nLine2\nLine3" | sed '2d' | wc -l | grep -q "2" && echo "[OK] Test 2 OK"
```

---

**Test 3 : awk extrait une colonne**

```bash
echo "a,b,c" | awk -F, '{print $2}' | grep -q "b" && echo "[OK] Test 3 OK"
```

---

**Test 4 : awk calcule une somme**

```bash
echo -e "1\n2\n3\n4\n5" | awk '{sum+=$1} END {print sum}' | grep -q "15" && echo "[OK] Test 4 OK"
```

---

**Test 5 : sed et awk combinés**

```bash
echo "John:Doe:30" | sed 's/:/ /g' | awk '{print $1, $3}' | grep -q "John 30" && echo "[OK] Test 5 OK"
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : sed modifie sans -i mais rien ne change

**Symptôme :**
```bash
sed 's/old/new/' file.txt
# Affiche le résultat mais file.txt n'a pas changé
```

**Solution :**
```bash
# Utiliser -i pour modifier en place
sed -i 's/old/new/' file.txt

# Ou rediriger vers un nouveau fichier
sed 's/old/new/' file.txt > file-new.txt
```

---

#### Erreur 2 : awk n'affiche rien

**Symptôme :**
```bash
awk -F, '$3 == Engineering' file.csv
# Rien ne s'affiche
```

**Cause :** Oubli des guillemets autour de la valeur.

**Solution :**
```bash
awk -F, '$3 == "Engineering"' file.csv
```

---

#### Erreur 3 : Variables shell non expansées dans sed

**Symptôme :**
```bash
VAR="production"
sed 's/development/$VAR/' file.txt
# Remplace par "$VAR" littéral, pas "production"
```

**Solution :**
```bash
# Utiliser des guillemets doubles
sed "s/development/$VAR/" file.txt
```

---

#### Erreur 4 : Mauvais séparateur dans sed

**Symptôme :**
```bash
sed 's//usr/local//opt/' file.txt
# Erreur de syntaxe
```

**Solution :**
```bash
# Utiliser un autre délimiteur
sed 's|/usr/local|/opt|' file.txt
```

---

#### Erreur 5 : awk ne voit pas toutes les colonnes

**Symptôme :**
```bash
awk '{print $5}' file.csv
# Affiche seulement une colonne au lieu de 5
```

**Cause :** Mauvais séparateur de champs.

**Solution :**
```bash
# Spécifier le séparateur correct
awk -F, '{print $5}' file.csv  # Pour CSV
awk -F'\t' '{print $5}' file.tsv  # Pour TSV
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**sed :**
- `s/old/new/` : Substitution
- `d` : Suppression
- `i`, `a`, `c` : Insertion, ajout, remplacement
- `-i` : Modification en place
- `-n` + `p` : Affichage sélectif
- Regex puissantes
- Enchaînement avec `;`

**awk :**
- `$0, $1, $2...` : Colonnes
- `NR, NF` : Numéro ligne, nombre colonnes
- `-F` : Séparateur de champs
- `BEGIN`, `END` : Sections spéciales
- Tableaux associatifs : `arr[key]`
- `printf` : Formatage précis
- Fonctions intégrées riches

**Bonnes pratiques :**
- Toujours tester sur une copie avant `-i`
- Utiliser des guillemets appropriés
- Commenter les scripts complexes
- Créer des fichiers de commandes réutilisables
- Combiner sed/awk avec pipes

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. perl - Plus puissant que sed/awk**

```bash
# Perl one-liner
perl -pe 's/old/new/g' file.txt

# Perl avec regex avancées
perl -ne 'print if /complex.*regex/' file.txt
```

---

**2. jq - Pour JSON (comme awk pour JSON)**

```bash
# Extraire un champ JSON
cat data.json | jq '.users[].name'

# Filtrer
cat data.json | jq '.users[] | select(.age > 30)'
```

---

**3. Miller (mlr) - Comme awk mais mieux**

```bash
# Installer
sudo apt install miller

# Utiliser
mlr --csv cut -f name,salary data/employees.csv
```

---

**4. Créer un ETL pipeline complet**

```bash
#!/bin/bash
# etl-pipeline.sh

# Extract
cat raw-data.txt |

# Transform
sed 's/old-format/new-format/' |
awk '{print toupper($0)}' |
grep -v '^#' |

# Load
sort | uniq > processed-data.txt
```

---

**5. Performance : sed vs awk vs perl**

```bash
# Benchmark simple
time sed 's/test/TEST/g' huge-file.txt > /dev/null
time awk '{gsub(/test/,"TEST"); print}' huge-file.txt > /dev/null
time perl -pe 's/test/TEST/g' huge-file.txt > /dev/null
```

---

## [COURS] CONCLUSION DE L'EXERCICE 2.3

**[OK] BRAVO ! Tu maîtrises maintenant sed et awk !**

**Ce que tu as appris :**

**sed :**
- Substitution de texte
- Suppression et insertion
- Modification en place
- Regex avancées
- Scripts sed

**awk :**
- Extraction de colonnes
- Calculs et statistiques
- Filtrage conditionnel
- Formatage précis
- Scripts awk professionnels

**Compétences acquises :**
- [OK] Manipulation de texte (niveau expert)
- [OK] Transformation de données
- [OK] Automatisation de traitements
- [OK] Génération de rapports

**Temps moyen de réalisation :** 2-3 heures

---

# [BRAVO] CONCLUSION SECTION 2 : AFFICHAGE & MANIPULATION

**[TROPHEE] FÉLICITATIONS ! Section 2 complète !**

**Récapitulatif des 3 exercices :**

| Exercice | Niveau | Compétences |
|----------|--------|-------------|
| 2.1 | [VERT] Débutant | cat, less, head, tail, wc |
| 2.2 | [JAUNE] Intermédiaire | grep, expressions régulières |
| 2.3 | [ROUGE] Avancé | sed, awk, transformation de données |

**Niveau atteint : EXPERT en Manipulation de Texte ! [RAPIDE]**

---

Tu veux que je continue avec la **Section 3 : Permissions & Propriété** ?


# [DOCS] SECTION 3 : PERMISSIONS & PROPRIÉTÉ

# [VERT] EXERCICE 3.1 : COMPRENDRE LES PERMISSIONS (DÉBUTANT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois gérer la sécurité des fichiers et dossiers sur un serveur Linux. Plusieurs utilisateurs et équipes accèdent au système, et tu dois :
- Contrôler qui peut lire, écrire ou exécuter les fichiers
- Protéger les fichiers sensibles
- Permettre la collaboration tout en maintenant la sécurité
- Comprendre pourquoi certaines commandes échouent avec "Permission denied"

### Cahier des charges

- Comprendre le système de permissions Unix/Linux
- Lire et interpréter les permissions d'un fichier
- Comprendre la différence fichier/dossier
- Identifier le propriétaire et le groupe
- Comprendre les trois niveaux : owner, group, others
- Diagnostiquer les problèmes de permissions

### Contraintes

- Commandes : `ls -l`, `stat`, `id`, `groups`
- Lecture et interprétation uniquement (pas de modification)
- Temps estimé : 45 minutes - 1 heure

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Lire les permissions Unix (`rwxrwxrwx`)
- [OK] Comprendre la notation octale (755, 644, etc.)
- [OK] Identifier propriétaire et groupe
- [OK] Comprendre les permissions sur dossiers
- [OK] Utiliser `ls -l` pour analyser les permissions
- [OK] Utiliser `stat` pour des informations détaillées
- [OK] Comprendre `umask`
- [OK] Diagnostiquer les problèmes "Permission denied"

---

## [DOCS] PRÉREQUIS

- Sections 1 et 2 complètes
- Accès à un terminal Linux

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer un environnement de test

```bash
# Créer le dossier de travail
mkdir -p ~/permissions-exercise
cd ~/permissions-exercise

# Créer différents types de fichiers
mkdir -p {public,private,shared,scripts}

# Fichiers avec différents contenus
echo "Fichier public accessible à tous" > public/readme.txt
echo "Fichier privé - confidentiel" > private/secret.txt
echo "Script de backup" > scripts/backup.sh
echo "Configuration partagée" > shared/config.conf

# Créer plus de fichiers pour tests
touch public/{file1.txt,file2.txt,file3.txt}
touch private/{confidential1.doc,confidential2.doc}
touch scripts/{deploy.sh,monitor.sh,cleanup.sh}
```

---

### ÉTAPE 2 : Comprendre la sortie de `ls -l`

**Afficher les informations détaillées :**

```bash
ls -l public/readme.txt
```

**Résultat typique :**
```
-rw-r--r-- 1 user user 35 Dec 30 10:00 public/readme.txt
```

**Décortiquons cette ligne :**

```
-rw-r--r--  1  user  user  35  Dec 30 10:00  public/readme.txt
    v       v   v     v    v       v              v
    │       │   │     │    │       │              └─ Nom du fichier
    │       │   │     │    │       └─ Date de modification
    │       │   │     │    └─ Taille (en octets)
    │       │   │     └─ Groupe propriétaire
    │       │   └─ Utilisateur propriétaire
    │       └─ Nombre de liens (hard links)
    └─ Permissions (10 caractères)
```

---

### ÉTAPE 3 : Décoder les 10 caractères de permissions

**Structure des permissions :**

```
-rwxrwxrwx
│└─┬─┘└┬┘└┬┘
│  │   │  └─── Others (autres utilisateurs)
│  │   └────── Group (groupe)
│  └────────── Owner (propriétaire)
└───────────── Type de fichier
```

---

**Premier caractère - Type de fichier :**

| Caractère | Type | Description |
|-----------|------|-------------|
| `-` | Fichier régulier | Fichier normal |
| `d` | Directory | Dossier |
| `l` | Link | Lien symbolique |
| `c` | Character device | Périphérique caractère |
| `b` | Block device | Périphérique bloc |
| `s` | Socket | Socket |
| `p` | Pipe | Pipe nommé |

---

**Les 9 caractères suivants - Permissions (par groupe de 3) :**

Chaque groupe de 3 caractères représente : **r w x**

| Position | Lettre | Permission | Valeur octale |
|----------|--------|------------|---------------|
| 1ère | `r` | Read (lecture) | 4 |
| 2ème | `w` | Write (écriture) | 2 |
| 3ème | `x` | Execute (exécution) | 1 |

**Si la permission est absente, on voit `-` au lieu de la lettre.**

---

**Exemple complet :**

```
-rw-r--r--
│││ │││ │││
│││ │││ ││└─ Others: Read (r)
│││ │││ │└── Others: No Write (-)
│││ │││ └─── Others: No Execute (-)
│││ ││└───── Group: Read (r)
│││ │└────── Group: No Write (-)
│││ └─────── Group: No Execute (-)
││└───────── Owner: Read (r)
│└────────── Owner: Write (w)
└─────────── Owner: No Execute (-)
```

**Interprétation :**
- **Propriétaire** : peut lire (r) et écrire (w)
- **Groupe** : peut seulement lire (r)
- **Autres** : peut seulement lire (r)

---

### ÉTAPE 4 : Les trois catégories d'utilisateurs

**1. Owner (Propriétaire) - u**
- Créateur du fichier (par défaut)
- Permissions : `rw-` (dans l'exemple)

**2. Group (Groupe) - g**
- Groupe associé au fichier
- Permissions : `r--` (dans l'exemple)

**3. Others (Autres) - o**
- Tous les autres utilisateurs du système
- Permissions : `r--` (dans l'exemple)

---

**Visualisation :**

```bash
# Afficher l'utilisateur actuel
whoami

# Afficher les groupes de l'utilisateur actuel
groups

# Afficher toutes les infos
id
```

**Résultat de `id` :**
```
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),27(sudo)
    ^           ^               ^
    User ID     Group ID        Tous les groupes
```

---

### ÉTAPE 5 : Permissions sur les FICHIERS

**Test 1 : Créer des fichiers avec différentes permissions**

```bash
# Créer un fichier normal
echo "test" > test-file.txt
ls -l test-file.txt
```

**Par défaut, vous verrez probablement :**
```
-rw-r--r-- 1 user user 5 Dec 30 10:00 test-file.txt
```

---

**Signification des permissions sur un FICHIER :**

**`r` (Read - Lecture) :**
- Permet de LIRE le contenu du fichier
- Commandes : `cat`, `less`, `grep`, etc.

**`w` (Write - Écriture) :**
- Permet de MODIFIER le contenu du fichier
- Commandes : éditeurs (`vim`, `nano`), redirection (`>`), etc.
- [ATTENTION] Ne permet PAS de supprimer le fichier (c'est une permission du dossier parent)

**`x` (Execute - Exécution) :**
- Permet d'EXÉCUTER le fichier comme un programme
- Nécessaire pour les scripts et binaires

---

**Exemples de combinaisons courantes sur fichiers :**

| Permissions | Octal | Signification | Usage |
|-------------|-------|---------------|-------|
| `-rw-------` | 600 | Owner: r+w | Fichiers privés |
| `-rw-r--r--` | 644 | Owner: r+w, Group+Others: r | Fichiers publics en lecture |
| `-rw-rw-r--` | 664 | Owner+Group: r+w, Others: r | Fichiers de groupe |
| `-rwxr-xr-x` | 755 | Owner: r+w+x, Group+Others: r+x | Scripts exécutables |
| `-rwx------` | 700 | Owner: r+w+x | Scripts privés |
| `-r--------` | 400 | Owner: r only | Lecture seule stricte |

---

### ÉTAPE 6 : Permissions sur les DOSSIERS

**[ATTENTION] IMPORTANT : Les permissions sur les dossiers ont une signification DIFFÉRENTE !**

**Test 1 : Créer et examiner un dossier**

```bash
mkdir test-dir
ls -ld test-dir
```

**Résultat typique :**
```
drwxr-xr-x 2 user user 4096 Dec 30 10:00 test-dir
^
Type = d (directory)
```

---

**Signification des permissions sur un DOSSIER :**

**`r` (Read - Lecture) :**
- Permet de LISTER le contenu du dossier
- Commande : `ls`
- [ATTENTION] Ne permet PAS d'accéder aux fichiers à l'intérieur !

**`w` (Write - Écriture) :**
- Permet de CRÉER, SUPPRIMER, RENOMMER des fichiers dans le dossier
- Commandes : `touch`, `rm`, `mv`, `mkdir`, etc.
- [ATTENTION] Nécessite aussi `x` pour fonctionner !

**`x` (Execute - Exécution) :**
- Permet de TRAVERSER le dossier (cd dedans)
- Permet d'accéder aux fichiers à l'intérieur
- **CRUCIAL : Sans `x`, on ne peut rien faire dans le dossier !**

---

**Tableau de vérité pour les dossiers :**

| r | w | x | Ce qu'on peut faire |
|---|---|---|---------------------|
| - | - | - | [X] Rien du tout |
| r | - | - | [X] Peut voir les noms mais pas y accéder |
| - | w | - | [X] Inutile sans x |
| - | - | x | [OK] Peut cd et accéder aux fichiers (si on connaît le nom) |
| r | - | x | [OK] Peut lister et accéder |
| - | w | x | [OK] Peut créer/supprimer (mais pas lister) |
| r | w | - | [X] Peut lister mais pas accéder |
| r | w | x | [OK] Contrôle total |

---

**Démonstration pratique :**

```bash
# Créer un dossier avec différents fichiers
mkdir demo-dir
echo "secret" > demo-dir/file1.txt
echo "public" > demo-dir/file2.txt

# Voir les permissions actuelles
ls -ld demo-dir
# drwxr-xr-x (755)

# On peut faire :
ls demo-dir              # [OK] Lister (r)
cat demo-dir/file1.txt   # [OK] Lire (x pour accès)
touch demo-dir/file3.txt # [OK] Créer (w + x)
```

---

**Exemples de combinaisons courantes sur dossiers :**

| Permissions | Octal | Signification | Usage |
|-------------|-------|---------------|-------|
| `drwx------` | 700 | Owner: full access | Dossier privé |
| `drwxr-xr-x` | 755 | Owner: full, Others: read+traverse | Dossier public |
| `drwxrwxr-x` | 775 | Owner+Group: full, Others: read+traverse | Dossier de groupe |
| `drwxrwx---` | 770 | Owner+Group: full, Others: nothing | Dossier de groupe privé |
| `drwxr-x---` | 750 | Owner: full, Group: read+traverse | Dossier semi-privé |

---

### ÉTAPE 7 : Notation octale (numérique)

**Conversion lettre -> nombre :**

Chaque permission a une valeur :
- `r` = 4
- `w` = 2
- `x` = 1
- `-` = 0

**On additionne pour chaque groupe de 3 :**

```
rwx = 4 + 2 + 1 = 7
rw- = 4 + 2 + 0 = 6
r-x = 4 + 0 + 1 = 5
r-- = 4 + 0 + 0 = 4
-wx = 0 + 2 + 1 = 3
-w- = 0 + 2 + 0 = 2
--x = 0 + 0 + 1 = 1
--- = 0 + 0 + 0 = 0
```

---

**Exemples de conversion complète :**

```
-rw-r--r--
 │  │  │
 │  │  └─ Others: r-- = 4
 │  └──── Group:  r-- = 4
 └─────── Owner:  rw- = 6
 
Résultat : 644
```

```
-rwxr-xr-x
 │  │  │
 │  │  └─ Others: r-x = 5
 │  └──── Group:  r-x = 5
 └─────── Owner:  rwx = 7
 
Résultat : 755
```

```
drwx------
 │  │  │
 │  │  └─ Others: --- = 0
 │  └──── Group:  --- = 0
 └─────── Owner:  rwx = 7
 
Résultat : 700
```

---

**Tableau de référence rapide :**

| Octal | Binaire | Lettres | Signification |
|-------|---------|---------|---------------|
| 0 | 000 | `---` | Aucune permission |
| 1 | 001 | `--x` | Exécution seulement |
| 2 | 010 | `-w-` | Écriture seulement |
| 3 | 011 | `-wx` | Écriture + Exécution |
| 4 | 100 | `r--` | Lecture seulement |
| 5 | 101 | `r-x` | Lecture + Exécution |
| 6 | 110 | `rw-` | Lecture + Écriture |
| 7 | 111 | `rwx` | Toutes permissions |

---

**Permissions courantes en octal :**

| Octal | Lettres | Usage typique |
|-------|---------|---------------|
| 644 | `-rw-r--r--` | Fichiers texte, config |
| 755 | `-rwxr-xr-x` | Scripts, programmes |
| 600 | `-rw-------` | Fichiers privés (ex: clés SSH) |
| 700 | `drwx------` | Dossiers privés |
| 664 | `-rw-rw-r--` | Fichiers de groupe |
| 775 | `drwxrwxr-x` | Dossiers de groupe |
| 777 | `-rwxrwxrwx` | [ATTENTION] Tout le monde (DANGEREUX) |
| 000 | `----------` | Aucun accès (même owner!) |

---

### ÉTAPE 8 : Commande `stat` - Informations détaillées

**`stat` donne TOUTES les informations sur un fichier :**

```bash
stat public/readme.txt
```

**Résultat :**
```
  File: public/readme.txt
  Size: 35        	Blocks: 8          IO Block: 4096   regular file
Device: 803h/2051d	Inode: 1234567     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    user)   Gid: ( 1000/    user)
Access: 2024-12-30 10:00:00.000000000 +0000
Modify: 2024-12-30 10:00:00.000000000 +0000
Change: 2024-12-30 10:00:00.000000000 +0000
 Birth: 2024-12-30 10:00:00.000000000 +0000
```

**Informations importantes :**

- **Access (permissions)** : `0644` en octal, `-rw-r--r--` en lettres
- **Uid** : User ID (1000) et nom (user)
- **Gid** : Group ID (1000) et nom (user)
- **Access** : Dernière lecture
- **Modify** : Dernière modification du contenu
- **Change** : Dernière modification des métadonnées (permissions, propriétaire)
- **Birth** : Date de création (pas toujours disponible)

---

**Format personnalisé avec `stat -c` :**

```bash
# Afficher seulement les permissions en octal
stat -c "%a" public/readme.txt
# 644

# Afficher permissions + propriétaire
stat -c "%a %U %G" public/readme.txt
# 644 user user

# Format personnalisé complet
stat -c "Fichier: %n | Perms: %a (%A) | Owner: %U | Group: %G" public/readme.txt
# Fichier: public/readme.txt | Perms: 644 (-rw-r--r--) | Owner: user | Group: user
```

**Codes de format utiles :**

| Code | Signification |
|------|---------------|
| `%n` | Nom du fichier |
| `%a` | Permissions en octal |
| `%A` | Permissions en lettres |
| `%U` | Nom du propriétaire |
| `%G` | Nom du groupe |
| `%u` | UID (numérique) |
| `%g` | GID (numérique) |
| `%s` | Taille en octets |
| `%F` | Type de fichier |

---

### ÉTAPE 9 : Comprendre `umask`

**`umask` = Masque de permissions par défaut**

**Concept :** Quand tu crées un nouveau fichier/dossier, `umask` définit les permissions par DÉFAUT en SOUSTRAYANT des permissions.

```bash
# Voir le umask actuel
umask
# 0022 (typique)

# Voir en mode symbolique
umask -S
# u=rwx,g=rx,o=rx
```

---

**Comment ça fonctionne :**

**Pour les FICHIERS :**
- Permissions maximales : `666` (rw-rw-rw-)
- On soustrait umask : `666 - 022 = 644`
- Résultat : `-rw-r--r--` (644)

**Pour les DOSSIERS :**
- Permissions maximales : `777` (rwxrwxrwx)
- On soustrait umask : `777 - 022 = 755`
- Résultat : `drwxr-xr-x` (755)

---

**Tableau des umask courants :**

| umask | Fichiers créés | Dossiers créés | Usage |
|-------|----------------|----------------|-------|
| 0022 | 644 (rw-r--r--) | 755 (rwxr-xr-x) | Par défaut (sécurité normale) |
| 0002 | 664 (rw-rw-r--) | 775 (rwxrwxr-x) | Travail en groupe |
| 0077 | 600 (rw-------) | 700 (rwx------) | Très sécurisé |
| 0000 | 666 (rw-rw-rw-) | 777 (rwxrwxrwx) | [ATTENTION] Pas sûr |

---

**Test pratique :**

```bash
# Vérifier umask actuel
umask
# 0022

# Créer un fichier
touch test-umask.txt
ls -l test-umask.txt
# -rw-r--r-- (644)

# Créer un dossier
mkdir test-umask-dir
ls -ld test-umask-dir
# drwxr-xr-x (755)
```

---

### ÉTAPE 10 : Diagnostiquer les problèmes "Permission denied"

**Scénario 1 : Impossible de lire un fichier**

```bash
# Créer un fichier test
echo "secret" > secret.txt

# Simuler restriction (on verra chmod dans le prochain exercice)
# Pour l'instant, juste analyser

ls -l secret.txt
# -rw-r--r-- 1 user user 7 Dec 30 10:00 secret.txt
```

**Questions à se poser :**

1. **Qui suis-je ?**
   ```bash
   whoami
   # user
   ```

2. **Qui est le propriétaire ?**
   ```bash
   ls -l secret.txt | awk '{print $3}'
   # user
   ```

3. **Ai-je la permission de lecture ?**
   ```bash
   # Je suis le propriétaire (user)
   # Permissions owner : rw-
   # Oui, j'ai 'r' (lecture) [OK]
   ```

---

**Scénario 2 : Impossible d'exécuter un script**

```bash
# Créer un script
cat > script.sh << 'EOF'
#!/bin/bash
echo "Hello World"
EOF

# Essayer de l'exécuter
./script.sh
# bash: ./script.sh: Permission denied
```

**Diagnostic :**

```bash
ls -l script.sh
# -rw-r--r-- 1 user user 31 Dec 30 10:00 script.sh
#     ^
# Pas de 'x' (exécution) !
```

**Problème :** Le fichier n'a pas la permission d'exécution.

---

**Scénario 3 : Impossible d'accéder à un dossier**

```bash
# Tenter d'accéder
cd /some/directory
# bash: cd: /some/directory: Permission denied
```

**Diagnostic :**

```bash
ls -ld /some/directory
# drwxr----- 2 root admin 4096 Dec 30 10:00 /some/directory
#       ^
# Others n'a aucune permission

# Vérifier mon identité
id
# uid=1000(user) gid=1000(user) groups=1000(user)

# Je ne suis ni 'root' ni dans le groupe 'admin'
# Donc je suis 'others' -> aucune permission [X]
```

---

**Scénario 4 : Peut lister mais pas accéder aux fichiers**

```bash
ls some-dir
# file1.txt  file2.txt

cat some-dir/file1.txt
# cat: some-dir/file1.txt: Permission denied
```

**Diagnostic :**

```bash
ls -ld some-dir
# dr--r--r-- 2 user user 4096 Dec 30 10:00 some-dir
#    ^
# 'r' mais pas 'x' !

# On peut lister (r) mais pas traverser (x)
```

---

**Arbre de décision pour diagnostiquer :**

```
1. Qui suis-je ?
   ├─ whoami
   └─ id

2. Qui possède le fichier ?
   └─ ls -l fichier

3. Dans quelle catégorie je tombe ?
   ├─ Owner ?    -> Regarder les 3 premiers caractères (rwx)
   ├─ Group ?    -> Regarder les 3 du milieu (rwx)
   └─ Others ?   -> Regarder les 3 derniers (rwx)

4. Ai-je la permission nécessaire ?
   ├─ Lecture ?   -> Ai-je 'r' ?
   ├─ Écriture ?  -> Ai-je 'w' ?
   └─ Exécution ? -> Ai-je 'x' ?

5. Si c'est un dossier :
   ├─ Ai-je 'x' pour y accéder ?
   └─ Ai-je 'w' pour modifier son contenu ?
```

---

### ÉTAPE 11 : Exercices pratiques d'analyse

**Exercice 1 : Interpréter ces permissions**

```bash
# Créer des fichiers exemples
touch example1.txt example2.txt example3.txt

# Analyser chacun (simulé)
```

**Questions :**

1. `-rwxr-xr-x` (755)
   - Qui peut lire ? `Owner, Group, Others` [OK]
   - Qui peut écrire ? `Owner seulement` [OK]
   - Qui peut exécuter ? `Owner, Group, Others` [OK]

2. `-rw-------` (600)
   - Qui peut lire ? `Owner seulement` [OK]
   - Qui peut écrire ? `Owner seulement` [OK]
   - Qui peut exécuter ? `Personne` [X]

3. `drwxrwx---` (770)
   - Type ? `Dossier` [OK]
   - Owner peut-il créer des fichiers dedans ? `Oui (w+x)` [OK]
   - Group peut-il lister le contenu ? `Oui (r+x)` [OK]
   - Others peut-il y accéder ? `Non` [X]

---

**Exercice 2 : Conversion octal <-> lettres**

**Convertir en octal :**

1. `-rw-rw-r--` -> `664`
   - Owner: rw- = 6
   - Group: rw- = 6
   - Others: r-- = 4

2. `-rwx------` -> `700`
   - Owner: rwx = 7
   - Group: --- = 0
   - Others: --- = 0

3. `drwxr-xr-x` -> `755`
   - Owner: rwx = 7
   - Group: r-x = 5
   - Others: r-x = 5

**Convertir en lettres :**

1. `644` -> `-rw-r--r--`
   - 6 = rw-
   - 4 = r--
   - 4 = r--

2. `777` -> `-rwxrwxrwx`
   - 7 = rwx
   - 7 = rwx
   - 7 = rwx

3. `400` -> `-r--------`
   - 4 = r--
   - 0 = ---
   - 0 = ---

---

### ÉTAPE 12 : Cas d'usage réels

**Cas 1 : Fichier de configuration système**

```bash
ls -l /etc/ssh/sshd_config
# -rw-r--r-- 1 root root 3265 Dec 30 10:00 /etc/ssh/sshd_config
```

**Analyse :**
- **Propriétaire :** root (admin système)
- **Permissions :** 644
  - root peut lire et modifier (rw-)
  - Tous les autres peuvent seulement lire (r--)
- **Pourquoi ?** Config système : root doit pouvoir modifier, utilisateurs peuvent lire pour référence

---

**Cas 2 : Clé SSH privée**

```bash
ls -l ~/.ssh/id_rsa
# -rw------- 1 user user 1679 Dec 30 10:00 /home/user/.ssh/id_rsa
```

**Analyse :**
- **Permissions :** 600
  - Seulement le propriétaire peut lire/écrire
  - Personne d'autre ne peut rien faire
- **Pourquoi ?** Sécurité maximale : clé privée ne doit JAMAIS être accessible aux autres

---

**Cas 3 : Dossier /tmp**

```bash
ls -ld /tmp
# drwxrwxrwt 10 root root 4096 Dec 30 10:00 /tmp
#         ^
# Note le 't' à la fin (sticky bit - on verra plus tard)
```

**Analyse :**
- **Permissions :** 777 (avec sticky bit)
  - Tout le monde peut créer/lire/exécuter
  - Mais seulement le propriétaire peut supprimer ses propres fichiers
- **Pourquoi ?** Zone temporaire partagée

---

**Cas 4 : Exécutable système**

```bash
ls -l /bin/ls
# -rwxr-xr-x 1 root root 133792 Dec 30 10:00 /bin/ls
```

**Analyse :**
- **Permissions :** 755
  - root peut modifier (rw-)
  - Tous peuvent exécuter (r-x)
- **Pourquoi ?** Programme système : tout le monde doit pouvoir l'exécuter, seul root peut le modifier

---

### [OK] TESTS DE VALIDATION

**Test 1 : Lire les permissions**

```bash
# Créer un fichier
echo "test" > test.txt

# Obtenir les permissions
PERMS=$(stat -c "%a" test.txt)

if [ "$PERMS" = "644" ] || [ "$PERMS" = "664" ]; then
    echo "[OK] Test 1 OK : Permissions par défaut correctes"
else
    echo "[ATTENTION]  Permissions inhabituelles : $PERMS"
fi
```

---

**Test 2 : Identifier le propriétaire**

```bash
# Créer un fichier
touch myfile.txt

# Vérifier que je suis le propriétaire
OWNER=$(stat -c "%U" myfile.txt)
ME=$(whoami)

if [ "$OWNER" = "$ME" ]; then
    echo "[OK] Test 2 OK : Je suis le propriétaire"
else
    echo "[X] Test 2 FAIL : Propriétaire = $OWNER, Moi = $ME"
fi
```

---

**Test 3 : Comprendre umask**

```bash
# Obtenir umask
UMASK=$(umask)

# Créer un fichier
touch umask-test.txt
FILE_PERMS=$(stat -c "%a" umask-test.txt)

# Créer un dossier
mkdir umask-test-dir
DIR_PERMS=$(stat -c "%a" umask-test-dir)

echo "umask actuel : $UMASK"
echo "Permissions fichier : $FILE_PERMS"
echo "Permissions dossier : $DIR_PERMS"
echo "[OK] Test 3 OK"
```

---

**Test 4 : Conversion octal/lettres**

```bash
# Test de conversion
test_conversion() {
    local octal=$1
    local expected=$2
    
    # Créer un fichier temporaire avec ces permissions (simulé)
    # On vérifiera dans le prochain exercice avec chmod
    echo "Octal $octal devrait donner : $expected"
}

test_conversion 644 "rw-r--r--"
test_conversion 755 "rwxr-xr-x"
test_conversion 600 "rw-------"
echo "[OK] Test 4 OK"
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Confusion entre fichiers et dossiers

**Symptôme :**
```bash
# Dossier avec permission 644
drw-r--r-- 2 user user 4096 Dec 30 10:00 mydir

# Essayer d'y accéder
cd mydir
# bash: cd: mydir: Permission denied
```

**Cause :** Pas de permission `x` (exécution/traversée) sur le dossier.

**Explication :** Sur les dossiers, `x` est CRUCIAL pour y accéder.

---

#### Erreur 2 : Penser que `r` suffit sur un dossier

**Symptôme :**
```bash
# Dossier lisible mais pas traversable
dr--r--r-- 2 user user 4096 Dec 30 10:00 mydir

ls mydir          # [OK] Fonctionne
cat mydir/file.txt # [X] Permission denied
```

**Cause :** `r` permet de lister, mais sans `x`, on ne peut pas accéder aux fichiers.

---

#### Erreur 3 : Interpréter owner/group/others incorrectement

**Symptôme :**
```bash
# Fichier : -rw-r-----
# Propriétaire : alice
# Groupe : developers
# Je suis bob, membre de developers

cat file.txt
# Permission denied
```

**Diagnostic :**
```bash
# Vérifier mes groupes
groups bob
# bob : bob users

# Je ne suis PAS dans le groupe 'developers'
# Donc je tombe dans 'others' -> aucune permission
```

---

#### Erreur 4 : Oublier que `w` ne supprime pas un fichier

**Symptôme :**
```bash
# Fichier avec w (write)
-rw-r--r-- 1 user user 100 Dec 30 10:00 file.txt

# Essayer de le supprimer
rm file.txt
# Fonctionne... ou pas ?
```

**Explication :** `w` sur le FICHIER permet de modifier son contenu. Pour SUPPRIMER, il faut `w` sur le DOSSIER parent.

```bash
# Vérifier les permissions du dossier
ls -ld .
# drwxr-xr-x 2 user user 4096 Dec 30 10:00 .
#    ^
# Si owner a 'w', je peux supprimer
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Structure des permissions**
- 10 caractères : type + owner + group + others
- Chaque groupe de 3 : r w x
- `-` indique absence de permission

**2. Notation octale**
- r = 4, w = 2, x = 1
- On additionne pour obtenir un chiffre (0-7)
- 3 chiffres : owner, group, others

**3. Différence fichier/dossier**
- **Fichiers :** r=lire, w=modifier, x=exécuter
- **Dossiers :** r=lister, w=créer/supprimer, x=traverser
- Sur dossiers, `x` est ESSENTIEL

**4. Les 3 catégories**
- **Owner (u)** : Propriétaire
- **Group (g)** : Groupe
- **Others (o)** : Tous les autres
- On tombe dans UNE SEULE catégorie (ordre de priorité : owner > group > others)

**5. umask**
- Définit les permissions par défaut
- Fichiers : 666 - umask
- Dossiers : 777 - umask
- umask courant : 0022 (fichiers 644, dossiers 755)

**6. Commandes d'analyse**
- `ls -l` : Affichage détaillé
- `ls -ld` : Pour les dossiers
- `stat` : Informations complètes
- `id` / `whoami` / `groups` : Mon identité

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Permissions spéciales (aperçu - détails dans 3.4)**

```bash
# SUID : Le fichier s'exécute avec les permissions du propriétaire
-rwsr-xr-x  # Note le 's' à la place du 'x' (owner)

# SGID : Le fichier s'exécute avec les permissions du groupe
-rwxr-sr-x  # Note le 's' à la place du 'x' (group)

# Sticky bit : Sur dossiers, seul le propriétaire peut supprimer ses fichiers
drwxrwxrwt  # Note le 't' à la fin
```

---

**2. Vérifier les permissions récursivement**

```bash
# Voir toutes les permissions dans un dossier
find . -type f -ls

# Trouver les fichiers avec permissions spécifiques
find . -type f -perm 644

# Trouver les fichiers trop permissifs
find . -type f -perm -o+w  # Others peut écrire
```

---

**3. Analyser la sécurité**

```bash
# Trouver les fichiers appartenant à un utilisateur
find / -user username 2>/dev/null

# Trouver les fichiers world-writable (dangereux)
find / -type f -perm -002 2>/dev/null

# Trouver les fichiers SUID/SGID (potentiel de sécurité)
find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null
```

---

**4. Calculateur de permissions**

```bash
#!/bin/bash
# perms-calc.sh - Calculer permissions

# Octal vers lettres
octal_to_letters() {
    local num=$1
    local result=""
    
    for digit in $(echo $num | grep -o .); do
        case $digit in
            0) result="${result}---" ;;
            1) result="${result}--x" ;;
            2) result="${result}-w-" ;;
            3) result="${result}-wx" ;;
            4) result="${result}r--" ;;
            5) result="${result}r-x" ;;
            6) result="${result}rw-" ;;
            7) result="${result}rwx" ;;
        esac
    done
    
    echo "-$result"
}

# Utilisation
octal_to_letters 755  # -rwxr-xr-x
octal_to_letters 644  # -rw-r--r--
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3.1

**[OK] Félicitations ! Tu comprends maintenant les permissions Linux !**

**Ce que tu as appris :**
- Lire et interpréter les permissions (`rwxrwxrwx`)
- Notation octale (644, 755, etc.)
- Différence fichier/dossier
- Les 3 catégories (owner, group, others)
- Utiliser `ls -l` et `stat`
- Comprendre `umask`
- Diagnostiquer "Permission denied"

**Compétences acquises :**
- [OK] Analyse de permissions (niveau intermédiaire)
- [OK] Diagnostic de problèmes
- [OK] Compréhension de la sécurité de base

**Temps moyen de réalisation :** 45 min - 1h

**Prochaine étape :** Exercice 3.2 - Modifier les permissions avec `chmod` ! [OUTIL]

---

Je continue avec l'exercice 3.2 ?

# [JAUNE] EXERCICE 3.2 : MODIFIER LES PERMISSIONS AVEC `chmod` (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Configurer les permissions des fichiers de configuration
- Rendre des scripts exécutables
- Sécuriser des fichiers sensibles
- Gérer les permissions de dossiers partagés
- Corriger les problèmes de permissions
- Automatiser la configuration de permissions

### Cahier des charges

- Modifier les permissions avec `chmod`
- Utiliser la notation symbolique (u+x, g-w, etc.)
- Utiliser la notation octale (755, 644, etc.)
- Appliquer récursivement sur des dossiers
- Configurer des permissions appropriées pour différents cas d'usage
- Créer des scripts d'automatisation
- Implémenter des bonnes pratiques de sécurité

### Contraintes

- Commande principale : `chmod`
- Deux modes : symbolique et octal
- Tests sur fichiers et dossiers
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Utiliser `chmod` en mode symbolique
- [OK] Utiliser `chmod` en mode octal
- [OK] Modifier récursivement avec `-R`
- [OK] Ajouter/retirer des permissions spécifiques
- [OK] Configurer des permissions sécurisées
- [OK] Rendre des scripts exécutables
- [OK] Gérer les permissions de dossiers partagés
- [OK] Automatiser avec scripts
- [OK] Diagnostiquer et corriger les problèmes

---

## [DOCS] PRÉREQUIS

- Exercice 3.1 terminé (Comprendre les permissions)
- Compréhension de la notation octale
- Compréhension de rwx

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer l'environnement de test

```bash
# Créer le dossier de travail
mkdir -p ~/chmod-exercise
cd ~/chmod-exercise

# Créer différents types de fichiers et dossiers
mkdir -p {scripts,documents,shared,private,web}

# Scripts
cat > scripts/backup.sh << 'EOF'
#!/bin/bash
echo "Running backup..."
EOF

cat > scripts/deploy.sh << 'EOF'
#!/bin/bash
echo "Deploying application..."
EOF

# Documents
echo "Public document" > documents/readme.txt
echo "Private document" > documents/confidential.txt
echo "Team document" > shared/team-notes.txt

# Configuration
cat > web/config.php << 'EOF'
<?php
define('DB_PASSWORD', 'secret123');
?>
EOF

# Fichiers de données
echo "user1:password1" > private/passwords.txt
echo "API_KEY=abc123" > private/secrets.env

# Afficher l'état initial
ls -la scripts/
ls -la documents/
```

---

### ÉTAPE 2 : `chmod` - Syntaxe de base

**Syntaxe générale :**

```
chmod [OPTIONS] MODE FICHIER
```

**Deux modes principaux :**

1. **Mode symbolique** : `u+x`, `go-w`, `a+r`
2. **Mode octal** : `755`, `644`, `600`

---

### ÉTAPE 3 : Mode SYMBOLIQUE - Syntaxe

**Structure du mode symbolique :**

```
[catégorie][opération][permission]
```

**Catégories (qui) :**

| Symbole | Signification | Description |
|---------|---------------|-------------|
| `u` | User | Propriétaire (owner) |
| `g` | Group | Groupe |
| `o` | Others | Autres |
| `a` | All | Tous (u+g+o) |

**Opérations (quoi faire) :**

| Symbole | Signification | Description |
|---------|---------------|-------------|
| `+` | Ajouter | Ajoute la permission |
| `-` | Retirer | Retire la permission |
| `=` | Définir exactement | Définit les permissions exactes (écrase les autres) |

**Permissions (quelles permissions) :**

| Symbole | Signification |
|---------|---------------|
| `r` | Read (lecture) |
| `w` | Write (écriture) |
| `x` | Execute (exécution) |

---

### ÉTAPE 4 : Mode symbolique - Exemples basiques

**Test 1 : Ajouter la permission d'exécution pour le propriétaire**

```bash
cd ~/chmod-exercise

# Voir les permissions actuelles
ls -l scripts/backup.sh
# -rw-r--r-- 1 user user 35 Dec 30 10:00 backup.sh

# Ajouter exécution pour owner
chmod u+x scripts/backup.sh

# Vérifier
ls -l scripts/backup.sh
# -rwxr--r-- 1 user user 35 Dec 30 10:00 backup.sh
#    ^
# Maintenant le owner a 'x'
```

**Explication :**
- `u` = User (propriétaire)
- `+` = Ajouter
- `x` = Exécution

**Résultat :** Le script est maintenant exécutable !

```bash
# Tester l'exécution
./scripts/backup.sh
# Running backup...
```

---

**Test 2 : Retirer la permission d'écriture pour group et others**

```bash
# Voir les permissions
ls -l documents/readme.txt
# -rw-r--r-- 1 user user 16 Dec 30 10:00 readme.txt

# Retirer write pour group et others
chmod go-w documents/readme.txt

# Vérifier
ls -l documents/readme.txt
# -rw-r--r-- 1 user user 16 Dec 30 10:00 readme.txt
```

**Note :** Dans ce cas, ils n'avaient déjà pas `w`, donc rien ne change visuellement, mais la commande fonctionne.

---

**Test 3 : Ajouter lecture pour tous**

```bash
# Fichier actuellement limité
echo "info" > test.txt

# Ajouter lecture pour tous
chmod a+r test.txt

# Vérifier
ls -l test.txt
# -rw-r--r-- 1 user user 5 Dec 30 10:00 test.txt
```

**`a+r` = All + read (tous peuvent lire)**

---

**Test 4 : Définir les permissions exactes avec `=`**

```bash
# État actuel quelconque
ls -l documents/confidential.txt
# -rw-r--r--

# Définir exactement : owner peut lire/écrire, rien pour les autres
chmod u=rw,go= documents/confidential.txt

# Vérifier
ls -l documents/confidential.txt
# -rw------- 1 user user 18 Dec 30 10:00 confidential.txt
```

**Explication :**
- `u=rw` : Owner a seulement read et write
- `go=` : Group et Others n'ont RIEN (= vide)

---

**Test 5 : Combiner plusieurs opérations**

```bash
# Multiples changements en une commande
chmod u+x,g+w,o-r scripts/deploy.sh

# Vérifier
ls -l scripts/deploy.sh
# -rwxrw---- 1 user user 42 Dec 30 10:00 deploy.sh
```

**Explication :**
- `u+x` : Owner peut exécuter
- `g+w` : Group peut écrire
- `o-r` : Others ne peut pas lire

**Séparateur : Virgules (`,`) sans espaces**

---

### ÉTAPE 5 : Mode OCTAL - Rappel et utilisation

**Notation octale complète :**

```
chmod 755 fichier
      vvv
      │││
      ││└─ Others: 5 (r-x) = 4+0+1
      │└── Group:  5 (r-x) = 4+0+1
      └─── Owner:  7 (rwx) = 4+2+1
```

---

**Test 1 : Permissions courantes sur fichiers**

```bash
# Fichier texte classique : 644
chmod 644 documents/readme.txt
ls -l documents/readme.txt
# -rw-r--r--

# Script exécutable : 755
chmod 755 scripts/backup.sh
ls -l scripts/backup.sh
# -rwxr-xr-x

# Fichier privé : 600
chmod 600 private/passwords.txt
ls -l private/passwords.txt
# -rw-------

# Fichier confidentiel lecture seule : 400
chmod 400 private/secrets.env
ls -l private/secrets.env
# -r--------
```

---

**Test 2 : Permissions courantes sur dossiers**

```bash
# Dossier personnel : 700
chmod 700 private/
ls -ld private/
# drwx------

# Dossier public : 755
chmod 755 documents/
ls -ld documents/
# drwxr-xr-x

# Dossier de groupe : 775
chmod 775 shared/
ls -ld shared/
# drwxrwxr-x

# Dossier web (lecture seule pour others) : 750
chmod 750 web/
ls -ld web/
# drwxr-x---
```

---

**Tableau de référence rapide :**

| Permissions | Octal | Usage typique |
|-------------|-------|---------------|
| `-rw-------` | 600 | Fichiers privés (clés SSH, passwords) |
| `-rw-r--r--` | 644 | Fichiers texte, config, données |
| `-rw-rw-r--` | 664 | Fichiers partagés en groupe |
| `-rwx------` | 700 | Scripts privés |
| `-rwxr-xr-x` | 755 | Scripts, binaires publics |
| `-rwxrwx---` | 770 | Scripts de groupe |
| `drwx------` | 700 | Dossiers privés |
| `drwxr-xr-x` | 755 | Dossiers publics |
| `drwxrwxr-x` | 775 | Dossiers de groupe |
| `drwxrwxrwx` | 777 | [ATTENTION] À éviter (trop permissif) |

---

### ÉTAPE 6 : Option `-R` - Modification récursive

**Modifier tous les fichiers et sous-dossiers d'un coup.**

**Test 1 : Récursif basique**

```bash
# Créer une structure
mkdir -p project/{src,tests,docs}
touch project/src/{main.py,utils.py}
touch project/tests/{test1.py,test2.py}
touch project/docs/README.md

# Voir l'état actuel
ls -lR project/

# Appliquer 755 à tout récursivement
chmod -R 755 project/

# Vérifier
ls -lR project/
# Tout est maintenant -rwxr-xr-x (fichiers) ou drwxr-xr-x (dossiers)
```

**`-R` = Recursive (récursif)**

---

**Test 2 : Problème courant - Fichiers et dossiers traités pareil**

```bash
# Appliquer 644 récursivement
chmod -R 644 project/

# Vérifier
ls -ld project/
# drw-r--r-- 2 user user 4096 Dec 30 10:00 project/
#    ^
# Problème : Le dossier n'a plus 'x' !

# Essayer d'y accéder
cd project/
# bash: cd: project/: Permission denied
```

**Problème :** `chmod -R 644` a retiré le `x` des dossiers, les rendant inaccessibles !

---

**Solution : Traiter dossiers et fichiers séparément**

```bash
# Méthode 1 : Avec find (recommandé)

# Dossiers : 755
find project/ -type d -exec chmod 755 {} \;

# Fichiers : 644
find project/ -type f -exec chmod 644 {} \;

# Vérifier
ls -lR project/
# Dossiers : drwxr-xr-x
# Fichiers : -rw-r--r--
```

**Méthode 2 : Ajouter sélectivement avec le mode symbolique**

```bash
# Appliquer 644 à tout
chmod -R 644 project/

# Ajouter X (majuscule) seulement pour les dossiers
chmod -R u+X,g+X,o+X project/
```

**`X` (majuscule) = Ajoute `x` seulement aux dossiers et aux fichiers déjà exécutables**

---

**Test 3 : Script pour permissions récursives sécurisées**

```bash
#!/bin/bash
# fix-permissions.sh

DIR=$1

if [ -z "$DIR" ]; then
    echo "Usage: $0 <directory>"
    exit 1
fi

echo "Fixing permissions for $DIR..."

# Dossiers : 755 (rwxr-xr-x)
find "$DIR" -type d -exec chmod 755 {} \;
echo "[OK] Dossiers: 755"

# Fichiers : 644 (rw-r--r--)
find "$DIR" -type f -exec chmod 644 {} \;
echo "[OK] Fichiers: 644"

# Scripts (.sh) : 755 (rwxr-xr-x)
find "$DIR" -type f -name "*.sh" -exec chmod 755 {} \;
echo "[OK] Scripts .sh: 755"

echo "[OK] Done!"
```

**Utilisation :**

```bash
chmod +x fix-permissions.sh
./fix-permissions.sh project/
```

---

### ÉTAPE 7 : Cas d'usage professionnels

**Cas 1 : Configurer un projet web**

```bash
# Structure web typique
mkdir -p webapp/{public,config,storage,scripts}
touch webapp/public/index.html
touch webapp/config/database.php
touch webapp/storage/app.log
touch webapp/scripts/deploy.sh

# Configuration de sécurité

# 1. Dossiers
chmod 755 webapp/                    # Dossier principal
chmod 755 webapp/public/             # Accessible au web
chmod 750 webapp/config/             # Config : pas d'accès others
chmod 770 webapp/storage/            # Logs : groupe peut écrire
chmod 755 webapp/scripts/            # Scripts

# 2. Fichiers publics
chmod 644 webapp/public/index.html   # Lisible par le serveur web

# 3. Fichiers de config (sensibles)
chmod 640 webapp/config/database.php # Owner r+w, Group r, Others rien

# 4. Logs (écriture par l'application)
chmod 660 webapp/storage/app.log     # Owner+Group peuvent écrire

# 5. Scripts
chmod 750 webapp/scripts/deploy.sh   # Exécutable, pas par others

# Vérifier
ls -lR webapp/
```

---

**Cas 2 : Sécuriser les clés SSH**

```bash
# Créer structure SSH
mkdir -p ~/.ssh
cd ~/.ssh

# Générer une clé (simulation)
ssh-keygen -t rsa -f id_rsa -N ""

# Permissions CRITIQUES pour SSH

# Dossier .ssh : 700
chmod 700 ~/.ssh

# Clé privée : 600 (TRÈS IMPORTANT !)
chmod 600 ~/.ssh/id_rsa

# Clé publique : 644
chmod 644 ~/.ssh/id_rsa.pub

# Fichier authorized_keys : 600
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

# Fichier config : 600
touch ~/.ssh/config
chmod 600 ~/.ssh/config

# Vérifier
ls -la ~/.ssh/
```

**[ATTENTION] CRITIQUE : Si les permissions ne sont pas exactes, SSH refusera de fonctionner !**

```bash
# Test avec de mauvaises permissions
chmod 644 ~/.ssh/id_rsa

# Essayer d'utiliser la clé
ssh -i ~/.ssh/id_rsa user@server
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# @         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# Permissions 0644 for 'id_rsa' are too open.
# It is required that your private key files are NOT accessible by others.
```

---

**Cas 3 : Dossier partagé pour une équipe**

```bash
# Créer un dossier de projet d'équipe
mkdir -p /shared/team-project

# Configuration
# Propriétaire : chef-projet
# Groupe : developers
# Others : lecture seule

# Permissions : 775
chmod 775 /shared/team-project

# Signification :
# - Chef et développeurs peuvent tout faire (rwx)
# - Others peuvent lire et naviguer (r-x)

# Pour les fichiers à l'intérieur
chmod 664 /shared/team-project/*.txt
# - Chef et développeurs peuvent éditer
# - Others peuvent lire
```

---

**Cas 4 : Scripts de backup automatique**

```bash
# Créer un script de backup
cat > /usr/local/bin/daily-backup.sh << 'EOF'
#!/bin/bash
# Backup automatique

BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d)

tar -czf "$BACKUP_DIR/backup-$DATE.tar.gz" /var/www/
EOF

# Permissions du script
chmod 750 /usr/local/bin/daily-backup.sh
# Owner (root) peut exécuter
# Group peut exécuter
# Others ne peut rien faire

# Le dossier de backups
chmod 700 /backups/
# Seulement root peut accéder
```

---

**Cas 5 : Fichier de log partagé**

```bash
# Log accessible par plusieurs services
touch /var/log/app.log

# Permissions : 666 (ou mieux, 664 avec bon groupe)
chmod 666 /var/log/app.log
# Tout le monde peut écrire

# Ou plus sûr :
# Créer un groupe 'loggers'
# chgrp loggers /var/log/app.log
# chmod 664 /var/log/app.log
# Seulement owner et groupe 'loggers' peuvent écrire
```

---

### ÉTAPE 8 : Scripts d'automatisation

**Script 1 : Configurer un projet web automatiquement**

```bash
#!/bin/bash
# setup-web-project.sh

PROJECT_DIR=$1

if [ -z "$PROJECT_DIR" ]; then
    echo "Usage: $0 <project-directory>"
    exit 1
fi

echo "[RAPIDE] Configuration des permissions pour $PROJECT_DIR..."

# Vérifier que le dossier existe
if [ ! -d "$PROJECT_DIR" ]; then
    echo "[X] Erreur : $PROJECT_DIR n'existe pas"
    exit 1
fi

# Dossiers
echo "[DOSSIER] Configuration des dossiers..."
find "$PROJECT_DIR" -type d -exec chmod 755 {} \;

# Fichiers généraux
echo "[FICHIER] Configuration des fichiers..."
find "$PROJECT_DIR" -type f -exec chmod 644 {} \;

# Scripts (.sh)
echo "[OUTIL] Configuration des scripts..."
find "$PROJECT_DIR" -type f -name "*.sh" -exec chmod 755 {} \;

# Fichiers sensibles (config, env)
echo "[VERROUILLE] Sécurisation des fichiers sensibles..."
find "$PROJECT_DIR" -type f \( -name "*.env" -o -name "*config.php" -o -name "*secrets*" \) -exec chmod 600 {} \;

# Dossiers storage/cache (écriture)
echo "[SAUVEGARDE] Configuration des dossiers de stockage..."
for dir in storage cache uploads logs; do
    if [ -d "$PROJECT_DIR/$dir" ]; then
        chmod 770 "$PROJECT_DIR/$dir"
        echo "  [OK] $dir: 770"
    fi
done

echo ""
echo "[OK] Configuration terminée !"
echo ""
echo "Résumé :"
echo "  - Dossiers : 755 (drwxr-xr-x)"
echo "  - Fichiers : 644 (-rw-r--r--)"
echo "  - Scripts : 755 (-rwxr-xr-x)"
echo "  - Sensibles : 600 (-rw-------)"
echo "  - Storage : 770 (drwxrwx---)"
```

**Utilisation :**

```bash
chmod +x setup-web-project.sh
./setup-web-project.sh /var/www/myapp
```

---

**Script 2 : Audit de sécurité des permissions**

```bash
#!/bin/bash
# security-audit.sh

echo "[RECHERCHE] Audit de sécurité des permissions"
echo "===================================="
echo ""

# 1. Fichiers world-writable (dangereux)
echo "[ATTENTION]  Fichiers accessibles en écriture par tous :"
find / -type f -perm -o+w 2>/dev/null | head -10
echo ""

# 2. Dossiers world-writable sans sticky bit (dangereux)
echo "[ATTENTION]  Dossiers accessibles en écriture par tous (sans sticky bit) :"
find / -type d -perm -o+w ! -perm -1000 2>/dev/null | head -10
echo ""

# 3. Fichiers sans propriétaire (problème)
echo "[ATTENTION]  Fichiers sans propriétaire valide :"
find / -nouser 2>/dev/null | head -10
echo ""

# 4. Fichiers SUID/SGID (potentiel de sécurité)
echo "[SECURISE] Fichiers avec SUID/SGID :"
find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | head -10
echo ""

# 5. Fichiers .ssh avec mauvaises permissions
echo "[CLE] Vérification des clés SSH :"
for user_home in /home/*; do
    if [ -d "$user_home/.ssh" ]; then
        user=$(basename "$user_home")
        
        # Vérifier .ssh directory
        ssh_perms=$(stat -c "%a" "$user_home/.ssh" 2>/dev/null)
        if [ "$ssh_perms" != "700" ]; then
            echo "  [ATTENTION]  $user: .ssh est $ssh_perms (devrait être 700)"
        fi
        
        # Vérifier clés privées
        for key in "$user_home/.ssh"/id_*; do
            if [ -f "$key" ] && [[ "$key" != *.pub ]]; then
                key_perms=$(stat -c "%a" "$key" 2>/dev/null)
                if [ "$key_perms" != "600" ]; then
                    echo "  [ATTENTION]  $user: $(basename $key) est $key_perms (devrait être 600)"
                fi
            fi
        done
    fi
done
echo ""

echo "[OK] Audit terminé"
```

---

**Script 3 : Réparer les permissions standard d'un projet**

```bash
#!/bin/bash
# fix-standard-permissions.sh

set -e

# Couleurs
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

PROJECT_DIR=$1

if [ -z "$PROJECT_DIR" ]; then
    echo -e "${RED}Usage: $0 <project-directory>${NC}"
    exit 1
fi

if [ ! -d "$PROJECT_DIR" ]; then
    echo -e "${RED}Erreur : $PROJECT_DIR n'existe pas${NC}"
    exit 1
fi

echo -e "${GREEN}[OUTIL] Réparation des permissions pour $PROJECT_DIR${NC}"
echo ""

# Compteurs
dirs_fixed=0
files_fixed=0
scripts_fixed=0

# Dossiers
echo -e "${YELLOW}[DOSSIER] Réparation des dossiers...${NC}"
while IFS= read -r dir; do
    current_perms=$(stat -c "%a" "$dir")
    if [ "$current_perms" != "755" ]; then
        chmod 755 "$dir"
        ((dirs_fixed++))
    fi
done < <(find "$PROJECT_DIR" -type d)
echo -e "${GREEN}  [OK] $dirs_fixed dossiers réparés${NC}"

# Fichiers
echo -e "${YELLOW}[FICHIER] Réparation des fichiers...${NC}"
while IFS= read -r file; do
    current_perms=$(stat -c "%a" "$file")
    if [ "$current_perms" != "644" ] && [[ ! "$file" =~ \.sh$ ]]; then
        chmod 644 "$file"
        ((files_fixed++))
    fi
done < <(find "$PROJECT_DIR" -type f)
echo -e "${GREEN}  [OK] $files_fixed fichiers réparés${NC}"

# Scripts
echo -e "${YELLOW}[OUTIL] Réparation des scripts...${NC}"
while IFS= read -r script; do
    current_perms=$(stat -c "%a" "$script")
    if [ "$current_perms" != "755" ]; then
        chmod 755 "$script"
        ((scripts_fixed++))
    fi
done < <(find "$PROJECT_DIR" -type f -name "*.sh")
echo -e "${GREEN}  [OK] $scripts_fixed scripts réparés${NC}"

echo ""
echo -e "${GREEN}[OK] Réparation terminée !${NC}"
echo ""
echo "Résumé :"
echo "  - Dossiers réparés : $dirs_fixed"
echo "  - Fichiers réparés : $files_fixed"
echo "  - Scripts réparés : $scripts_fixed"
```

---

### ÉTAPE 9 : Comparaison mode symbolique vs octal

**Quand utiliser quoi ?**

| Situation | Mode recommandé | Exemple |
|-----------|-----------------|---------|
| Ajouter une permission | Symbolique | `chmod u+x script.sh` |
| Retirer une permission | Symbolique | `chmod go-w file.txt` |
| Permissions exactes connues | Octal | `chmod 644 file.txt` |
| Configuration standard | Octal | `chmod 755 dir/` |
| Modification partielle | Symbolique | `chmod g+w file.txt` |
| Scripts automatisés | Octal | Plus court, plus clair |
| Tutoriels, documentation | Octal | Plus universel |

---

**Équivalences :**

```bash
# Rendre exécutable (pour owner)
chmod u+x script.sh    # Symbolique
chmod 755 script.sh    # Octal (si départ de 644)

# Fichier privé
chmod go-rwx file.txt  # Symbolique
chmod 600 file.txt     # Octal

# Lecture pour tous
chmod a+r file.txt     # Symbolique
chmod 644 file.txt     # Octal (avec write pour owner)

# Dossier partagé
chmod ug+rwx,o+rx dir/ # Symbolique
chmod 775 dir/         # Octal
```

---

### ÉTAPE 10 : Bonnes pratiques de sécurité

**1. Principe du moindre privilège**

```bash
# [X] Mauvais : Tout pour tout le monde
chmod 777 file.txt

# [OK] Bon : Seulement ce qui est nécessaire
chmod 644 file.txt  # Owner peut modifier, autres lisent
```

---

**2. Jamais 777 sur les fichiers de production**

```bash
# [X] DANGER : Tout le monde peut modifier
chmod 777 /var/www/config.php

# [OK] Correct
chmod 640 /var/www/config.php
# Owner et groupe peuvent lire/écrire
# Others ne peut rien faire
```

---

**3. Protéger les fichiers sensibles**

```bash
# Fichiers de mots de passe, clés, secrets
chmod 600 /etc/app/secrets.env
chmod 600 ~/.ssh/id_rsa
chmod 600 /root/.mysql_history

# Dossiers de configuration
chmod 750 /etc/app/
```

---

**4. Scripts exécutables : 755 ou 750**

```bash
# Scripts publics (dans /usr/local/bin)
chmod 755 /usr/local/bin/mon-script.sh

# Scripts admin (dans /root ou /opt)
chmod 750 /root/admin-script.sh
# Seulement root et son groupe
```

---

**5. Dossiers web : attention aux uploads**

```bash
# Dossier d'uploads utilisateurs
mkdir /var/www/uploads
chmod 755 /var/www/uploads  # Pas 777 !

# Le serveur web doit pouvoir y écrire
# Solution : changer le groupe, pas les permissions others
# chgrp www-data /var/www/uploads
# chmod 775 /var/www/uploads
```

---

**6. Vérifier après modification**

```bash
# Toujours vérifier après chmod
chmod 755 script.sh
ls -l script.sh
# -rwxr-xr-x [OK]

# Tester que ça fonctionne
./script.sh
```

---

### ÉTAPE 11 : Résolution de problèmes courants

**Problème 1 : Script non exécutable**

**Symptôme :**
```bash
./script.sh
# bash: ./script.sh: Permission denied
```

**Diagnostic :**
```bash
ls -l script.sh
# -rw-r--r-- 1 user user 100 Dec 30 10:00 script.sh
#    ^
# Pas de 'x'
```

**Solution :**
```bash
chmod +x script.sh
# Ou
chmod 755 script.sh
```

---

**Problème 2 : Dossier inaccessible après chmod récursif**

**Symptôme :**
```bash
chmod -R 644 mydir/
cd mydir/
# bash: cd: mydir/: Permission denied
```

**Diagnostic :**
```bash
ls -ld mydir/
# drw-r--r-- 2 user user 4096 Dec 30 10:00 mydir/
#    ^
# Pas de 'x' sur le dossier !
```

**Solution :**
```bash
# Ajouter x pour les dossiers
chmod -R u+X mydir/
# X majuscule = ajoute x seulement aux dossiers

# Ou refaire proprement
find mydir/ -type d -exec chmod 755 {} \;
find mydir/ -type f -exec chmod 644 {} \;
```

---

**Problème 3 : SSH refuse la connexion**

**Symptôme :**
```bash
ssh -i ~/.ssh/id_rsa server
# WARNING: UNPROTECTED PRIVATE KEY FILE!
# Permissions 0644 for 'id_rsa' are too open.
```

**Solution :**
```bash
# Permissions strictes sur les clés SSH
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/config
```

---

**Problème 4 : Serveur web ne peut pas écrire**

**Symptôme :**
```bash
# Application web essaie d'écrire dans uploads/
# Erreur : Permission denied
```

**Diagnostic :**
```bash
ls -ld /var/www/uploads
# drwxr-xr-x 2 root root 4096 Dec 30 10:00 uploads/
# Le serveur web (www-data) est dans 'others' -> pas de 'w'
```

**Solutions possibles :**

```bash
# Option 1 : Changer le propriétaire (préféré)
sudo chown www-data:www-data /var/www/uploads

# Option 2 : Ajouter write pour le groupe
sudo chgrp www-data /var/www/uploads
sudo chmod 775 /var/www/uploads

# Option 3 : 777 (NON RECOMMANDÉ sauf temporairement pour debug)
sudo chmod 777 /var/www/uploads  # [ATTENTION] Dangereux !
```

---

**Problème 5 : "Operation not permitted" même avec bonnes permissions**

**Symptôme :**
```bash
chmod 755 /usr/bin/sudo
# chmod: changing permissions of '/usr/bin/sudo': Operation not permitted
```

**Cause :** Attributs de fichier protégés (immutable).

**Diagnostic :**
```bash
lsattr /usr/bin/sudo
# ----i---------e----- /usr/bin/sudo
#     ^
# Attribut 'i' (immutable)
```

**Solution :**
```bash
# Retirer l'attribut immutable (root requis)
sudo chattr -i /usr/bin/sudo

# Maintenant chmod fonctionne
sudo chmod 755 /usr/bin/sudo

# Remettre immutable si nécessaire
sudo chattr +i /usr/bin/sudo
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : chmod symbolique fonctionne**

```bash
# Créer un fichier
touch test-symbolic.txt
chmod 644 test-symbolic.txt

# Ajouter exécution
chmod u+x test-symbolic.txt

# Vérifier
PERMS=$(stat -c "%a" test-symbolic.txt)
if [ "$PERMS" = "744" ]; then
    echo "[OK] Test 1 OK : chmod u+x fonctionne"
else
    echo "[X] Test 1 FAIL : Attendu 744, obtenu $PERMS"
fi

rm test-symbolic.txt
```

---

**Test 2 : chmod octal fonctionne**

```bash
# Créer un fichier
touch test-octal.txt

# Appliquer 755
chmod 755 test-octal.txt

# Vérifier
PERMS=$(stat -c "%a" test-octal.txt)
if [ "$PERMS" = "755" ]; then
    echo "[OK] Test 2 OK : chmod 755 fonctionne"
else
    echo "[X] Test 2 FAIL : Attendu 755, obtenu $PERMS"
fi

rm test-octal.txt
```

---

**Test 3 : chmod -R fonctionne**

```bash
# Créer structure
mkdir -p test-recursive/{dir1,dir2}
touch test-recursive/file.txt
touch test-recursive/dir1/file1.txt

# Appliquer récursivement
chmod -R 755 test-recursive/

# Vérifier un fichier et un dossier
FILE_PERMS=$(stat -c "%a" test-recursive/file.txt)
DIR_PERMS=$(stat -c "%a" test-recursive/dir1)

if [ "$FILE_PERMS" = "755" ] && [ "$DIR_PERMS" = "755" ]; then
    echo "[OK] Test 3 OK : chmod -R fonctionne"
else
    echo "[X] Test 3 FAIL : File=$FILE_PERMS, Dir=$DIR_PERMS"
fi

rm -rf test-recursive/
```

---

**Test 4 : Script devient exécutable**

```bash
# Créer un script
cat > test-script.sh << 'EOF'
#!/bin/bash
echo "Success"
EOF

# Rendre exécutable
chmod +x test-script.sh

# Tester l'exécution
OUTPUT=$(./test-script.sh)

if [ "$OUTPUT" = "Success" ]; then
    echo "[OK] Test 4 OK : Script exécutable"
else
    echo "[X] Test 4 FAIL"
fi

rm test-script.sh
```

---

**Test 5 : Permissions par défaut avec umask**

```bash
# Sauvegarder umask actuel
OLD_UMASK=$(umask)

# Définir umask connu
umask 0022

# Créer un fichier
touch test-umask.txt
FILE_PERMS=$(stat -c "%a" test-umask.txt)

# Créer un dossier
mkdir test-umask-dir
DIR_PERMS=$(stat -c "%a" test-umask-dir)

# Vérifier
if [ "$FILE_PERMS" = "644" ] && [ "$DIR_PERMS" = "755" ]; then
    echo "[OK] Test 5 OK : umask 0022 donne 644/755"
else
    echo "[ATTENTION]  Test 5 : File=$FILE_PERMS, Dir=$DIR_PERMS"
fi

# Restaurer umask
umask $OLD_UMASK

# Nettoyer
rm test-umask.txt
rmdir test-umask-dir
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Syntaxe symbolique incorrecte

**Symptôme :**
```bash
chmod u +x file.txt
# chmod: invalid mode: '+x'
```

**Cause :** Espace entre `u` et `+`.

**Solution :**
```bash
# Pas d'espace !
chmod u+x file.txt
```

---

#### Erreur 2 : Oublier -R pour les dossiers

**Symptôme :**
```bash
chmod 755 mydir/
# Seulement le dossier est modifié, pas son contenu
```

**Solution :**
```bash
# Utiliser -R pour récursif
chmod -R 755 mydir/
```

---

#### Erreur 3 : 777 sur tout par paresse

**Symptôme :**
```bash
chmod -R 777 /var/www/
# [ATTENTION] TRÈS DANGEREUX !
```

**Problème :**
- N'importe qui peut modifier les fichiers
- Faille de sécurité majeure
- Mauvaise pratique

**Solution :**
```bash
# Permissions appropriées
find /var/www/ -type d -exec chmod 755 {} \;
find /var/www/ -type f -exec chmod 644 {} \;
```

---

#### Erreur 4 : chmod sur un lien symbolique

**Symptôme :**
```bash
ln -s /path/to/file link
chmod 644 link
# Modifie le fichier cible, pas le lien !
```

**Explication :** Par défaut, `chmod` suit les liens et modifie la cible.

**Solution :**
```bash
# Les permissions d'un lien symbolique sont toujours 777
# et ne comptent pas
ls -l link
# lrwxrwxrwx ... link -> /path/to/file

# Pour modifier, il faut chmod sur la cible
chmod 644 /path/to/file
```

---

#### Erreur 5 : Permissions insuffisantes pour chmod

**Symptôme :**
```bash
chmod 755 /etc/passwd
# chmod: changing permissions of '/etc/passwd': Operation not permitted
```

**Cause :** Pas propriétaire du fichier.

**Solution :**
```bash
# Utiliser sudo si nécessaire
sudo chmod 644 /etc/passwd

# Ou devenir propriétaire d'abord
sudo chown $(whoami) fichier
chmod 644 fichier
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Deux modes de chmod**
- **Symbolique** : `u+x`, `go-w`, `a=r`
- **Octal** : `755`, `644`, `600`

**2. Mode symbolique**
- Catégories : `u` (user), `g` (group), `o` (others), `a` (all)
- Opérations : `+` (ajouter), `-` (retirer), `=` (définir exactement)
- Permissions : `r`, `w`, `x`

**3. Mode octal**
- 3 chiffres : owner, group, others
- Chaque chiffre : somme de r(4), w(2), x(1)
- Exemples courants : 644, 755, 700, 600

**4. Option -R**
- Récursif : modifie tout le contenu
- Attention : traite fichiers et dossiers pareil
- Mieux : utiliser `find` pour traiter séparément

**5. Permissions courantes**
- Fichiers texte : 644
- Scripts : 755
- Fichiers privés : 600
- Dossiers publics : 755
- Dossiers privés : 700

**6. Sécurité**
- Principe du moindre privilège
- Jamais 777 en production
- Protéger les fichiers sensibles (600)
- Vérifier après modification

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Permissions ACL (Access Control Lists)**

```bash
# Plus fin que rwx traditionnel
# Permet des permissions par utilisateur

# Installer
sudo apt install acl

# Donner accès à un utilisateur spécifique
setfacl -m u:alice:rw file.txt

# Voir les ACL
getfacl file.txt
```

---

**2. Permissions avec capabilities**

```bash
# Alternative à SUID pour certains programmes
# Plus sécurisé

# Donner capability à un programme
sudo setcap cap_net_bind_service=+ep /usr/bin/python3.8

# Voir les capabilities
getcap /usr/bin/python3.8
```

---

**3. Monitoring des changements de permissions**

```bash
# Surveiller avec auditd
sudo apt install auditd

# Ajouter règle
sudo auditctl -w /etc/ -p wa -k config_changes

# Voir les logs
sudo ausearch -k config_changes
```

---

**4. Restaurer les permissions par défaut**

```bash
# Debian/Ubuntu a un fichier de permissions par défaut
sudo apt install --reinstall $(dpkg -S /etc/passwd | cut -d: -f1)

# Ou utiliser getfacl/setfacl pour backup/restore
getfacl -R /etc > /backup/etc-permissions.txt
setfacl --restore=/backup/etc-permissions.txt
```

---

**5. Calculateur de permissions interactif**

```bash
#!/bin/bash
# chmod-calculator.sh

echo "Calculateur de permissions chmod"
echo "================================"
echo ""

read -p "Owner peut lire ? (y/n) " or
read -p "Owner peut écrire ? (y/n) " ow
read -p "Owner peut exécuter ? (y/n) " ox

read -p "Group peut lire ? (y/n) " gr
read -p "Group peut écrire ? (y/n) " gw
read -p "Group peut exécuter ? (y/n) " gx

read -p "Others peut lire ? (y/n) " ar
read -p "Others peut écrire ? (y/n) " aw
read -p "Others peut exécuter ? (y/n) " ax

# Calculer
calc_digit() {
    local r=$1
    local w=$2
    local x=$3
    local sum=0
    
    [[ "$r" == "y" ]] && ((sum+=4))
    [[ "$w" == "y" ]] && ((sum+=2))
    [[ "$x" == "y" ]] && ((sum+=1))
    
    echo $sum
}

OWNER=$(calc_digit $or $ow $ox)
GROUP=$(calc_digit $gr $gw $gx)
OTHERS=$(calc_digit $ar $aw $ax)

echo ""
echo "Résultat :"
echo "  Octal : $OWNER$GROUP$OTHERS"
echo "  Commande : chmod $OWNER$GROUP$OTHERS fichier"
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3.2

**[OK] Félicitations ! Tu maîtrises maintenant chmod !**

**Ce que tu as appris :**
- Modifier les permissions avec chmod
- Mode symbolique (u+x, go-w, etc.)
- Mode octal (755, 644, etc.)
- Modification récursive (-R)
- Cas d'usage professionnels
- Sécurisation de fichiers et dossiers
- Scripts d'automatisation
- Résolution de problèmes

**Compétences acquises :**
- [OK] Gestion des permissions (niveau avancé)
- [OK] Sécurisation de systèmes
- [OK] Automatisation
- [OK] Bonnes pratiques

**Temps moyen de réalisation :** 1h30 - 2h

**Prochaine étape :** Exercice 3.3 - Changer propriétaire et groupe avec `chown` et `chgrp` ! [UTILISATEURS]

---

Je continue avec l'exercice 3.3 ?

# [JAUNE] EXERCICE 3.3 : CHANGER PROPRIÉTAIRE ET GROUPE AVEC `chown` ET `chgrp` (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois gérer quotidiennement :
- Les fichiers de plusieurs utilisateurs et équipes
- Les applications web qui nécessitent des propriétaires spécifiques (www-data, nginx)
- Les bases de données avec leurs propres utilisateurs (mysql, postgres)
- Les dossiers partagés entre équipes
- Les migrations de fichiers entre serveurs
- Les restaurations de backups avec mauvaises propriétés

**Problématique :** Les permissions seules ne suffisent pas. Il faut aussi contrôler QUI possède les fichiers.

### Cahier des charges

- Comprendre la notion de propriétaire (owner) et groupe
- Changer le propriétaire d'un fichier/dossier
- Changer le groupe d'un fichier/dossier
- Changer les deux simultanément
- Appliquer récursivement
- Gérer les utilisateurs et groupes système
- Configurer des applications (web, bases de données)
- Sécuriser l'accès multi-utilisateurs

### Contraintes

- Commandes : `chown`, `chgrp`, `id`, `groups`, `getent`
- Nécessite souvent `sudo` (permissions root)
- Comprendre les utilisateurs système
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre owner et group
- [OK] Utiliser `chown` pour changer le propriétaire
- [OK] Utiliser `chgrp` pour changer le groupe
- [OK] Changer owner et group simultanément
- [OK] Appliquer récursivement avec `-R`
- [OK] Gérer les utilisateurs et groupes
- [OK] Configurer des serveurs web et BDD
- [OK] Résoudre les problèmes de propriété
- [OK] Automatiser avec scripts

---

## [DOCS] PRÉREQUIS

- Exercices 3.1 et 3.2 terminés
- Compréhension des permissions
- Accès root ou sudo

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre les utilisateurs et groupes Linux

**Chaque fichier a DEUX attributs de propriété :**

1. **Owner (Propriétaire)** : Un utilisateur
2. **Group (Groupe)** : Un groupe d'utilisateurs

```bash
# Voir les infos d'un fichier
ls -l fichier.txt
# -rw-r--r-- 1 alice developers 100 Dec 30 10:00 fichier.txt
#              v     v
#           Owner  Group
```

---

**Voir mon identité :**

```bash
# Qui suis-je ?
whoami
# alice

# Mes informations complètes
id
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(developers)

# Mes groupes
groups
# alice sudo developers
```

**Explication de `id` :**

```
uid=1000(alice)           # User ID et nom
gid=1000(alice)           # Group ID principal
groups=1000(alice),       # Groupe principal
       27(sudo),          # Groupe secondaire (droits admin)
       1001(developers)   # Groupe secondaire (équipe dev)
```

---

### ÉTAPE 2 : Lister les utilisateurs et groupes du système

**Fichiers système importants :**

**1. `/etc/passwd` - Liste des utilisateurs**

```bash
# Voir tous les utilisateurs
cat /etc/passwd

# Extraire juste les noms
cut -d: -f1 /etc/passwd
```

**Format :**
```
username:x:UID:GID:comment:home:shell
alice:x:1000:1000:Alice Smith:/home/alice:/bin/bash
```

---

**2. `/etc/group` - Liste des groupes**

```bash
# Voir tous les groupes
cat /etc/group

# Extraire juste les noms
cut -d: -f1 /etc/group
```

**Format :**
```
groupname:x:GID:members
developers:x:1001:alice,bob,charlie
```

---

**3. Commandes utiles**

```bash
# Voir les infos d'un utilisateur
id alice
# uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),1001(developers)

# Voir les infos d'un groupe
getent group developers
# developers:x:1001:alice,bob,charlie

# Voir tous les membres d'un groupe
members developers
# alice bob charlie

# Lister tous les utilisateurs humains (UID >= 1000)
awk -F: '$3 >= 1000 {print $1}' /etc/passwd
```

---

**Utilisateurs système courants :**

| Utilisateur | Description | Usage typique |
|-------------|-------------|---------------|
| `root` | Superutilisateur | Administration système |
| `www-data` | Serveur web | Apache, Nginx |
| `mysql` | Base de données MySQL | Fichiers MySQL |
| `postgres` | Base de données PostgreSQL | Fichiers PostgreSQL |
| `nginx` | Serveur Nginx | Configuration Nginx |
| `nobody` | Utilisateur sans privilèges | Processus sécurisés |
| `daemon` | Processus système | Services en arrière-plan |

**Groupes système courants :**

| Groupe | Description | Membres typiques |
|--------|-------------|------------------|
| `sudo` | Droits administrateur | Administrateurs |
| `www-data` | Serveur web | Processus web |
| `docker` | Docker | Utilisateurs Docker |
| `adm` | Logs système | Admins pouvant lire /var/log |
| `staff` | Administrateurs locaux | Admins non-root |

---

### ÉTAPE 3 : Créer l'environnement de test

```bash
# Créer le dossier de travail
mkdir -p ~/chown-exercise
cd ~/chown-exercise

# Créer différents fichiers et dossiers
mkdir -p {webapp,database,shared,backups,logs}

# Fichiers web
cat > webapp/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Mon Site</title></head>
<body><h1>Bienvenue</h1></body>
</html>
EOF

cat > webapp/config.php << 'EOF'
<?php
define('DB_HOST', 'localhost');
define('DB_USER', 'webapp_user');
?>
EOF

# Fichiers de base de données
touch database/myapp.db
touch database/backup.sql

# Fichiers partagés
echo "Document d'équipe" > shared/team-doc.txt
echo "Notes de réunion" > shared/meeting-notes.txt

# Logs
touch logs/app.log
touch logs/error.log

# Scripts
cat > backups/backup.sh << 'EOF'
#!/bin/bash
echo "Running backup..."
EOF

# Afficher l'état initial
echo "État initial :"
ls -la webapp/
ls -la database/
ls -la shared/
```

---

### ÉTAPE 4 : Commande `chown` - Changer le propriétaire

**Syntaxe de base :**

```
chown [OPTIONS] OWNER FICHIER
chown [OPTIONS] OWNER:GROUP FICHIER
chown [OPTIONS] :GROUP FICHIER
```

**[ATTENTION] IMPORTANT : `chown` nécessite généralement `sudo` !**

On ne peut changer le propriétaire que si on est root.

---

**Test 1 : Changer le propriétaire d'un fichier**

```bash
# Voir le propriétaire actuel
ls -l webapp/index.html
# -rw-r--r-- 1 alice alice 123 Dec 30 10:00 index.html

# Changer le propriétaire (nécessite sudo)
sudo chown www-data webapp/index.html

# Vérifier
ls -l webapp/index.html
# -rw-r--r-- 1 www-data alice 123 Dec 30 10:00 index.html
#                ^
#            Propriétaire changé !
```

**Explication :**
- Le fichier appartient maintenant à `www-data`
- Le groupe reste `alice`
- Seul le propriétaire a changé

---

**Test 2 : Changer propriétaire ET groupe simultanément**

```bash
# Changer owner ET group en une commande
sudo chown www-data:www-data webapp/config.php

# Vérifier
ls -l webapp/config.php
# -rw-r--r-- 1 www-data www-data 95 Dec 30 10:00 config.php
#                ^        ^
#            Owner et Group changés
```

**Syntaxe : `owner:group`**

---

**Test 3 : Changer seulement le groupe avec chown**

```bash
# Utiliser : devant le nom du groupe
sudo chown :developers shared/team-doc.txt

# Vérifier
ls -l shared/team-doc.txt
# -rw-r--r-- 1 alice developers 20 Dec 30 10:00 team-doc.txt
#                       ^
#                  Groupe changé
```

**`:group` = Change seulement le groupe (équivalent à `chgrp`)**

---

**Test 4 : Changer récursivement avec `-R`**

```bash
# Changer tout le dossier webapp récursivement
sudo chown -R www-data:www-data webapp/

# Vérifier
ls -lR webapp/
# Tous les fichiers et dossiers appartiennent maintenant à www-data:www-data
```

**`-R` = Recursive (tous les fichiers et sous-dossiers)**

---

**Test 5 : Utiliser les UID/GID numériques**

```bash
# Voir le UID d'un utilisateur
id -u www-data
# 33

# Changer avec UID
sudo chown 33:33 webapp/index.html

# Vérifier
ls -ln webapp/index.html
# -rw-r--r-- 1 33 33 123 Dec 30 10:00 index.html
```

**`ls -ln` = Affiche les UID/GID numériques au lieu des noms**

**Quand utiliser les UID/GID ?**
- Quand l'utilisateur n'existe pas encore dans /etc/passwd
- Pour les scripts portables entre systèmes
- Pour restaurer des backups avec les bons propriétaires

---

### ÉTAPE 5 : Commande `chgrp` - Changer le groupe

**Syntaxe de base :**

```
chgrp [OPTIONS] GROUP FICHIER
```

**`chgrp` est spécialisé pour changer SEULEMENT le groupe.**

---

**Test 1 : Changer le groupe d'un fichier**

```bash
# Voir le groupe actuel
ls -l shared/meeting-notes.txt
# -rw-r--r-- 1 alice alice 20 Dec 30 10:00 meeting-notes.txt

# Changer le groupe
sudo chgrp developers shared/meeting-notes.txt

# Vérifier
ls -l shared/meeting-notes.txt
# -rw-r--r-- 1 alice developers 20 Dec 30 10:00 meeting-notes.txt
#                       ^
#                  Groupe changé
```

---

**Test 2 : chgrp récursif**

```bash
# Changer le groupe de tout le dossier shared/
sudo chgrp -R developers shared/

# Vérifier
ls -lR shared/
# Tous les fichiers ont maintenant le groupe 'developers'
```

---

**Différence `chown :group` vs `chgrp` :**

```bash
# Ces deux commandes sont équivalentes :
sudo chown :developers fichier.txt
sudo chgrp developers fichier.txt

# chgrp est plus explicite pour changer seulement le groupe
# chown est plus flexible (peut changer owner, group, ou les deux)
```

---

### ÉTAPE 6 : Options avancées

**Option `--reference` : Copier les propriétés d'un autre fichier**

```bash
# Créer deux fichiers
touch file1.txt file2.txt
sudo chown www-data:www-data file1.txt

# Copier les propriétés de file1 vers file2
sudo chown --reference=file1.txt file2.txt

# Vérifier
ls -l file*.txt
# -rw-r--r-- 1 www-data www-data 0 Dec 30 10:00 file1.txt
# -rw-r--r-- 1 www-data www-data 0 Dec 30 10:00 file2.txt
```

**Très utile pour synchroniser les propriétés !**

---

**Option `-v` : Mode verbose (afficher les changements)**

```bash
# Voir ce qui est modifié
sudo chown -v www-data webapp/index.html
# changed ownership of 'webapp/index.html' from alice to www-data

# Avec -R, affiche chaque fichier
sudo chown -Rv www-data:www-data webapp/
# changed ownership of 'webapp/' from alice:alice to www-data:www-data
# changed ownership of 'webapp/index.html' from alice:alice to www-data:www-data
# ...
```

---

**Option `-c` : Afficher seulement les changements réels**

```bash
# Affiche seulement si le propriétaire a vraiment changé
sudo chown -c www-data webapp/index.html

# Si déjà www-data, rien ne s'affiche
# Si changé, affiche le message
```

---

**Option `--from` : Changer seulement si le propriétaire actuel correspond**

```bash
# Changer seulement si actuellement alice
sudo chown --from=alice www-data fichier.txt

# Si le fichier appartient à bob, rien ne se passe
# Si le fichier appartient à alice, changement effectué
```

**Utile pour les scripts conditionnels !**

---

### ÉTAPE 7 : Cas d'usage professionnels

**Cas 1 : Configurer un serveur web (Apache/Nginx)**

```bash
# Structure typique d'un site web
sudo mkdir -p /var/www/monsite
sudo mkdir -p /var/www/monsite/{public,logs,storage,config}

# Créer des fichiers
sudo touch /var/www/monsite/public/index.html
sudo touch /var/www/monsite/config/database.php
sudo touch /var/www/monsite/storage/app.log

# Configuration des propriétaires

# Tout appartient à www-data par défaut
sudo chown -R www-data:www-data /var/www/monsite/

# Le dossier storage doit être accessible en écriture
sudo chmod 775 /var/www/monsite/storage/

# Les fichiers de config doivent être protégés
sudo chmod 640 /var/www/monsite/config/*.php

# Vérifier
ls -lR /var/www/monsite/
```

**Pourquoi www-data ?**
- C'est l'utilisateur sous lequel Apache/Nginx s'exécute
- Le serveur web doit pouvoir lire les fichiers du site
- Pour écrire (uploads, logs), il faut que www-data soit le propriétaire

---

**Cas 2 : Base de données MySQL**

```bash
# Dossier de données MySQL
ls -ld /var/lib/mysql/
# drwx------ 5 mysql mysql 4096 Dec 30 10:00 /var/lib/mysql/

# Tous les fichiers MySQL doivent appartenir à mysql:mysql
sudo chown -R mysql:mysql /var/lib/mysql/

# Permissions strictes pour la sécurité
sudo chmod 750 /var/lib/mysql/

# Fichier de config
sudo chown root:root /etc/mysql/my.cnf
sudo chmod 644 /etc/mysql/my.cnf
```

---

**Cas 3 : Dossier partagé entre utilisateurs**

```bash
# Créer un dossier partagé pour l'équipe 'developers'
sudo mkdir /shared/projects
sudo chown root:developers /shared/projects
sudo chmod 2775 /shared/projects
#           ^
#       Bit SGID (on verra dans 3.4)

# Tous les fichiers créés dans ce dossier auront le groupe 'developers'

# Ajouter un utilisateur au groupe developers
sudo usermod -aG developers alice
sudo usermod -aG developers bob
```

**Explication :**
- **Propriétaire** : root (admin gère le dossier)
- **Groupe** : developers (l'équipe)
- **Permissions** : 2775
  - Owner : rwx (root peut tout faire)
  - Group : rwx (équipe peut tout faire)
  - Others : r-x (lecture seule)
  - 2 = SGID (nouveaux fichiers héritent du groupe)

---

**Cas 4 : Application Node.js déployée**

```bash
# Créer un utilisateur système pour l'app
sudo useradd -r -s /bin/false nodeapp

# Créer la structure
sudo mkdir -p /opt/nodeapp/{app,logs,config}

# Donner ownership à l'utilisateur de l'app
sudo chown -R nodeapp:nodeapp /opt/nodeapp/

# Les logs doivent être accessibles aux admins
sudo chgrp adm /opt/nodeapp/logs/
sudo chmod 750 /opt/nodeapp/logs/

# Config protégée
sudo chmod 640 /opt/nodeapp/config/*.json
```

---

**Cas 5 : Restauration de backup**

```bash
# Extraire un backup
sudo tar -xzf backup.tar.gz -C /tmp/restore/

# Problème : Les fichiers ont les mauvais propriétaires !
ls -l /tmp/restore/
# -rw-r--r-- 1 1002 1003 100 Dec 30 10:00 file.txt
#                ^    ^
#            UID et GID du serveur d'origine

# Solution : Changer récursivement
sudo chown -R www-data:www-data /tmp/restore/

# Copier vers destination
sudo cp -a /tmp/restore/* /var/www/monsite/
```

---

**Cas 6 : Fichiers uploadés par les utilisateurs**

```bash
# Dossier uploads
sudo mkdir -p /var/www/monsite/uploads

# Le serveur web doit pouvoir y écrire
sudo chown www-data:www-data /var/www/monsite/uploads/
sudo chmod 775 /var/www/monsite/uploads/

# Script de nettoyage périodique
cat > /usr/local/bin/clean-uploads.sh << 'EOF'
#!/bin/bash
# Supprimer les fichiers de plus de 30 jours
find /var/www/monsite/uploads/ -type f -mtime +30 -delete

# S'assurer que les permissions sont correctes
chown -R www-data:www-data /var/www/monsite/uploads/
chmod -R 644 /var/www/monsite/uploads/*
EOF

sudo chmod 755 /usr/local/bin/clean-uploads.sh
sudo chown root:root /usr/local/bin/clean-uploads.sh
```

---

### ÉTAPE 8 : Gestion des utilisateurs et groupes

**Créer un utilisateur**

```bash
# Créer un utilisateur standard
sudo useradd -m -s /bin/bash alice

# Créer un utilisateur système (pour une app)
sudo useradd -r -s /bin/false webapp
```

**Options :**
- `-m` : Créer le home directory
- `-s` : Shell (bash pour humains, /bin/false pour apps)
- `-r` : Utilisateur système (UID < 1000)

---

**Créer un groupe**

```bash
# Créer un groupe
sudo groupadd developers

# Créer un groupe système
sudo groupadd -r webapps
```

---

**Ajouter un utilisateur à un groupe**

```bash
# Ajouter alice au groupe developers
sudo usermod -aG developers alice

# Vérifier
groups alice
# alice : alice developers

# Pour que les changements prennent effet :
# L'utilisateur doit se déconnecter et reconnecter
# Ou : su - alice
```

**Options :**
- `-a` : Append (ajouter sans retirer des autres groupes)
- `-G` : Groupes secondaires

**[ATTENTION] Sans `-a`, usermod REMPLACE tous les groupes !**

```bash
# Mauvais : Retire alice de tous les autres groupes
sudo usermod -G developers alice

# Bon : Ajoute developers aux groupes existants
sudo usermod -aG developers alice
```

---

**Retirer un utilisateur d'un groupe**

```bash
# Retirer alice du groupe developers
sudo gpasswd -d alice developers

# Vérifier
groups alice
```

---

**Supprimer un utilisateur**

```bash
# Supprimer l'utilisateur (garde le home)
sudo userdel alice

# Supprimer l'utilisateur et son home
sudo userdel -r alice
```

---

**Supprimer un groupe**

```bash
# Supprimer un groupe
sudo groupdel developers
```

---

### ÉTAPE 9 : Scripts d'automatisation

**Script 1 : Configurer un projet web automatiquement**

```bash
#!/bin/bash
# setup-webapp-ownership.sh

set -e

WEBAPP_DIR=$1
WEB_USER=${2:-www-data}
WEB_GROUP=${3:-www-data}

if [ -z "$WEBAPP_DIR" ]; then
    echo "Usage: $0 <webapp-directory> [web-user] [web-group]"
    exit 1
fi

if [ ! -d "$WEBAPP_DIR" ]; then
    echo "[X] Erreur : $WEBAPP_DIR n'existe pas"
    exit 1
fi

echo "[OUTIL] Configuration de $WEBAPP_DIR..."
echo "   Utilisateur web : $WEB_USER"
echo "   Groupe web : $WEB_GROUP"
echo ""

# Vérifier que l'utilisateur web existe
if ! id "$WEB_USER" &>/dev/null; then
    echo "[X] Erreur : L'utilisateur $WEB_USER n'existe pas"
    exit 1
fi

# Propriété générale : www-data
echo "[DOSSIER] Configuration du propriétaire..."
chown -R "$WEB_USER:$WEB_GROUP" "$WEBAPP_DIR"

# Dossiers spéciaux
for dir in storage cache logs uploads temp; do
    if [ -d "$WEBAPP_DIR/$dir" ]; then
        echo "  [OK] $dir : $WEB_USER:$WEB_GROUP (775)"
        chmod 775 "$WEBAPP_DIR/$dir"
    fi
done

# Fichiers de config (lecture seule pour le groupe)
for file in "$WEBAPP_DIR"/config/*.php "$WEBAPP_DIR"/.env; do
    if [ -f "$file" ]; then
        echo "  [VERROUILLE] $(basename $file) : $WEB_USER:$WEB_GROUP (640)"
        chmod 640 "$file"
    fi
done

# Scripts exécutables
if [ -d "$WEBAPP_DIR/scripts" ]; then
    find "$WEBAPP_DIR/scripts" -type f -name "*.sh" -exec chmod 750 {} \;
    echo "  [OK] Scripts : 750"
fi

echo ""
echo "[OK] Configuration terminée !"
```

---

**Script 2 : Audit de propriété**

```bash
#!/bin/bash
# audit-ownership.sh

echo "[RECHERCHE] Audit de propriété des fichiers"
echo "=================================="
echo ""

# 1. Fichiers sans propriétaire valide
echo "[ATTENTION]  Fichiers orphelins (sans propriétaire) :"
find /var/www /opt /home -nouser 2>/dev/null | head -10
echo ""

# 2. Fichiers sans groupe valide
echo "[ATTENTION]  Fichiers sans groupe valide :"
find /var/www /opt /home -nogroup 2>/dev/null | head -10
echo ""

# 3. Fichiers appartenant à root dans /home
echo "[ATTENTION]  Fichiers root dans /home (potentiellement problématiques) :"
find /home -user root -type f 2>/dev/null | head -10
echo ""

# 4. Dossiers web n'appartenant pas à www-data
echo "[ATTENTION]  Fichiers web n'appartenant pas à www-data :"
find /var/www -type f ! -user www-data 2>/dev/null | head -10
echo ""

# 5. Résumé par propriétaire dans un dossier
report_ownership() {
    local dir=$1
    echo "[GRAPHIQUE] Résumé pour $dir :"
    find "$dir" -type f 2>/dev/null | xargs ls -l 2>/dev/null | \
        awk '{print $3}' | sort | uniq -c | sort -rn | head -10
    echo ""
}

if [ -d "/var/www" ]; then
    report_ownership "/var/www"
fi

echo "[OK] Audit terminé"
```

---

**Script 3 : Fixer les propriétés après déploiement**

```bash
#!/bin/bash
# fix-deployment-ownership.sh

DEPLOY_DIR=$1

if [ -z "$DEPLOY_DIR" ]; then
    echo "Usage: $0 <deployment-directory>"
    exit 1
fi

echo "[OUTIL] Correction des propriétés après déploiement..."

# Logs de ce qui a été fait
LOG_FILE="/var/log/fix-ownership-$(date +%Y%m%d_%H%M%S).log"

# Fonction de log
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "Début de la correction pour $DEPLOY_DIR"

# 1. Tout appartient à www-data par défaut
log "Changement du propriétaire global..."
chown -R www-data:www-data "$DEPLOY_DIR" 2>&1 | tee -a "$LOG_FILE"

# 2. Les scripts de déploiement appartiennent à root
if [ -d "$DEPLOY_DIR/deploy-scripts" ]; then
    log "Scripts de déploiement -> root:root"
    chown -R root:root "$DEPLOY_DIR/deploy-scripts"
fi

# 3. Les fichiers de config sensibles
for file in "$DEPLOY_DIR"/config/*.{php,env,yml,yaml} 2>/dev/null; do
    if [ -f "$file" ]; then
        log "Config sensible : $(basename $file) -> www-data:www-data (640)"
        chown www-data:www-data "$file"
        chmod 640 "$file"
    fi
done

# 4. Dossiers avec écriture
for dir in storage logs cache uploads tmp; do
    if [ -d "$DEPLOY_DIR/$dir" ]; then
        log "Dossier écriture : $dir -> www-data:www-data (775)"
        chown -R www-data:www-data "$DEPLOY_DIR/$dir"
        chmod 775 "$DEPLOY_DIR/$dir"
    fi
done

log "Correction terminée"
log "Log complet : $LOG_FILE"

echo ""
echo "[OK] Propriétés corrigées !"
echo "[FICHIER] Log : $LOG_FILE"
```

---

### ÉTAPE 10 : Résolution de problèmes courants

**Problème 1 : "Operation not permitted" avec chown**

**Symptôme :**
```bash
chown alice fichier.txt
# chown: changing ownership of 'fichier.txt': Operation not permitted
```

**Cause :** Seul root peut changer le propriétaire.

**Solution :**
```bash
# Utiliser sudo
sudo chown alice fichier.txt
```

---

**Problème 2 : Application web ne peut pas écrire**

**Symptôme :**
```bash
# Dans les logs du serveur web
PHP Warning: file_put_contents(/var/www/uploads/file.txt): failed to open stream: Permission denied
```

**Diagnostic :**
```bash
# Vérifier le propriétaire
ls -ld /var/www/uploads/
# drwxr-xr-x 2 root root 4096 Dec 30 10:00 uploads/
#                ^    ^
#            Appartient à root, pas www-data

# Vérifier sous quel utilisateur tourne le serveur
ps aux | grep apache2
# www-data  1234  ... /usr/sbin/apache2
```

**Solution :**
```bash
# Changer le propriétaire
sudo chown -R www-data:www-data /var/www/uploads/

# Ajouter les permissions d'écriture
sudo chmod 775 /var/www/uploads/
```

---

**Problème 3 : Fichiers créés avec mauvais groupe**

**Symptôme :**
```bash
# Dans un dossier partagé
touch /shared/projects/file.txt
ls -l /shared/projects/file.txt
# -rw-r--r-- 1 alice alice 0 Dec 30 10:00 file.txt
#                       ^
# Devrait être 'developers' !
```

**Cause :** Le dossier n'a pas le bit SGID.

**Solution :**
```bash
# Ajouter le bit SGID au dossier
sudo chmod g+s /shared/projects/

# Vérifier
ls -ld /shared/projects/
# drwxrwsr-x 2 root developers 4096 Dec 30 10:00 projects/
#       ^
#     's' = SGID

# Maintenant les nouveaux fichiers auront le bon groupe
touch /shared/projects/newfile.txt
ls -l /shared/projects/newfile.txt
# -rw-r--r-- 1 alice developers 0 Dec 30 10:00 newfile.txt
#                       ^
#                   Groupe correct !
```

---

**Problème 4 : Restauration de backup avec mauvais propriétaires**

**Symptôme :**
```bash
# Après restauration
ls -ln /var/www/site/
# -rw-r--r-- 1 1002 1003 100 Dec 30 10:00 index.html
#                ^    ^
#            UID/GID du serveur d'origine
```

**Solution :**
```bash
# Méthode 1 : Changer tous les fichiers
sudo chown -R www-data:www-data /var/www/site/

# Méthode 2 : Utiliser une map de conversion (avancé)
# Si plusieurs utilisateurs à mapper
while IFS=: read old_uid new_user; do
    find /var/www/site/ -uid $old_uid -exec chown $new_user {} \;
done << EOF
1002:www-data
1003:mysql
1004:postgres
EOF
```

---

**Problème 5 : Impossible de supprimer un fichier même avec write**

**Symptôme :**
```bash
# J'ai write sur le fichier
ls -l file.txt
# -rw-rw-rw- 1 bob bob 100 Dec 30 10:00 file.txt

rm file.txt
# rm: cannot remove 'file.txt': Permission denied
```

**Diagnostic :**
```bash
# Vérifier le dossier parent
ls -ld .
# drwxr-xr-x 2 bob bob 4096 Dec 30 10:00 .
# Je ne suis pas bob !

whoami
# alice

# alice est dans 'others' -> pas de 'w' sur le dossier
```

**Explication :** Pour supprimer un fichier, il faut `w` sur le DOSSIER, pas sur le fichier.

**Solution :**
```bash
# Option 1 : bob ajoute alice au groupe et donne w au groupe
sudo chgrp developers .
sudo chmod 775 .

# Option 2 : bob change le propriétaire du dossier
sudo chown alice .
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : chown change le propriétaire**

```bash
# Créer un fichier
touch test-chown.txt

# Vérifier propriétaire actuel
OWNER_BEFORE=$(stat -c "%U" test-chown.txt)
echo "Avant : $OWNER_BEFORE"

# Changer (avec sudo si nécessaire)
if [ "$OWNER_BEFORE" != "www-data" ] && id www-data &>/dev/null; then
    sudo chown www-data test-chown.txt
    OWNER_AFTER=$(stat -c "%U" test-chown.txt)
    
    if [ "$OWNER_AFTER" = "www-data" ]; then
        echo "[OK] Test 1 OK : chown fonctionne"
    else
        echo "[X] Test 1 FAIL"
    fi
fi

sudo rm test-chown.txt
```

---

**Test 2 : chgrp change le groupe**

```bash
# Créer un fichier
touch test-chgrp.txt

# Changer le groupe (si le groupe existe)
if getent group sudo &>/dev/null; then
    sudo chgrp sudo test-chgrp.txt
    
    GROUP=$(stat -c "%G" test-chgrp.txt)
    if [ "$GROUP" = "sudo" ]; then
        echo "[OK] Test 2 OK : chgrp fonctionne"
    else
        echo "[X] Test 2 FAIL : Groupe = $GROUP"
    fi
fi

sudo rm test-chgrp.txt
```

---

**Test 3 : chown -R fonctionne récursivement**

```bash
# Créer une structure
mkdir -p test-recursive/{dir1,dir2}
touch test-recursive/file.txt
touch test-recursive/dir1/file1.txt

# Changer récursivement
sudo chown -R nobody:nogroup test-recursive/ 2>/dev/null || \
sudo chown -R daemon:daemon test-recursive/

# Vérifier
OWNER=$(stat -c "%U" test-recursive/dir1/file1.txt)

if [ "$OWNER" = "nobody" ] || [ "$OWNER" = "daemon" ]; then
    echo "[OK] Test 3 OK : chown -R fonctionne"
else
    echo "[X] Test 3 FAIL : Owner = $OWNER"
fi

sudo rm -rf test-recursive/
```

---

**Test 4 : Vérifier les utilisateurs/groupes**

```bash
# Vérifier que les commandes de base fonctionnent
if id &>/dev/null; then
    echo "[OK] Test 4.1 OK : id fonctionne"
fi

if groups &>/dev/null; then
    echo "[OK] Test 4.2 OK : groups fonctionne"
fi

if getent passwd $(whoami) &>/dev/null; then
    echo "[OK] Test 4.3 OK : getent fonctionne"
fi

echo "[OK] Test 4 OK"
```

---

**Test 5 : Référence fonctionne**

```bash
# Créer deux fichiers
touch ref.txt target.txt

# Définir propriétaire sur ref
sudo chown nobody:nogroup ref.txt 2>/dev/null || \
sudo chown daemon:daemon ref.txt

# Copier vers target avec --reference
sudo chown --reference=ref.txt target.txt

# Vérifier
REF_OWNER=$(stat -c "%U" ref.txt)
TARGET_OWNER=$(stat -c "%U" target.txt)

if [ "$REF_OWNER" = "$TARGET_OWNER" ]; then
    echo "[OK] Test 5 OK : --reference fonctionne"
else
    echo "[X] Test 5 FAIL : $REF_OWNER != $TARGET_OWNER"
fi

sudo rm ref.txt target.txt
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Oubli de sudo

**Symptôme :**
```bash
chown www-data fichier.txt
# chown: changing ownership of 'fichier.txt': Operation not permitted
```

**Solution :**
```bash
sudo chown www-data fichier.txt
```

---

#### Erreur 2 : Utilisateur n'existe pas

**Symptôme :**
```bash
sudo chown webapp fichier.txt
# chown: invalid user: 'webapp'
```

**Solution :**
```bash
# Vérifier que l'utilisateur existe
id webapp
# id: 'webapp': no such user

# Créer l'utilisateur
sudo useradd -r webapp

# Maintenant chown fonctionne
sudo chown webapp fichier.txt
```

---

#### Erreur 3 : usermod sans -a écrase les groupes

**Symptôme :**
```bash
# Alice est dans plusieurs groupes
groups alice
# alice : alice sudo developers

# Ajouter à un nouveau groupe (SANS -a)
sudo usermod -G webdev alice

# Vérifier
groups alice
# alice : alice webdev
#         ^
# sudo et developers ont été RETIRÉS !
```

**Solution :**
```bash
# TOUJOURS utiliser -a avec -G
sudo usermod -aG webdev alice

# Maintenant ça AJOUTE au lieu de remplacer
groups alice
# alice : alice sudo developers webdev
```

---

#### Erreur 4 : Chown sur lien symbolique

**Symptôme :**
```bash
ln -s /path/to/file link
sudo chown www-data link

# Le propriétaire du FICHIER cible change, pas du lien !
ls -l link
# lrwxrwxrwx 1 alice alice 15 Dec 30 10:00 link -> /path/to/file

ls -l /path/to/file
# -rw-r--r-- 1 www-data alice 100 Dec 30 10:00 /path/to/file
```

**Solution :**
```bash
# Pour changer le lien lui-même (rare), utiliser -h
sudo chown -h www-data link

# Mais généralement on veut changer la cible, donc comportement par défaut OK
```

---

#### Erreur 5 : Permissions perdues après chown

**Symptôme :**
```bash
# Script exécutable
ls -l script.sh
# -rwxr-xr-x 1 alice alice 100 Dec 30 10:00 script.sh

# Changer owner
sudo chown bob script.sh

# Script toujours exécutable
ls -l script.sh
# -rwxr-xr-x 1 bob alice 100 Dec 30 10:00 script.sh
```

**Note :** Ce n'est PAS une erreur ! chown ne change PAS les permissions.

**Attention :** Certains systèmes de fichiers ou options peuvent retirer les bits SUID/SGID pour sécurité.

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Propriété des fichiers**
- Chaque fichier a un owner (propriétaire) et un group
- Visible avec `ls -l`
- Informations dans /etc/passwd et /etc/group

**2. chown - Change owner**
- `chown user file` : Change owner
- `chown user:group file` : Change owner et group
- `chown :group file` : Change seulement group
- `chown -R` : Récursif
- Nécessite sudo (sauf cas spéciaux)

**3. chgrp - Change group**
- `chgrp group file` : Change group
- Équivalent à `chown :group file`
- Plus explicite pour changer seulement le groupe

**4. Options utiles**
- `-R` : Récursif
- `-v` : Verbose
- `--reference` : Copier d'un autre fichier
- `--from` : Conditionnel

**5. Utilisateurs système**
- www-data : Serveur web
- mysql : Base de données
- nobody : Sans privilèges
- Créés avec `useradd -r`

**6. Groupes**
- Ajouter : `usermod -aG group user`
- Retirer : `gpasswd -d user group`
- Lister : `groups user`

**7. Sécurité**
- Applications web -> www-data
- Bases de données -> mysql/postgres
- Fichiers sensibles -> root ou utilisateur spécifique
- Dossiers partagés -> groupe approprié avec SGID

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. ACL (Access Control Lists) - Permissions fines**

```bash
# Donner accès à un utilisateur spécifique sans changer owner/group
setfacl -m u:bob:rw file.txt

# Voir les ACL
getfacl file.txt

# Retirer ACL
setfacl -x u:bob file.txt
```

---

**2. Namespaces utilisateurs (containers)**

```bash
# Dans les containers Docker
# Les UID/GID sont mappés différemment

# Exemple : UID 1000 dans container = UID 100000 sur host
# Important pour sécurité et ownership
```

---

**3. Quotas par utilisateur**

```bash
# Limiter l'espace disque par utilisateur
sudo apt install quota
sudo quotacheck -cum /home
sudo edquota alice

# Voir l'usage
sudo repquota /home
```

---

**4. Audit des changements de propriété**

```bash
# Avec auditd
sudo apt install auditd

# Surveiller chown/chgrp
sudo auditctl -w /etc/passwd -p wa -k user_changes
sudo auditctl -a always,exit -F arch=b64 -S chown -k ownership_changes

# Voir les logs
sudo ausearch -k ownership_changes
```

---

**5. Script de synchronisation ownership**

```bash
#!/bin/bash
# sync-ownership.sh
# Synchronise les propriétaires entre deux dossiers

SOURCE=$1
DEST=$2

if [ -z "$SOURCE" ] || [ -z "$DEST" ]; then
    echo "Usage: $0 <source> <dest>"
    exit 1
fi

# Pour chaque fichier dans source
find "$SOURCE" -type f | while read src_file; do
    # Chemin relatif
    rel_path=${src_file#$SOURCE/}
    dest_file="$DEST/$rel_path"
    
    if [ -f "$dest_file" ]; then
        # Obtenir owner:group de source
        owner=$(stat -c "%U:%G" "$src_file")
        
        # Appliquer à dest
        chown "$owner" "$dest_file"
        echo "[OK] $rel_path -> $owner"
    fi
done
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3.3

**[OK] Félicitations ! Tu maîtrises maintenant chown et chgrp !**

**Ce que tu as appris :**
- Comprendre owner et group
- Utiliser chown pour changer le propriétaire
- Utiliser chgrp pour changer le groupe
- Changer owner et group simultanément
- Modification récursive
- Gérer les utilisateurs et groupes
- Configurer des serveurs (web, BDD)
- Scripts d'automatisation
- Résolution de problèmes

**Compétences acquises :**
- [OK] Gestion de propriété (niveau avancé)
- [OK] Administration utilisateurs
- [OK] Configuration d'applications
- [OK] Sécurisation

**Temps moyen de réalisation :** 1h30 - 2h

**Prochaine étape :** Exercice 3.4 - Permissions spéciales (SUID, SGID, Sticky Bit) ! [SECURISE]

---

Je continue avec l'exercice 3.4 (permissions spéciales) ?

# [ROUGE] EXERCICE 3.4 : PERMISSIONS SPÉCIALES - SUID, SGID, STICKY BIT (EXPERT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système senior et tu dois gérer des configurations avancées de sécurité :
- Programmes qui nécessitent des privilèges temporaires (passwd, sudo, ping)
- Dossiers partagés entre équipes avec contraintes spécifiques
- Zones temporaires multi-utilisateurs (/tmp)
- Applications nécessitant des élévations de privilèges contrôlées
- Audits de sécurité pour détecter les binaires SUID dangereux

**Problématique :** Les permissions rwx standard ne suffisent pas toujours. Il existe 3 permissions spéciales cruciales pour la sécurité et le fonctionnement du système.

### Cahier des charges

- Comprendre SUID (Set User ID)
- Comprendre SGID (Set Group ID)
- Comprendre Sticky Bit
- Notation octale à 4 chiffres
- Identifier les cas d'usage légitimes
- Détecter les risques de sécurité
- Configurer des dossiers partagés sécurisés
- Auditer les permissions spéciales

### Contraintes

- Commandes : `chmod`, `find`, `ls -l`
- Nécessite généralement `sudo`
- Implications de sécurité critiques
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre le SUID et ses implications
- [OK] Comprendre le SGID sur fichiers et dossiers
- [OK] Comprendre le Sticky Bit
- [OK] Lire la notation à 4 chiffres (4755, 2755, 1777)
- [OK] Utiliser la notation symbolique (u+s, g+s, o+t)
- [OK] Identifier les binaires SUID système
- [OK] Configurer des dossiers partagés sécurisés
- [OK] Auditer les permissions spéciales
- [OK] Comprendre les risques de sécurité
- [OK] Implémenter des configurations sécurisées

---

## [DOCS] PRÉREQUIS

- Exercices 3.1, 3.2 et 3.3 terminés
- Maîtrise complète des permissions rwx
- Compréhension de chown/chgrp
- Accès sudo

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Vue d'ensemble des 3 permissions spéciales

**Les permissions spéciales sont représentées par un 4ème chiffre en octal :**

| Permission | Bit | Octal | Symbolique | Emplacement visuel |
|------------|-----|-------|------------|-------------------|
| **SUID** | Set User ID | 4000 | `u+s` | Owner execute -> `s` |
| **SGID** | Set Group ID | 2000 | `g+s` | Group execute -> `s` |
| **Sticky Bit** | Restricted deletion | 1000 | `o+t` | Others execute -> `t` |

---

**Notation octale complète à 4 chiffres :**

```
chmod 4755 fichier
      vvvv
      ││││
      │││└─ Others: 5 (r-x)
      ││└── Group:  5 (r-x)
      │└─── Owner:  7 (rwx)
      └──── Permissions spéciales: 4 (SUID)
```

**Valeurs du 4ème chiffre :**
- 0 = Aucune permission spéciale
- 1 = Sticky bit
- 2 = SGID
- 3 = SGID + Sticky bit (rare)
- 4 = SUID
- 5 = SUID + Sticky bit (rare)
- 6 = SUID + SGID
- 7 = SUID + SGID + Sticky bit

---

### ÉTAPE 2 : SUID (Set User ID) - Bit 4000

**Qu'est-ce que le SUID ?**

Le SUID permet à un fichier exécutable de s'exécuter avec les **permissions du propriétaire** du fichier, pas de l'utilisateur qui l'exécute.

---

**Visualisation :**

```bash
# Fichier normal
-rwxr-xr-x 1 root root 100 file
# Alice exécute -> processus tourne comme alice

# Fichier avec SUID
-rwsr-xr-x 1 root root 100 file
   ^
's' au lieu de 'x' (owner)
# Alice exécute -> processus tourne comme root !
```

---

**Créer l'environnement de test :**

```bash
# Créer le dossier de travail
mkdir -p ~/special-perms-exercise
cd ~/special-perms-exercise
```

---

**Test 1 : Comprendre le SUID avec un exemple simple**

```bash
# Créer un script qui affiche l'utilisateur effectif
cat > whoami-test.sh << 'EOF'
#!/bin/bash
echo "Script exécuté par : $(whoami)"
echo "UID réel : $(id -u)"
echo "UID effectif : $(id -u)"
echo "User réel : $(id -un)"
echo "User effectif : $(id -un)"
EOF

chmod 755 whoami-test.sh

# Exécuter normalement
./whoami-test.sh
# Script exécuté par : alice
# Tout est alice
```

**Maintenant avec SUID :**

```bash
# Ajouter le bit SUID
sudo chown root:root whoami-test.sh
sudo chmod 4755 whoami-test.sh

# Vérifier
ls -l whoami-test.sh
# -rwsr-xr-x 1 root root 150 Dec 30 10:00 whoami-test.sh
#    ^
# 's' = SUID activé

# Exécuter
./whoami-test.sh
# Script exécuté par : root
# UID effectif = 0 (root)
```

**[ATTENTION] DANGER : Le script s'exécute avec les privilèges root même si lancé par alice !**

---

**Test 2 : Exemple réel - La commande `passwd`**

```bash
# passwd permet aux utilisateurs de changer leur mot de passe
# Mais les mots de passe sont stockés dans /etc/shadow (accessible seulement par root)

ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Dec 30 10:00 /usr/bin/passwd
#    ^
# SUID bit activé

# Comment ça fonctionne :
# 1. Alice lance : passwd
# 2. Le processus s'exécute comme root (grâce au SUID)
# 3. Le processus peut modifier /etc/shadow
# 4. Alice peut changer son mot de passe !

# Vérifier /etc/shadow
ls -l /etc/shadow
# -rw-r----- 1 root shadow 1234 Dec 30 10:00 /etc/shadow
# Seulement root peut écrire
```

---

**Test 3 : Créer un binaire SUID (programme C)**

```bash
# Créer un programme simple en C
cat > privileged-app.c << 'EOF'
#include <stdio.h>
#include <unistd.h>

int main() {
    printf("Real UID: %d\n", getuid());
    printf("Effective UID: %d\n", geteuid());
    printf("Real User: %s\n", getlogin());
    
    // Faire quelque chose qui nécessite root
    if (geteuid() == 0) {
        printf("Running with root privileges!\n");
    } else {
        printf("Running as normal user\n");
    }
    
    return 0;
}
EOF

# Compiler
gcc privileged-app.c -o privileged-app

# Donner ownership à root et SUID
sudo chown root:root privileged-app
sudo chmod 4755 privileged-app

# Vérifier
ls -l privileged-app
# -rwsr-xr-x 1 root root 16760 Dec 30 10:00 privileged-app

# Exécuter
./privileged-app
# Real UID: 1000 (alice)
# Effective UID: 0 (root)
# Running with root privileges!
```

---

**Notation pour SUID :**

```bash
# Méthode 1 : Octal à 4 chiffres
chmod 4755 fichier
#     ^
#   SUID = 4

# Méthode 2 : Symbolique
chmod u+s fichier

# Retirer SUID
chmod u-s fichier
# Ou
chmod 0755 fichier
```

---

**Affichage du SUID dans ls -l :**

```bash
# Avec x (exécutable)
-rwsr-xr-x    # 's' minuscule = SUID + executable

# Sans x (pas exécutable) - configuration incorrecte !
-rwSr-xr-x    # 'S' majuscule = SUID mais pas executable
```

**'S' majuscule = SUID activé mais fichier pas exécutable (erreur de config)**

---

**[ATTENTION] RISQUES DE SÉCURITÉ DU SUID :**

1. **Élévation de privilèges**
   - Un bug dans un programme SUID = faille root
   - Peut compromettre tout le système

2. **Scripts shell avec SUID = TRÈS DANGEREUX**
   - Faciles à exploiter
   - La plupart des systèmes ignorent le SUID sur les scripts shell

3. **Programmes mal sécurisés**
   - Injection de commandes
   - Buffer overflow
   - Path hijacking

**Bonnes pratiques :**
- Minimiser les programmes SUID
- Auditer régulièrement
- Ne JAMAIS mettre SUID sur des scripts shell
- Utiliser des alternatives (sudo, capabilities)

---

### ÉTAPE 3 : SGID (Set Group ID) - Bit 2000

**Le SGID a DEUX comportements différents selon le type :**

**1. SGID sur FICHIER exécutable**
- Le processus s'exécute avec les privilèges du **groupe** propriétaire
- Comme SUID mais pour le groupe

**2. SGID sur DOSSIER** * **Plus utilisé et important**
- Les nouveaux fichiers créés héritent du **groupe** du dossier
- Pas du groupe de l'utilisateur qui crée le fichier
- **Essentiel pour les dossiers partagés !**

---

**Test 1 : SGID sur fichier exécutable**

```bash
# Créer un programme qui affiche le GID
cat > group-test.c << 'EOF'
#include <stdio.h>
#include <unistd.h>

int main() {
    printf("Real GID: %d\n", getgid());
    printf("Effective GID: %d\n", getegid());
    return 0;
}
EOF

gcc group-test.c -o group-test

# Créer un groupe spécial
sudo groupadd specialgroup

# Définir ownership et SGID
sudo chown root:specialgroup group-test
sudo chmod 2755 group-test

# Vérifier
ls -l group-test
# -rwxr-sr-x 1 root specialgroup 16648 Dec 30 10:00 group-test
#       ^
#     's' = SGID

# Exécuter
./group-test
# Real GID: 1000 (alice)
# Effective GID: 1001 (specialgroup)
```

---

**Test 2 : SGID sur dossier (CAS LE PLUS IMPORTANT)**

```bash
# Créer un dossier partagé pour une équipe
sudo mkdir /shared/team-project
sudo chown root:developers /shared/team-project
sudo chmod 2775 /shared/team-project
#             ^
#           SGID = 2

# Vérifier
ls -ld /shared/team-project
# drwxrwsr-x 2 root developers 4096 Dec 30 10:00 /shared/team-project
#       ^
#     's' = SGID sur dossier
```

**Comportement SANS SGID :**

```bash
# Dossier normal (sans SGID)
mkdir normal-dir
chmod 775 normal-dir

# Alice crée un fichier
touch normal-dir/alice-file.txt
ls -l normal-dir/alice-file.txt
# -rw-r--r-- 1 alice alice 0 Dec 30 10:00 alice-file.txt
#                     ^
#                Groupe = alice (groupe primaire d'alice)

# Bob crée un fichier
sudo -u bob touch normal-dir/bob-file.txt
ls -l normal-dir/bob-file.txt
# -rw-r--r-- 1 bob bob 0 Dec 30 10:00 bob-file.txt
#                   ^
#              Groupe = bob

# Problème : Fichiers avec des groupes différents !
# Alice et Bob ne peuvent pas modifier les fichiers de l'autre (si pas même groupe)
```

**Comportement AVEC SGID :**

```bash
# Dossier avec SGID
sudo mkdir sgid-dir
sudo chgrp developers sgid-dir
sudo chmod 2775 sgid-dir

# Alice crée un fichier
touch sgid-dir/alice-file.txt
ls -l sgid-dir/alice-file.txt
# -rw-r--r-- 1 alice developers 0 Dec 30 10:00 alice-file.txt
#                     ^
#                Groupe = developers (groupe du dossier !)

# Bob crée un fichier
sudo -u bob touch sgid-dir/bob-file.txt
ls -l sgid-dir/bob-file.txt
# -rw-r--r-- 1 bob developers 0 Dec 30 10:00 bob-file.txt
#                   ^
#              Groupe = developers

# [OK] Tous les fichiers ont le même groupe !
# Avec chmod 775 (ou 664), tous les membres du groupe peuvent collaborer
```

---

**Notation pour SGID :**

```bash
# Méthode 1 : Octal
chmod 2775 dossier
#     ^
#   SGID = 2

# Méthode 2 : Symbolique
chmod g+s dossier

# Retirer SGID
chmod g-s dossier
# Ou
chmod 0775 dossier
```

---

**Affichage du SGID dans ls -l :**

```bash
# Sur dossier avec x
drwxrwsr-x    # 's' minuscule = SGID + executable

# Sur dossier sans x (rare et problématique)
drwxrwSr-x    # 'S' majuscule = SGID sans executable
```

---

**Cas d'usage professionnels du SGID :**

**1. Dossier de projet d'équipe**

```bash
# Configuration type
sudo mkdir /projects/webapp
sudo chgrp webdev /projects/webapp
sudo chmod 2775 /projects/webapp

# Tous les fichiers créés auront le groupe 'webdev'
# Tous les membres de webdev peuvent collaborer
```

---

**2. Dossier de logs partagés**

```bash
# Plusieurs applications écrivent dans le même dossier de logs
sudo mkdir /var/log/apps
sudo chgrp adm /var/log/apps
sudo chmod 2770 /var/log/apps

# Tous les logs auront le groupe 'adm'
# Les admins peuvent lire tous les logs
```

---

**3. Zone de dépôt (uploads)**

```bash
# Utilisateurs peuvent déposer des fichiers
# Admins gèrent le contenu
sudo mkdir /uploads
sudo chgrp uploaders /uploads
sudo chmod 2775 /uploads

# Fichiers uploadés auront le groupe 'uploaders'
```

---

### ÉTAPE 4 : Sticky Bit - Bit 1000

**Qu'est-ce que le Sticky Bit ?**

Sur un dossier, le **Sticky Bit** empêche les utilisateurs de supprimer ou renommer les fichiers qui ne leur appartiennent pas, **même s'ils ont les permissions d'écriture sur le dossier**.

**Utilisé principalement sur `/tmp` !**

---

**Comportement SANS Sticky Bit :**

```bash
# Créer un dossier partagé sans sticky bit
mkdir /tmp/shared-nosticky
chmod 777 /tmp/shared-nosticky

# Alice crée un fichier
touch /tmp/shared-nosticky/alice-file.txt
ls -l /tmp/shared-nosticky/alice-file.txt
# -rw-r--r-- 1 alice alice 0 Dec 30 10:00 alice-file.txt

# Bob peut supprimer le fichier d'Alice !
# (car il a 'w' sur le dossier)
sudo -u bob rm /tmp/shared-nosticky/alice-file.txt
# Succès ! Fichier supprimé

# [X] Problème : N'importe qui peut supprimer les fichiers des autres !
```

---

**Comportement AVEC Sticky Bit :**

```bash
# Créer un dossier avec sticky bit
sudo mkdir /tmp/shared-sticky
sudo chmod 1777 /tmp/shared-sticky
#             ^
#       Sticky bit = 1

# Vérifier
ls -ld /tmp/shared-sticky
# drwxrwxrwt 2 root root 4096 Dec 30 10:00 /tmp/shared-sticky
#         ^
#       't' = Sticky bit

# Alice crée un fichier
touch /tmp/shared-sticky/alice-file.txt

# Bob essaie de supprimer le fichier d'Alice
sudo -u bob rm /tmp/shared-sticky/alice-file.txt
# rm: cannot remove 'alice-file.txt': Operation not permitted

# [OK] Seul Alice (ou root) peut supprimer son fichier !
```

---

**Le dossier /tmp système :**

```bash
# Vérifier /tmp
ls -ld /tmp
# drwxrwxrwt 10 root root 4096 Dec 30 10:00 /tmp
#         ^
#       't' = Sticky bit

# Permissions : 1777
# - Tout le monde peut créer des fichiers (777)
# - Mais seulement le propriétaire peut supprimer ses fichiers (sticky bit)
```

**Pourquoi c'est crucial ?**
- /tmp est accessible à TOUS les utilisateurs
- Sans sticky bit, n'importe qui pourrait supprimer les fichiers de n'importe qui
- Avec sticky bit, chacun protège ses propres fichiers temporaires

---

**Notation pour Sticky Bit :**

```bash
# Méthode 1 : Octal
chmod 1777 dossier
#     ^
#   Sticky = 1

# Méthode 2 : Symbolique
chmod o+t dossier

# Retirer sticky bit
chmod o-t dossier
# Ou
chmod 0777 dossier
```

---

**Affichage du Sticky Bit dans ls -l :**

```bash
# Avec x
drwxrwxrwt    # 't' minuscule = Sticky + executable

# Sans x (très rare)
drwxrwxrwT    # 'T' majuscule = Sticky sans executable
```

---

**Cas d'usage du Sticky Bit :**

**1. Dossiers temporaires multi-utilisateurs**

```bash
# /tmp, /var/tmp
chmod 1777 /tmp
```

---

**2. Dossier de dépôt public**

```bash
# Zone où les utilisateurs déposent des fichiers
# Mais ne peuvent pas modifier/supprimer ceux des autres
sudo mkdir /dropbox
sudo chmod 1777 /dropbox
```

---

**3. Zone de travail collaborative avec protection**

```bash
# Équipe peut créer des fichiers
# Mais seulement l'auteur peut supprimer
sudo mkdir /shared/work
sudo chgrp team /shared/work
sudo chmod 1775 /shared/work
```

---

### ÉTAPE 5 : Combiner les permissions spéciales

**On peut combiner SUID, SGID et Sticky Bit :**

```bash
# SGID + Sticky Bit (courant sur dossiers partagés)
chmod 3775 dossier
#     ^
#   3 = 2 (SGID) + 1 (Sticky)

# Vérifier
ls -ld dossier
# drwxrwsr-t 2 root team 4096 Dec 30 10:00 dossier
#       ^  ^
#     SGID Sticky
```

---

**Tableau récapitulatif des combinaisons :**

| Octal | Binaire | Permissions | Usage |
|-------|---------|-------------|-------|
| 0 | 000 | Aucune | Normal |
| 1 | 001 | Sticky | /tmp |
| 2 | 010 | SGID | Dossiers partagés |
| 3 | 011 | SGID + Sticky | Dossiers équipe protégés |
| 4 | 100 | SUID | Programmes privilégiés |
| 5 | 101 | SUID + Sticky | Très rare |
| 6 | 110 | SUID + SGID | Très rare |
| 7 | 111 | Tous | Jamais utilisé |

---

**Exemples de configurations complètes :**

```bash
# Dossier d'équipe avec SGID et permissions
chmod 2775 /shared/team/
# drwxrwsr-x = Owner et Group peuvent tout faire, Others peuvent lire

# Zone temporaire collaborative
chmod 3777 /shared/temp/
# drwxrwsrwt = Tout le monde peut créer, SGID pour groupe, Sticky pour protection

# Programme système avec SUID
chmod 4755 /usr/bin/program
# -rwsr-xr-x = S'exécute avec privilèges owner (root)

# Configuration web avec SGID
chmod 2755 /var/www/project/uploads/
# drwxr-sr-x = Owner peut modifier, groupe hérité, others peuvent lire
```

---

### ÉTAPE 6 : Détecter et auditer les permissions spéciales

**Trouver tous les fichiers avec SUID :**

```bash
# Dans tout le système
sudo find / -type f -perm -4000 2>/dev/null

# Résultat typique :
# /usr/bin/passwd
# /usr/bin/sudo
# /usr/bin/chsh
# /usr/bin/newgrp
# /usr/bin/gpasswd
```

**C'est important pour la sécurité !**

---

**Trouver tous les fichiers avec SGID :**

```bash
sudo find / -type f -perm -2000 2>/dev/null

# Exemples :
# /usr/bin/wall
# /usr/bin/ssh-agent
```

---

**Trouver tous les dossiers avec Sticky Bit :**

```bash
sudo find / -type d -perm -1000 2>/dev/null

# Exemples :
# /tmp
# /var/tmp
```

---

**Script d'audit complet :**

```bash
#!/bin/bash
# audit-special-permissions.sh

echo "[RECHERCHE] Audit des permissions spéciales"
echo "===================================="
echo ""

# 1. Fichiers SUID
echo "[SECURISE] Fichiers avec SUID (4000) :"
SUID_COUNT=$(sudo find / -type f -perm -4000 2>/dev/null | wc -l)
echo "   Total : $SUID_COUNT fichiers"
sudo find / -type f -perm -4000 -ls 2>/dev/null | head -10
echo ""

# 2. Fichiers SGID
echo "[UTILISATEURS] Fichiers avec SGID (2000) :"
SGID_COUNT=$(sudo find / -type f -perm -2000 2>/dev/null | wc -l)
echo "   Total : $SGID_COUNT fichiers"
sudo find / -type f -perm -2000 -ls 2>/dev/null | head -10
echo ""

# 3. Dossiers SGID
echo "[DOSSIER] Dossiers avec SGID :"
SGID_DIR_COUNT=$(sudo find / -type d -perm -2000 2>/dev/null | wc -l)
echo "   Total : $SGID_DIR_COUNT dossiers"
sudo find / -type d -perm -2000 -ls 2>/dev/null | head -10
echo ""

# 4. Dossiers Sticky Bit
echo "[IMPORTANT] Dossiers avec Sticky Bit (1000) :"
STICKY_COUNT=$(sudo find / -type d -perm -1000 2>/dev/null | wc -l)
echo "   Total : $STICKY_COUNT dossiers"
sudo find / -type d -perm -1000 -ls 2>/dev/null | head -10
echo ""

# 5. Permissions suspectes
echo "[ATTENTION]  Fichiers SUID appartenant à des utilisateurs non-root (SUSPECT) :"
sudo find / -type f -perm -4000 ! -user root 2>/dev/null
echo ""

# 6. World-writable avec SUID ou SGID (TRÈS DANGEREUX)
echo "[ALERTE] Fichiers world-writable avec SUID/SGID (CRITIQUE) :"
sudo find / -type f \( -perm -4000 -o -perm -2000 \) -perm -0002 2>/dev/null
echo ""

echo "[OK] Audit terminé"
```

---

**Sauvegarder la liste des SUID pour comparaison future :**

```bash
# Créer une baseline
sudo find / -type f -perm -4000 2>/dev/null | sort > /root/suid-baseline.txt

# Plus tard, comparer
sudo find / -type f -perm -4000 2>/dev/null | sort > /tmp/suid-current.txt
diff /root/suid-baseline.txt /tmp/suid-current.txt

# Nouveaux fichiers SUID = potentielle faille de sécurité !
```

---

### ÉTAPE 7 : Cas d'usage professionnels complets

**Cas 1 : Configurer un dossier de projet d'équipe optimal**

```bash
#!/bin/bash
# setup-team-directory.sh

TEAM_DIR=$1
TEAM_GROUP=$2

if [ -z "$TEAM_DIR" ] || [ -z "$TEAM_GROUP" ]; then
    echo "Usage: $0 <directory> <group>"
    exit 1
fi

# Créer le dossier
sudo mkdir -p "$TEAM_DIR"

# Propriété : root:team
sudo chown root:"$TEAM_GROUP" "$TEAM_DIR"

# Permissions : 2775 (SGID + rwx pour owner et group)
sudo chmod 2775 "$TEAM_DIR"

# Vérifier
ls -ld "$TEAM_DIR"

echo "[OK] Dossier d'équipe configuré"
echo ""
echo "Caractéristiques :"
echo "  - Owner : root (admin gère le dossier)"
echo "  - Group : $TEAM_GROUP (équipe)"
echo "  - Permissions : 2775 (drwxrwsr-x)"
echo "  - SGID : Activé (nouveaux fichiers -> groupe $TEAM_GROUP)"
echo "  - Membres de $TEAM_GROUP peuvent créer/modifier"
echo "  - Others peuvent lire"
```

**Utilisation :**

```bash
chmod +x setup-team-directory.sh
sudo ./setup-team-directory.sh /shared/webdev webdev
```

---

**Cas 2 : Zone de dépôt sécurisée**

```bash
# Zone où les utilisateurs peuvent déposer des fichiers
# Mais ne peuvent pas voir ou modifier les dépôts des autres

sudo mkdir /dropbox
sudo chmod 1733 /dropbox
#             ^^^
#             │││
#             ││└─ Others: -wx (write + execute, PAS read)
#             │└── Group: -wx
#             └─── Owner: rwx

# Vérifier
ls -ld /dropbox
# drwx-wx-wt 2 root root 4096 Dec 30 10:00 /dropbox

# Comportement :
# - Utilisateurs peuvent créer des fichiers (w+x)
# - Utilisateurs NE peuvent PAS lister le contenu (pas de r)
# - Utilisateurs peuvent accéder à leurs fichiers (si connaissent le nom)
# - Utilisateurs NE peuvent PAS supprimer les fichiers des autres (sticky bit)
# - Seul root peut lister et gérer tous les fichiers
```

---

**Cas 3 : Application web avec uploads**

```bash
# Structure web avec uploads sécurisés

# 1. Dossier principal
sudo mkdir -p /var/www/myapp/{public,uploads,private}

# 2. Public : lecture pour tous
sudo chown www-data:www-data /var/www/myapp/public
sudo chmod 755 /var/www/myapp/public

# 3. Uploads : SGID pour groupe www-data
sudo chown www-data:www-data /var/www/myapp/uploads
sudo chmod 2775 /var/www/myapp/uploads

# 4. Private : Stricte
sudo chown www-data:www-data /var/www/myapp/private
sudo chmod 700 /var/www/myapp/private

# Résultat :
ls -ld /var/www/myapp/*
# drwxr-xr-x  2 www-data www-data 4096 Dec 30 10:00 public
# drwxrwsr-x  2 www-data www-data 4096 Dec 30 10:00 uploads
# drwx------  2 www-data www-data 4096 Dec 30 10:00 private
```

---

**Cas 4 : Dossier de collaboration avec protection individuelle**

```bash
# Équipe peut collaborer
# Mais chacun protège ses fichiers contre suppression accidentelle

sudo mkdir /shared/collab
sudo chgrp developers /shared/collab
sudo chmod 3775 /shared/collab
#             ^
#           SGID (2) + Sticky (1) = 3

# Vérifier
ls -ld /shared/collab
# drwxrwsr-t 2 root developers 4096 Dec 30 10:00 /shared/collab
#       ^  ^
#     SGID Sticky

# Comportement :
# - Membres de 'developers' peuvent créer/modifier
# - Nouveaux fichiers -> groupe 'developers' (SGID)
# - Seul le propriétaire peut supprimer son fichier (Sticky)
```

---

### ÉTAPE 8 : Sécurité et bonnes pratiques

**[ROUGE] DANGERS DES PERMISSIONS SPÉCIALES :**

**1. SUID sur scripts shell = TRÈS DANGEREUX**

```bash
# [X] JAMAIS FAIRE ÇA
cat > dangerous.sh << 'EOF'
#!/bin/bash
rm -rf /
EOF

sudo chown root:root dangerous.sh
sudo chmod 4755 dangerous.sh
# Un utilisateur lambda peut détruire le système !
```

**La plupart des systèmes modernes IGNORENT le SUID sur les scripts shell pour cette raison.**

---

**2. SUID sur binaires vulnérables**

```bash
# Si un programme SUID a un bug d'injection de commande
# -> Attaquant peut exécuter des commandes comme root

# Exemple de code vulnérable (C) :
/*
system("ls " + user_input);  // VULNÉRABLE !
// Si user_input = "; rm -rf /"
// Commande exécutée : ls ; rm -rf /
*/
```

---

**3. World-writable avec SUID/SGID**

```bash
# CRITIQUE : Fichier modifiable par tous avec SUID
-rwsrwxrwx 1 root root 100 program
# N'importe qui peut modifier le programme
# Puis l'exécuter avec privilèges root !

# Détecter ce genre de problèmes :
sudo find / -type f \( -perm -4000 -o -perm -2000 \) -perm -0002 2>/dev/null
```

---

**[OK] BONNES PRATIQUES :**

**1. Minimiser les binaires SUID/SGID**

```bash
# Auditer régulièrement
sudo find / -type f -perm -4000 -ls 2>/dev/null > suid-audit.txt

# Retirer SUID des programmes qui n'en ont pas besoin
sudo chmod u-s /path/to/program
```

---

**2. Utiliser des alternatives plus sûres**

```bash
# Au lieu de SUID, utiliser :

# A. sudo avec configuration précise
# /etc/sudoers :
# alice ALL=(root) NOPASSWD: /usr/bin/specific-command

# B. Capabilities Linux (plus fin que SUID)
sudo setcap cap_net_bind_service=+ep /usr/bin/myapp
# Permet de binder sur port <1024 sans être root

# C. PolicyKit pour GUI
# Permet des élévations contrôlées via interface graphique
```

---

**3. Protéger les dossiers SGID**

```bash
# Dossier SGID avec permissions appropriées
sudo chmod 2770 /shared/team/  # Pas 2777 !

# Seuls owner et group peuvent accéder
# Others ne peut rien faire
```

---

**4. Sticky bit sur dossiers publics**

```bash
# Toujours mettre sticky bit sur les dossiers world-writable
sudo chmod 1777 /tmp
# Jamais 777 sans sticky bit !
```

---

**5. Monitoring continu**

```bash
# Cron job pour surveiller les nouveaux SUID
# /etc/cron.daily/check-suid

#!/bin/bash
CURRENT=$(sudo find / -type f -perm -4000 2>/dev/null | sort)
BASELINE=$(cat /root/suid-baseline.txt 2>/dev/null)

if [ "$CURRENT" != "$BASELINE" ]; then
    echo "[ALERTE] ALERTE : Nouveaux fichiers SUID détectés !" | \
        mail -s "Security Alert: SUID" root
fi
```

---

### ÉTAPE 9 : Exercices pratiques

**Exercice 1 : Configurer un dossier de logs partagés**

```bash
# Objectif : Plusieurs applications écrivent dans /var/log/apps
# Tous les logs doivent avoir le même groupe 'syslog'
# Seuls les admins peuvent lire

# Solution :
sudo mkdir /var/log/apps
sudo chgrp syslog /var/log/apps
sudo chmod 2770 /var/log/apps

# Vérifier
ls -ld /var/log/apps
# drwxrws--- 2 root syslog 4096 Dec 30 10:00 /var/log/apps

# Test
sudo -u syslog touch /var/log/apps/test.log
ls -l /var/log/apps/test.log
# -rw-r--r-- 1 syslog syslog 0 Dec 30 10:00 test.log
# [OK] Groupe = syslog (hérité du dossier grâce à SGID)
```

---

**Exercice 2 : Créer une zone temporaire d'équipe**

```bash
# Objectif : Dossier temporaire pour l'équipe 'developers'
# - Membres peuvent créer/modifier
# - Fichiers héritent du groupe
# - Protection contre suppression accidentelle

# Solution :
sudo mkdir /tmp/dev-workspace
sudo chgrp developers /tmp/dev-workspace
sudo chmod 3775 /tmp/dev-workspace
#             ^
#           SGID (2) + Sticky (1)

# Vérifier
ls -ld /tmp/dev-workspace
# drwxrwsr-t 2 root developers 4096 Dec 30 10:00 /tmp/dev-workspace

# Test 1 : Héritage du groupe
touch /tmp/dev-workspace/myfile.txt
ls -l /tmp/dev-workspace/myfile.txt
# -rw-r--r-- 1 alice developers 0 Dec 30 10:00 myfile.txt
# [OK] Groupe = developers

# Test 2 : Protection sticky
sudo -u bob rm /tmp/dev-workspace/myfile.txt
# rm: cannot remove: Operation not permitted
# [OK] Bob ne peut pas supprimer le fichier d'Alice
```

---

**Exercice 3 : Auditer un système**

```bash
# Créer un rapport complet des permissions spéciales

#!/bin/bash
# full-special-perms-audit.sh

REPORT="special-perms-report-$(date +%Y%m%d).txt"

{
    echo "============================================"
    echo "Audit des permissions spéciales"
    echo "Date : $(date)"
    echo "Système : $(hostname)"
    echo "============================================"
    echo ""
    
    echo "1. FICHIERS SUID :"
    sudo find / -type f -perm -4000 -ls 2>/dev/null
    echo ""
    
    echo "2. FICHIERS SGID :"
    sudo find / -type f -perm -2000 -ls 2>/dev/null
    echo ""
    
    echo "3. DOSSIERS SGID :"
    sudo find / -type d -perm -2000 -ls 2>/dev/null
    echo ""
    
    echo "4. DOSSIERS STICKY BIT :"
    sudo find / -type d -perm -1000 -ls 2>/dev/null
    echo ""
    
    echo "5. PROBLÈMES POTENTIELS :"
    echo "   a) SUID non-root :"
    sudo find / -type f -perm -4000 ! -user root 2>/dev/null
    echo ""
    
    echo "   b) World-writable avec SUID/SGID :"
    sudo find / -type f \( -perm -4000 -o -perm -2000 \) -perm -0002 2>/dev/null
    echo ""
    
    echo "============================================"
    echo "Fin du rapport"
    echo "============================================"
} | tee "$REPORT"

echo "Rapport sauvegardé : $REPORT"
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : SUID fonctionne**

```bash
# Créer un programme test
cat > test-suid.c << 'EOF'
#include <stdio.h>
#include <unistd.h>
int main() {
    printf("EUID: %d\n", geteuid());
    return 0;
}
EOF

gcc test-suid.c -o test-suid
sudo chown root:root test-suid
sudo chmod 4755 test-suid

# Tester
EUID=$(./test-suid | grep EUID | cut -d: -f2 | tr -d ' ')

if [ "$EUID" = "0" ]; then
    echo "[OK] Test 1 OK : SUID fonctionne"
else
    echo "[X] Test 1 FAIL : EUID=$EUID (attendu 0)"
fi

sudo rm test-suid
```

---

**Test 2 : SGID sur dossier fonctionne**

```bash
# Créer dossier avec SGID
sudo mkdir test-sgid-dir
sudo chgrp sudo test-sgid-dir 2>/dev/null || sudo chgrp adm test-sgid-dir
sudo chmod 2775 test-sgid-dir

# Créer un fichier dedans
touch test-sgid-dir/file.txt

# Vérifier le groupe
FILE_GROUP=$(stat -c "%G" test-sgid-dir/file.txt)
DIR_GROUP=$(stat -c "%G" test-sgid-dir)

if [ "$FILE_GROUP" = "$DIR_GROUP" ]; then
    echo "[OK] Test 2 OK : SGID sur dossier fonctionne"
else
    echo "[X] Test 2 FAIL : File=$FILE_GROUP, Dir=$DIR_GROUP"
fi

sudo rm -rf test-sgid-dir
```

---

**Test 3 : Sticky bit fonctionne**

```bash
# Créer dossier avec sticky bit
mkdir test-sticky
chmod 1777 test-sticky

# Alice crée un fichier
touch test-sticky/alice-file

# Vérifier si sticky bit est actif
PERMS=$(stat -c "%a" test-sticky)

if [ "${PERMS:0:1}" = "1" ]; then
    echo "[OK] Test 3 OK : Sticky bit activé"
else
    echo "[X] Test 3 FAIL : Permissions=$PERMS"
fi

rm -rf test-sticky
```

---

**Test 4 : Détecter les permissions spéciales**

```bash
# Créer fichiers test
touch test-normal test-suid test-sgid test-sticky

chmod 644 test-normal
chmod 4755 test-suid
chmod 2755 test-sgid
chmod 1755 test-sticky

# Tester find
SUID=$(find . -name "test-suid" -perm -4000 | wc -l)
SGID=$(find . -name "test-sgid" -perm -2000 | wc -l)
STICKY=$(find . -name "test-sticky" -perm -1000 | wc -l)

if [ "$SUID" = "1" ] && [ "$SGID" = "1" ] && [ "$STICKY" = "1" ]; then
    echo "[OK] Test 4 OK : Détection des permissions spéciales"
else
    echo "[X] Test 4 FAIL : SUID=$SUID, SGID=$SGID, STICKY=$STICKY"
fi

rm test-normal test-suid test-sgid test-sticky
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Les 3 permissions spéciales**

| Permission | Bit | Octal | Symbole | Sur fichier | Sur dossier |
|------------|-----|-------|---------|-------------|-------------|
| SUID | 4 | 4000 | u+s | Exec avec owner | N/A |
| SGID | 2 | 2000 | g+s | Exec avec group | Hérite groupe |
| Sticky | 1 | 1000 | o+t | N/A | Protection suppression |

---

**2. Notation octale à 4 chiffres**

```
chmod 4755 fichier
      vvvv
      ││││
      │││└─ Others
      ││└── Group
      │└─── Owner
      └──── Spécial (4=SUID, 2=SGID, 1=Sticky)
```

---

**3. Affichage visuel**

```
-rwsr-xr-x  -> SUID (s minuscule = SUID + x)
-rwSr-xr-x  -> SUID sans x (S majuscule = erreur config)
drwxrwsr-x  -> SGID sur dossier
drwxrwxrwt  -> Sticky bit
```

---

**4. Cas d'usage principaux**

- **SUID** : Programmes privilégiés (passwd, sudo, ping)
- **SGID fichier** : Programmes avec privilèges groupe
- **SGID dossier** : * Collaboration équipe (héritage groupe)
- **Sticky bit** : Protection dossiers publics (/tmp)

---

**5. Sécurité**

- [ATTENTION] SUID = Risque majeur si mal utilisé
- [RECHERCHE] Auditer régulièrement
- [INTERDIT] Jamais SUID sur scripts shell
- [OK] Minimiser les binaires SUID
- [SECURITE] Utiliser alternatives (sudo, capabilities)

---

**6. Commandes de détection**

```bash
# SUID
find / -type f -perm -4000

# SGID
find / -type f -perm -2000
find / -type d -perm -2000

# Sticky
find / -type d -perm -1000
```

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Capabilities Linux (alternative au SUID)**

```bash
# Au lieu de SUID root, donner une capability précise
# Exemple : Permettre à un programme de binder sur port <1024

# Sans SUID (méthode moderne) :
sudo setcap cap_net_bind_service=+ep /usr/bin/node

# Vérifier
getcap /usr/bin/node
# /usr/bin/node = cap_net_bind_service+ep

# Maintenant node peut binder sur port 80 sans être root !

# Lister toutes les capabilities
sudo getcap -r / 2>/dev/null
```

---

**2. Extended Attributes (xattr)**

```bash
# Attributs étendus pour protection supplémentaire

# Rendre un fichier immutable (même root ne peut pas modifier)
sudo chattr +i fichier.txt

# Tenter de supprimer
sudo rm fichier.txt
# rm: cannot remove 'fichier.txt': Operation not permitted

# Retirer l'attribut
sudo chattr -i fichier.txt

# Voir les attributs
lsattr fichier.txt
```

---

**3. AppArmor / SELinux (Mandatory Access Control)**

```bash
# Contrôle d'accès obligatoire
# Restreint ce que les processus peuvent faire, même avec SUID

# AppArmor (Ubuntu)
sudo aa-status

# SELinux (RedHat/CentOS)
getenforce

# Ces systèmes ajoutent une couche de sécurité supplémentaire
# même pour les binaires SUID
```

---

**4. Surveillance en temps réel**

```bash
# Avec inotify-tools
sudo apt install inotify-tools

# Surveiller /tmp en temps réel
inotifywait -m -r -e create,delete,modify /tmp/

# Surveiller les modifications de permissions
# (nécessite auditd ou solution similaire)
```

---

**5. Hardening script complet**

```bash
#!/bin/bash
# security-hardening.sh

echo "[VERROUILLE] Hardening des permissions spéciales..."

# 1. Retirer SUID/SGID des programmes non essentiels
for prog in /usr/bin/at /usr/bin/wall; do
    if [ -f "$prog" ]; then
        sudo chmod u-s,g-s "$prog"
        echo "[OK] Retiré SUID/SGID de $prog"
    fi
done

# 2. Ajouter sticky bit sur dossiers world-writable
for dir in /tmp /var/tmp; do
    if [ -d "$dir" ]; then
        sudo chmod +t "$dir"
        echo "[OK] Sticky bit ajouté sur $dir"
    fi
done

# 3. Vérifier /tmp
TMP_PERMS=$(stat -c "%a" /tmp)
if [ "$TMP_PERMS" != "1777" ]; then
    echo "[ATTENTION]  /tmp devrait être 1777, actuellement $TMP_PERMS"
    sudo chmod 1777 /tmp
fi

# 4. Créer une liste de référence SUID
sudo find / -type f -perm -4000 2>/dev/null | sort > /root/suid-baseline.txt
echo "[OK] Baseline SUID créée : /root/suid-baseline.txt"

echo "[OK] Hardening terminé"
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3.4

**[OK] BRAVO ! Tu maîtrises maintenant les permissions spéciales Linux !**

**Ce que tu as appris :**

**SUID :**
- Exécution avec privilèges du propriétaire
- Utilisé par passwd, sudo
- Risques de sécurité majeurs

**SGID :**
- Sur fichier : Exécution avec privilèges du groupe
- Sur dossier : Héritage du groupe (collaboration)
- Essentiel pour travail d'équipe

**Sticky Bit :**
- Protection contre suppression
- Utilisé sur /tmp
- Sécurité dossiers publics

**Compétences acquises :**
- [OK] Permissions spéciales (niveau expert)
- [OK] Sécurité avancée
- [OK] Configuration dossiers partagés
- [OK] Audit de sécurité
- [OK] Détection de vulnérabilités

**Temps moyen de réalisation :** 2-3 heures

---

# [BRAVO] CONCLUSION SECTION 3 : PERMISSIONS & PROPRIÉTÉ COMPLÈTE

**[TROPHEE] FÉLICITATIONS ! Section 3 100% terminée !**

**Récapitulatif des 4 exercices :**

| Exercice | Niveau | Compétences |
|----------|--------|-------------|
| 3.1 | [VERT] Débutant | Lire et comprendre les permissions |
| 3.2 | [JAUNE] Intermédiaire | Modifier avec chmod |
| 3.3 | [JAUNE] Intermédiaire | chown, chgrp, utilisateurs/groupes |
| 3.4 | [ROUGE] Expert | SUID, SGID, Sticky Bit |

**Niveau atteint : EXPERT en Sécurité et Permissions Linux ! [SECURISE]**

---

**Tu es maintenant capable de :**
- [OK] Lire n'importe quelle permission
- [OK] Configurer des permissions adaptées
- [OK] Gérer utilisateurs et groupes
- [OK] Utiliser les permissions spéciales
- [OK] Auditer la sécurité d'un système
- [OK] Détecter des vulnérabilités
- [OK] Configurer des environnements collaboratifs sécurisés

---

C'était une section dense mais cruciale ! Tu as maintenant une maîtrise complète des permissions Linux, ce qui est fondamental pour l'administration système.

Tu veux que je continue avec une nouvelle section ? J'ai plusieurs options :
- **Section 4 : Processus & Services**
- **Section 5 : Réseau & Connectivité**
- **Section 6 : Archives & Compression**
- **Section 7 : Variables & Environnement**

Qu'est-ce qui t'intéresse ?

# [DOCS] SECTION 4 : PROCESSUS & SERVICES

# [VERT] EXERCICE 4.1 : COMPRENDRE LES PROCESSUS (DÉBUTANT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Identifier quels programmes tournent sur le serveur
- Comprendre qui utilise les ressources (CPU, RAM)
- Diagnostiquer pourquoi un serveur est lent
- Trouver quel processus écoute sur un port
- Surveiller l'état du système en temps réel
- Comprendre la hiérarchie des processus (parent/enfant)

**Problématique :** Un processus = un programme en cours d'exécution. Il faut savoir les identifier, les lister, les comprendre et les surveiller.

### Cahier des charges

- Comprendre ce qu'est un processus
- Lister les processus avec `ps`
- Surveiller en temps réel avec `top`
- Comprendre les PIDs (Process IDs)
- Identifier les processus parents/enfants
- Lire les informations de `/proc`
- Comprendre les états des processus
- Utiliser `pgrep`, `pidof`, `pstree`

### Contraintes

- Commandes : `ps`, `top`, `htop`, `pgrep`, `pidof`, `pstree`
- Lecture du système de fichiers `/proc`
- Temps estimé : 1h - 1h30

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre ce qu'est un processus
- [OK] Utiliser `ps` pour lister les processus
- [OK] Utiliser `top` pour le monitoring en temps réel
- [OK] Comprendre les PIDs et PPIDs
- [OK] Lire les informations dans `/proc`
- [OK] Comprendre les états des processus
- [OK] Trouver rapidement un processus
- [OK] Visualiser la hiérarchie des processus
- [OK] Analyser l'utilisation des ressources

---

## [DOCS] PRÉREQUIS

- Sections 1, 2 et 3 complètes
- Terminal Linux fonctionnel
- Quelques processus en cours d'exécution

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Qu'est-ce qu'un processus ?

**Définition :**

Un **processus** est une instance d'un programme en cours d'exécution. C'est l'unité de base de l'exécution dans un système d'exploitation.

---

**Concepts clés :**

**1. Programme vs Processus**

```
Programme (fichier sur disque)
    v
Exécution
    v
Processus (en mémoire)
```

**Exemple :**
- `/usr/bin/firefox` = Programme (fichier)
- Quand tu lances Firefox -> Crée un processus
- Tu peux lancer Firefox 3 fois -> 3 processus différents

---

**2. Chaque processus a :**

| Attribut | Description | Exemple |
|----------|-------------|---------|
| **PID** | Process ID (identifiant unique) | 1234 |
| **PPID** | Parent Process ID | 1000 |
| **UID** | User ID (qui exécute) | 1000 (alice) |
| **État** | Running, Sleeping, Zombie, etc. | S (Sleeping) |
| **Priorité** | Niveau de priorité CPU | 20 |
| **Mémoire** | RAM utilisée | 150 MB |
| **CPU** | % de CPU utilisé | 5.2% |
| **Commande** | Programme exécuté | /usr/bin/firefox |

---

**3. Hiérarchie des processus**

```
systemd (PID 1)
    ├─ sshd (PID 500)
    │   └─ bash (PID 1234)
    │       └─ vim (PID 1500)
    │
    ├─ apache2 (PID 600)
    │   ├─ apache2 (PID 601)
    │   ├─ apache2 (PID 602)
    │   └─ apache2 (PID 603)
    │
    └─ mysqld (PID 700)
```

**Tous les processus descendent de `systemd` (ou `init`) avec PID 1.**

---

### ÉTAPE 2 : Commande `ps` - Liste des processus

**Syntaxe de base :**

```
ps [OPTIONS]
```

**`ps` = Process Status**

---

**Test 1 : ps sans options (processus du terminal courant)**

```bash
ps
```

**Résultat typique :**
```
  PID TTY          TIME CMD
 1234 pts/0    00:00:00 bash
 5678 pts/0    00:00:00 ps
```

**Colonnes :**
- **PID** : Process ID
- **TTY** : Terminal associé
- **TIME** : Temps CPU cumulé
- **CMD** : Commande exécutée

---

**Test 2 : ps aux (TOUS les processus, format détaillé)**

```bash
ps aux
```

**Résultat :**
```
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3 169820 13424 ?        Ss   08:00   0:01 /sbin/init
root       500  0.0  0.1  72304  5940 ?        Ss   08:00   0:00 /usr/sbin/sshd
alice     1234  0.0  0.2  21532  9876 pts/0    Ss   08:30   0:00 -bash
alice     5678  0.0  0.1  38392  3456 pts/0    R+   10:00   0:00 ps aux
```

**Colonnes importantes :**

| Colonne | Description | Exemple |
|---------|-------------|---------|
| **USER** | Utilisateur propriétaire | alice |
| **PID** | Process ID | 1234 |
| **%CPU** | % CPU utilisé | 0.0 |
| **%MEM** | % RAM utilisée | 0.2 |
| **VSZ** | Mémoire virtuelle (KB) | 21532 |
| **RSS** | Mémoire physique (KB) | 9876 |
| **TTY** | Terminal (? = aucun) | pts/0 |
| **STAT** | État du processus | S, R, D, Z |
| **START** | Heure de démarrage | 08:30 |
| **TIME** | Temps CPU cumulé | 0:00 |
| **COMMAND** | Commande complète | -bash |

---

**Test 3 : ps -ef (format standard Unix)**

```bash
ps -ef
```

**Résultat :**
```
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:00 ?        00:00:01 /sbin/init
root       500     1  0 08:00 ?        00:00:00 /usr/sbin/sshd
alice     1234   500  0 08:30 pts/0    00:00:00 -bash
alice     5678  1234  0 10:00 pts/0    00:00:00 ps -ef
```

**Colonnes supplémentaires :**
- **PPID** : Parent Process ID (processus parent)
- **C** : Utilisation CPU
- **STIME** : Heure de démarrage

---

**Test 4 : Filtrer les processus**

```bash
# Processus d'un utilisateur spécifique
ps -u alice

# Processus d'un programme spécifique
ps aux | grep firefox

# Processus d'un PID spécifique
ps -p 1234

# Arborescence (hiérarchie parent/enfant)
ps auxf

# Ou mieux :
ps -ef --forest
```

---

**Options courantes de `ps` :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `aux` | Tous les processus, format BSD | `ps aux` |
| `-ef` | Tous les processus, format Unix | `ps -ef` |
| `-u user` | Processus d'un utilisateur | `ps -u alice` |
| `-p PID` | Processus spécifique | `ps -p 1234` |
| `--forest` | Arborescence | `ps -ef --forest` |
| `-o` | Colonnes personnalisées | `ps -eo pid,user,cmd` |

---

**Test 5 : Personnaliser les colonnes avec `-o`**

```bash
# Afficher seulement PID, USER et COMMAND
ps -eo pid,user,cmd

# Afficher avec utilisation mémoire
ps -eo pid,user,%mem,rss,cmd --sort=-%mem | head -20

# Afficher les 10 processus qui utilisent le plus de CPU
ps -eo pid,user,%cpu,cmd --sort=-%cpu | head -10
```

**`-o` = Output format (format de sortie personnalisé)**

---

### ÉTAPE 3 : États des processus

**Chaque processus a un ÉTAT (STAT) :**

| Code | État | Description |
|------|------|-------------|
| **R** | Running | En cours d'exécution ou prêt |
| **S** | Sleeping (interruptible) | En attente d'un événement |
| **D** | Sleeping (uninterruptible) | Attente I/O (disque, réseau) |
| **T** | sTopped | Arrêté (Ctrl+Z) |
| **Z** | Zombie | Terminé mais pas nettoyé par parent |
| **I** | Idle | Processus noyau inactif |

**Modificateurs supplémentaires :**

| Code | Signification |
|------|---------------|
| **<** | Haute priorité |
| **N** | Basse priorité |
| **L** | Pages verrouillées en mémoire |
| **s** | Leader de session |
| **l** | Multi-thread |
| **+** | Processus au premier plan |

---

**Exemples d'états :**

```bash
ps aux | head -5
```

```
USER  PID  STAT
root    1  Ss     # S = Sleeping, s = session leader
alice 1234 Ss     # Sleeping, session leader
alice 5678 R+     # Running, au premier plan (+)
mysql  700 Sl     # Sleeping, multi-thread (l)
```

---

**État Zombie (Z) - Important à comprendre :**

```bash
# Un processus devient zombie quand :
# 1. Il se termine
# 2. Son parent ne récupère pas son code de sortie (wait())

# Voir les zombies
ps aux | grep 'Z'

# Les zombies ne consomment pas de ressources
# Mais trop de zombies = problème avec le processus parent

# Solution : Redémarrer le processus parent
```

---

### ÉTAPE 4 : Le système de fichiers `/proc`

**`/proc` = Système de fichiers virtuel (pseudo-filesystem)**

Chaque processus a un dossier `/proc/PID/` avec toutes ses informations.

---

**Test 1 : Explorer /proc pour le shell courant**

```bash
# Obtenir le PID du shell courant
echo $$
# 1234

# Explorer son dossier
ls -la /proc/1234/
```

**Résultat :**
```
dr-xr-xr-x   9 alice alice 0 Dec 30 10:00 .
dr-xr-xr-x 340 root  root  0 Dec 30 08:00 ..
-r--r--r--   1 alice alice 0 Dec 30 10:00 cmdline      # Ligne de commande
lrwxrwxrwx   1 alice alice 0 Dec 30 10:00 cwd -> /home/alice  # Dossier courant
-r--------   1 alice alice 0 Dec 30 10:00 environ      # Variables d'environnement
lrwxrwxrwx   1 alice alice 0 Dec 30 10:00 exe -> /bin/bash    # Exécutable
dr-x------   2 alice alice 0 Dec 30 10:00 fd           # File descriptors
-r--r--r--   1 alice alice 0 Dec 30 10:00 limits       # Limites de ressources
-r--r--r--   1 alice alice 0 Dec 30 10:00 status       # État détaillé
```

---

**Test 2 : Lire les informations d'un processus**

```bash
# Ligne de commande du processus
cat /proc/1234/cmdline
# bash

# Variables d'environnement
cat /proc/1234/environ | tr '\0' '\n'

# Mémoire utilisée
cat /proc/1234/status | grep VmSize
# VmSize: 21532 kB

# État du processus
cat /proc/1234/status | grep State
# State: S (sleeping)
```

---

**Test 3 : Fichiers importants dans /proc**

| Fichier | Description |
|---------|-------------|
| `/proc/PID/cmdline` | Commande de lancement |
| `/proc/PID/cwd` | Dossier de travail courant |
| `/proc/PID/exe` | Lien vers l'exécutable |
| `/proc/PID/environ` | Variables d'environnement |
| `/proc/PID/fd/` | File descriptors ouverts |
| `/proc/PID/status` | État détaillé |
| `/proc/PID/maps` | Carte mémoire |
| `/proc/PID/limits` | Limites de ressources |

---

**Test 4 : Informations système globales**

```bash
# Informations CPU
cat /proc/cpuinfo

# Mémoire
cat /proc/meminfo

# Version du noyau
cat /proc/version

# Temps depuis le démarrage (uptime)
cat /proc/uptime
# 12345.67 98765.43
#    ^       ^
# Uptime  Idle time (en secondes)

# Moyenne de charge
cat /proc/loadavg
# 0.50 0.75 1.00 2/345 12345
#  ^    ^    ^    ^     ^
# 1min 5min 15min running/total last_PID
```

---

### ÉTAPE 5 : Commande `top` - Monitoring en temps réel

**`top` = Table Of Processes (tableau des processus)**

Affichage dynamique et interactif des processus.

---

**Test 1 : Lancer top**

```bash
top
```

**Interface de top :**

```
top - 10:15:32 up 2:15,  2 users,  load average: 0.50, 0.75, 1.00
Tasks: 245 total,   1 running, 244 sleeping,   0 stopped,   0 zombie
%Cpu(s):  5.2 us,  2.1 sy,  0.0 ni, 92.5 id,  0.0 wa,  0.1 hi,  0.1 si,  0.0 st
MiB Mem :   7850.5 total,   2341.2 free,   3245.8 used,   2263.5 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   4234.1 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 1234 alice     20   0 1234567 123456  45678 S   5.2   1.5   0:12.34 firefox
 5678 alice     20   0  234567  23456  12345 S   2.1   0.3   0:01.23 chrome
    1 root      20   0  169820  13424   8765 S   0.0   0.2   0:01.45 systemd
```

---

**Lignes d'en-tête expliquées :**

**Ligne 1 : Résumé système**
```
top - 10:15:32 up 2:15,  2 users,  load average: 0.50, 0.75, 1.00
      v         v         v                        v    v    v
   Heure    Uptime   Utilisateurs            Load avg (1, 5, 15 min)
```

**Load Average :**
- < 1.0 = Système peu chargé
- = nombre de CPUs = Chargé
- > nombre de CPUs = Surchargé

---

**Ligne 2 : Tâches**
```
Tasks: 245 total,   1 running, 244 sleeping,   0 stopped,   0 zombie
```

---

**Ligne 3 : CPU**
```
%Cpu(s):  5.2 us,  2.1 sy,  0.0 ni, 92.5 id,  0.0 wa,  0.1 hi,  0.1 si,  0.0 st
          v        v        v        v        v        v        v        v
        user    system    nice     idle     wait   hard IRQ  soft IRQ  steal
```

**Signification :**
- **us** (user) : Temps en espace utilisateur
- **sy** (system) : Temps en espace noyau
- **ni** (nice) : Processus de basse priorité
- **id** (idle) : Temps d'inactivité (% CPU libre)
- **wa** (wait) : Attente I/O (disque/réseau)
- **hi** : Interruptions matérielles
- **si** : Interruptions logicielles
- **st** (steal) : Volé par hyperviseur (VM)

**Si `wa` est élevé -> Goulot d'étranglement I/O (disque lent)**

---

**Lignes 4-5 : Mémoire**
```
MiB Mem :   7850.5 total,   2341.2 free,   3245.8 used,   2263.5 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   4234.1 avail Mem
```

**Mémoire disponible réelle = free + buff/cache**

---

**Colonnes du tableau :**

| Colonne | Description |
|---------|-------------|
| **PID** | Process ID |
| **USER** | Utilisateur |
| **PR** | Priorité |
| **NI** | Nice value (-20 à 19) |
| **VIRT** | Mémoire virtuelle totale |
| **RES** | Mémoire physique (RAM) |
| **SHR** | Mémoire partagée |
| **S** | État (R, S, D, Z, T) |
| **%CPU** | % CPU utilisé |
| **%MEM** | % RAM utilisée |
| **TIME+** | Temps CPU cumulé |
| **COMMAND** | Nom du processus |

---

**Commandes interactives dans top :**

| Touche | Action |
|--------|--------|
| **h** | Aide |
| **q** | Quitter |
| **k** | Tuer un processus (kill) |
| **r** | Renice (changer priorité) |
| **1** | Afficher chaque CPU séparément |
| **Shift+M** | Trier par mémoire |
| **Shift+P** | Trier par CPU (défaut) |
| **Shift+T** | Trier par temps |
| **u** | Filtrer par utilisateur |
| **c** | Afficher commande complète |
| **Space** | Rafraîchir immédiatement |

---

**Test 2 : Options de top**

```bash
# Lancer avec délai de rafraîchissement personnalisé
top -d 1   # Rafraîchir chaque seconde (défaut = 3s)

# Mode batch (non-interactif, pour scripts)
top -b -n 1 > top-snapshot.txt

# Afficher seulement les processus d'un utilisateur
top -u alice

# Afficher un nombre limité de processus
top -n 1 | head -20
```

---

### ÉTAPE 6 : `htop` - Version améliorée de top

**`htop` est une version modernisée et plus conviviale de `top`.**

**Installation :**

```bash
sudo apt update
sudo apt install htop
```

---

**Lancer htop :**

```bash
htop
```

**Avantages de htop :**

[OK] **Interface colorée et intuitive**
[OK] **Utilisation souris (clic pour sélectionner)**
[OK] **Barre de visualisation CPU/RAM**
[OK] **Recherche incrémentale (F3)**
[OK] **Arborescence des processus (F5)**
[OK] **Tri facile (F6)**
[OK] **Kill, nice, etc. plus faciles**

---

**Touches de htop :**

| Touche | Action |
|--------|--------|
| **F1** | Aide |
| **F2** | Configuration |
| **F3** | Rechercher |
| **F4** | Filtrer |
| **F5** | Arborescence |
| **F6** | Trier |
| **F9** | Kill |
| **F10** | Quitter |
| **Space** | Marquer/démarquer |
| **u** | Filtrer utilisateur |
| **t** | Vue arborescence |
| **/** | Rechercher |

---

### ÉTAPE 7 : Commandes rapides - pgrep, pidof, pstree

**`pgrep` - Trouver PID par nom**

```bash
# Trouver le PID de firefox
pgrep firefox
# 1234
# 5678

# Avec le nom complet
pgrep -a firefox
# 1234 /usr/lib/firefox/firefox
# 5678 /usr/lib/firefox/firefox

# Processus d'un utilisateur
pgrep -u alice

# Processus le plus récent
pgrep -n firefox

# Processus le plus ancien
pgrep -o firefox
```

---

**`pidof` - PID d'un programme**

```bash
# Plus simple que pgrep
pidof firefox
# 1234 5678

# Premier PID seulement
pidof -s firefox
# 1234
```

---

**`pstree` - Arborescence des processus**

```bash
# Voir la hiérarchie complète
pstree

# Arborescence avec PIDs
pstree -p

# Arborescence d'un utilisateur
pstree alice

# Arborescence d'un processus spécifique
pstree -p 1234
```

**Exemple de sortie :**

```
systemd(1)─┬─ModemManager(623)─┬─{ModemManager}(638)
           │                    └─{ModemManager}(641)
           ├─NetworkManager(625)─┬─{NetworkManager}(630)
           │                      └─{NetworkManager}(632)
           ├─sshd(500)───sshd(1200)───bash(1234)───vim(1500)
           ├─apache2(600)─┬─apache2(601)
           │              ├─apache2(602)
           │              └─apache2(603)
           └─mysqld(700)─┬─{mysqld}(701)
                         ├─{mysqld}(702)
                         └─{mysqld}(703)
```

---

### ÉTAPE 8 : Cas d'usage pratiques

**Cas 1 : Trouver le processus qui utilise le plus de CPU**

```bash
# Méthode 1 : ps avec tri
ps aux --sort=-%cpu | head -10

# Méthode 2 : top en mode batch
top -b -n 1 | head -20

# Méthode 3 : htop (interactif)
htop
# Appuyer sur F6, choisir %CPU
```

---

**Cas 2 : Trouver le processus qui utilise le plus de RAM**

```bash
# ps avec tri par mémoire
ps aux --sort=-%mem | head -10

# Avec colonnes personnalisées
ps -eo pid,user,%mem,rss,cmd --sort=-%mem | head -10
```

---

**Cas 3 : Lister tous les processus d'un utilisateur**

```bash
# ps
ps -u alice

# pgrep
pgrep -u alice -a

# top
top -u alice
```

---

**Cas 4 : Trouver tous les processus Apache**

```bash
# Méthode 1 : ps + grep
ps aux | grep apache2 | grep -v grep

# Méthode 2 : pgrep
pgrep -a apache2

# Méthode 3 : pidof
pidof apache2
```

---

**Cas 5 : Voir la hiérarchie parent/enfant**

```bash
# Arborescence complète
ps -ef --forest

# Ou avec pstree
pstree -p

# Pour un processus spécifique
pstree -p 1234
```

---

**Cas 6 : Trouver quel processus écoute sur un port**

```bash
# Méthode 1 : netstat
sudo netstat -tulpn | grep :80
# tcp  0  0 0.0.0.0:80  0.0.0.0:*  LISTEN  600/apache2

# Méthode 2 : ss (plus moderne)
sudo ss -tulpn | grep :80

# Méthode 3 : lsof
sudo lsof -i :80
# COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
# apache2  600     root    4u  IPv6  12345      0t0  TCP *:80 (LISTEN)

# Méthode 4 : fuser
sudo fuser 80/tcp
# 80/tcp: 600
```

---

**Cas 7 : Surveiller un processus spécifique**

```bash
# Surveiller les ressources d'un PID
watch -n 1 "ps -p 1234 -o pid,user,%cpu,%mem,cmd"

# Ou avec top
top -p 1234
```

---

### ÉTAPE 9 : Scripts d'analyse

**Script 1 : Top 10 processus par CPU**

```bash
#!/bin/bash
# top-cpu.sh

echo "[HOT] Top 10 processus par CPU"
echo "============================"
echo ""

ps aux --sort=-%cpu | head -11 | awk '
NR==1 {
    printf "%-10s %6s %6s %6s %s\n", "USER", "PID", "%CPU", "%MEM", "COMMAND"
    printf "%-10s %6s %6s %6s %s\n", "----", "---", "----", "----", "-------"
}
NR>1 {
    printf "%-10s %6s %6.1f %6.1f %s\n", $1, $2, $3, $4, $11
}
'
```

---

**Script 2 : Top 10 processus par mémoire**

```bash
#!/bin/bash
# top-memory.sh

echo "[SAUVEGARDE] Top 10 processus par mémoire"
echo "================================"
echo ""

ps aux --sort=-%mem | head -11 | awk '
NR==1 {
    printf "%-10s %6s %8s %6s %s\n", "USER", "PID", "RSS (MB)", "%MEM", "COMMAND"
    printf "%-10s %6s %8s %6s %s\n", "----", "---", "--------", "----", "-------"
}
NR>1 {
    rss_mb = $6 / 1024
    printf "%-10s %6s %8.1f %6.1f %s\n", $1, $2, rss_mb, $4, $11
}
'
```

---

**Script 3 : Résumé système**

```bash
#!/bin/bash
# system-summary.sh

echo "[GRAPHIQUE] Résumé du système"
echo "===================="
echo ""

# Uptime
echo "[TEMPS]  Uptime :"
uptime
echo ""

# CPU
echo "[ECRAN]  CPU :"
echo "Nombre de CPUs : $(nproc)"
echo "Load average : $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
echo ""

# Mémoire
echo "[SAUVEGARDE] Mémoire :"
free -h | awk '
NR==2 {printf "Total: %s | Utilisé: %s | Libre: %s\n", $2, $3, $4}
'
echo ""

# Processus
echo "[SYNC] Processus :"
echo "Total : $(ps aux | wc -l)"
echo "Running : $(ps aux | grep -c ' R ')"
echo "Sleeping : $(ps aux | grep -c ' S ')"
echo "Zombie : $(ps aux | grep -c ' Z ')"
echo ""

# Top 5 CPU
echo "[HOT] Top 5 processus CPU :"
ps aux --sort=-%cpu | head -6 | tail -5 | awk '{printf "  %s (%s) - %.1f%%\n", $11, $2, $3}'
echo ""

# Top 5 Mémoire
echo "[SAUVEGARDE] Top 5 processus Mémoire :"
ps aux --sort=-%mem | head -6 | tail -5 | awk '{printf "  %s (%s) - %.1f%%\n", $11, $2, $4}'
```

---

**Script 4 : Chercher un processus par nom**

```bash
#!/bin/bash
# find-process.sh

PROCESS_NAME=$1

if [ -z "$PROCESS_NAME" ]; then
    echo "Usage: $0 <process-name>"
    exit 1
fi

echo "[RECHERCHE] Recherche de processus : $PROCESS_NAME"
echo "=========================================="
echo ""

# Méthode 1 : pgrep
echo "Méthode 1 : pgrep"
pgrep -a "$PROCESS_NAME"
echo ""

# Méthode 2 : ps + grep
echo "Méthode 2 : ps + grep"
ps aux | grep "$PROCESS_NAME" | grep -v grep
echo ""

# Méthode 3 : pidof
echo "Méthode 3 : pidof"
pidof "$PROCESS_NAME" && echo "PIDs : $(pidof $PROCESS_NAME)" || echo "Aucun processus trouvé"
echo ""

# Arborescence
PIDS=$(pgrep "$PROCESS_NAME")
if [ -n "$PIDS" ]; then
    echo "Arborescence :"
    for pid in $PIDS; do
        pstree -p "$pid"
    done
fi
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : ps fonctionne**

```bash
# Vérifier que ps retourne des processus
COUNT=$(ps aux | wc -l)

if [ "$COUNT" -gt 10 ]; then
    echo "[OK] Test 1 OK : ps liste $COUNT processus"
else
    echo "[X] Test 1 FAIL : Trop peu de processus"
fi
```

---

**Test 2 : Trouver son propre shell**

```bash
# Le shell courant doit être dans la liste
MY_PID=$$
FOUND=$(ps -p $MY_PID | grep -c bash)

if [ "$FOUND" -gt 0 ]; then
    echo "[OK] Test 2 OK : Shell trouvé (PID $MY_PID)"
else
    echo "[X] Test 2 FAIL"
fi
```

---

**Test 3 : /proc existe et est lisible**

```bash
if [ -d "/proc/$$" ]; then
    echo "[OK] Test 3 OK : /proc/$$ existe"
    
    # Vérifier cmdline
    if [ -f "/proc/$$/cmdline" ]; then
        echo "   [OK] cmdline lisible"
    fi
else
    echo "[X] Test 3 FAIL"
fi
```

---

**Test 4 : pgrep fonctionne**

```bash
# Chercher le shell courant
PGREP_RESULT=$(pgrep -u $(whoami) bash)

if [ -n "$PGREP_RESULT" ]; then
    echo "[OK] Test 4 OK : pgrep trouve bash"
else
    echo "[X] Test 4 FAIL"
fi
```

---

**Test 5 : top peut s'exécuter en mode batch**

```bash
# top en mode non-interactif
TOP_OUTPUT=$(top -b -n 1 | head -1)

if [ -n "$TOP_OUTPUT" ]; then
    echo "[OK] Test 5 OK : top fonctionne"
else
    echo "[X] Test 5 FAIL"
fi
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Concepts de base**
- Processus = programme en cours d'exécution
- Chaque processus a un PID unique
- Hiérarchie : tous descendent de PID 1 (systemd/init)
- PPID = Parent Process ID

**2. Commandes essentielles**
- `ps aux` : Liste tous les processus
- `ps -ef` : Format Unix standard
- `top` : Monitoring en temps réel
- `htop` : Version améliorée de top
- `pgrep` : Trouver PID par nom
- `pidof` : PID d'un programme
- `pstree` : Arborescence

**3. États des processus**
- **R** : Running (en cours)
- **S** : Sleeping (en attente)
- **D** : Uninterruptible sleep (I/O)
- **Z** : Zombie (terminé mais pas nettoyé)
- **T** : Stopped (arrêté)

**4. Le système /proc**
- `/proc/PID/` : Infos sur chaque processus
- `/proc/cpuinfo` : Infos CPU
- `/proc/meminfo` : Infos mémoire
- Système de fichiers virtuel

**5. Colonnes importantes**
- **PID** : Process ID
- **%CPU** : Utilisation CPU
- **%MEM** : Utilisation RAM
- **VSZ** : Mémoire virtuelle
- **RSS** : Mémoire physique
- **STAT** : État du processus

**6. Monitoring**
- Load average : < nombre de CPUs = OK
- CPU wait (wa) élevé = problème I/O
- Mémoire : free + buff/cache = vraiment disponible
- Zombies : problème si nombreux

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. glances - Monitoring complet**

```bash
# Installer
sudo apt install glances

# Lancer
glances

# Mode serveur
glances -s

# Mode client
glances -c localhost
```

---

**2. atop - Analyse historique**

```bash
# Installer
sudo apt install atop

# Lancer
atop

# Voir l'historique
atop -r /var/log/atop/atop_20241230
```

---

**3. Monitoring en continu avec collectd**

```bash
# Installer
sudo apt install collectd collectd-utils

# Collecter des métriques système
# Analyse long-terme
```

---

**4. BPF/eBPF - Traçage avancé**

```bash
# Outils BPF pour analyse poussée
sudo apt install bpfcc-tools

# Tracer les appels système
sudo execsnoop

# Tracer les fichiers ouverts
sudo opensnoop
```

---

**5. Limites de ressources avec cgroups**

```bash
# Voir les cgroups d'un processus
cat /proc/$$/cgroup

# Limiter CPU/RAM d'un processus (avec systemd)
systemd-run --unit=myapp --slice=user.slice -p CPUQuota=50% ./myapp
```

---

## [COURS] CONCLUSION DE L'EXERCICE 4.1

**[OK] Félicitations ! Tu comprends maintenant les processus Linux !**

**Ce que tu as appris :**
- Concept de processus (PID, PPID, états)
- Lister avec `ps` (aux, -ef)
- Monitoring avec `top` et `htop`
- Système de fichiers `/proc`
- États des processus (R, S, D, Z, T)
- Recherche rapide (`pgrep`, `pidof`, `pstree`)
- Analyse de ressources (CPU, RAM)
- Scripts de monitoring

**Compétences acquises :**
- [OK] Compréhension des processus (niveau intermédiaire)
- [OK] Monitoring système
- [OK] Diagnostic de performance
- [OK] Navigation dans /proc

**Temps moyen de réalisation :** 1h - 1h30

**Prochaine étape :** Exercice 4.2 - Gérer les processus (kill, signals, jobs) ! [OBJECTIF]

---

Je continue avec l'exercice 4.2 ?

# [JAUNE] EXERCICE 4.2 : GÉRER LES PROCESSUS - KILL, SIGNALS, JOBS (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Arrêter proprement des services/applications
- Forcer l'arrêt de processus bloqués
- Gérer des tâches en arrière-plan (background)
- Relancer des services avec rechargement de configuration
- Gérer des processus qui ne répondent plus
- Exécuter des commandes longues sans bloquer le terminal
- Maintenir des processus actifs après déconnexion

**Problématique :** Les processus ne s'arrêtent pas toujours gentiment. Il faut savoir comment les contrôler, les arrêter proprement ou les forcer, et les gérer en arrière-plan.

### Cahier des charges

- Comprendre les signaux Unix/Linux
- Utiliser `kill` pour envoyer des signaux
- Utiliser `killall` et `pkill`
- Gérer les jobs (tâches)
- Utiliser `fg` et `bg` (foreground/background)
- Utiliser `nohup` et `disown`
- Comprendre `Ctrl+C`, `Ctrl+Z`
- Gérer les processus zombies et bloqués
- Arrêter proprement vs forcer

### Contraintes

- Commandes : `kill`, `killall`, `pkill`, `fg`, `bg`, `jobs`, `nohup`, `disown`
- Signaux : SIGTERM, SIGKILL, SIGHUP, SIGINT, etc.
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre les signaux Linux
- [OK] Utiliser `kill` avec différents signaux
- [OK] Utiliser `killall` et `pkill`
- [OK] Gérer les jobs (arrière-plan/premier-plan)
- [OK] Utiliser `Ctrl+C` et `Ctrl+Z`
- [OK] Exécuter des processus en arrière-plan
- [OK] Maintenir des processus après déconnexion
- [OK] Arrêter proprement vs forcer
- [OK] Gérer les processus problématiques
- [OK] Créer des scripts de gestion

---

## [DOCS] PRÉREQUIS

- Exercice 4.1 terminé (Comprendre les processus)
- Compréhension des PIDs
- Commandes ps, pgrep

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre les signaux (Signals)

**Qu'est-ce qu'un signal ?**

Un **signal** est un message envoyé à un processus pour lui demander d'effectuer une action (arrêt, pause, rechargement config, etc.).

**Chaque signal a :**
- Un **numéro** (ex: 9, 15)
- Un **nom** (ex: SIGKILL, SIGTERM)
- Une **action par défaut**

---

**Liste des signaux principaux :**

```bash
# Voir tous les signaux
kill -l
```

**Résultat :**
```
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
...
```

---

**Signaux les plus importants :**

| Signal | Numéro | Nom | Description | Peut être intercepté ? |
|--------|--------|-----|-------------|------------------------|
| **SIGTERM** | 15 | Terminate | Arrêt propre (défaut de kill) | [OK] Oui |
| **SIGKILL** | 9 | Kill | Arrêt forcé immédiat | [X] Non |
| **SIGHUP** | 1 | Hangup | Rechargement config | [OK] Oui |
| **SIGINT** | 2 | Interrupt | Interruption (Ctrl+C) | [OK] Oui |
| **SIGQUIT** | 3 | Quit | Quitter avec core dump | [OK] Oui |
| **SIGSTOP** | 19 | Stop | Pause forcée | [X] Non |
| **SIGTSTP** | 20 | Terminal Stop | Pause (Ctrl+Z) | [OK] Oui |
| **SIGCONT** | 18 | Continue | Reprendre après pause | [OK] Oui |
| **SIGUSR1** | 10 | User 1 | Signal utilisateur 1 | [OK] Oui |
| **SIGUSR2** | 12 | User 2 | Signal utilisateur 2 | [OK] Oui |

---

**Différence SIGTERM vs SIGKILL :**

```
SIGTERM (15) - "Arrêt propre"
├─ Peut être intercepté par le processus
├─ Permet de faire du nettoyage (fermer fichiers, sauvegarder, etc.)
├─ Le processus peut refuser/ignorer
└─ [OK] TOUJOURS ESSAYER EN PREMIER

SIGKILL (9) - "Arrêt forcé"
├─ NE PEUT PAS être intercepté
├─ Tue immédiatement (noyau tue le processus)
├─ Pas de nettoyage possible
└─ [ATTENTION] DERNIER RECOURS seulement
```

---

**Analogie :**

```
SIGTERM = "S'il te plaît, arrête-toi"
SIGKILL = "JE T'ARRÊTE DE FORCE !"
```

---

### ÉTAPE 2 : Commande `kill` - Envoyer des signaux

**Syntaxe de base :**

```
kill [OPTIONS] [SIGNAL] PID
```

**`kill` = Envoyer un signal à un processus**

---

**Créer des processus de test :**

```bash
# Créer le dossier de travail
mkdir -p ~/process-control-exercise
cd ~/process-control-exercise

# Script 1 : Processus simple qui boucle
cat > loop.sh << 'EOF'
#!/bin/bash
echo "Processus démarré (PID: $$)"
while true; do
    echo "$(date): Je tourne..."
    sleep 2
done
EOF

chmod +x loop.sh

# Script 2 : Processus qui capture SIGTERM
cat > trap-term.sh << 'EOF'
#!/bin/bash
echo "Processus avec trap (PID: $$)"

# Intercepter SIGTERM
trap 'echo "SIGTERM reçu ! Nettoyage..."; sleep 2; echo "Terminé proprement"; exit 0' TERM

while true; do
    echo "$(date): Je tourne..."
    sleep 2
done
EOF

chmod +x trap-term.sh

# Script 3 : Processus qui ignore SIGTERM
cat > ignore-term.sh << 'EOF'
#!/bin/bash
echo "Processus qui ignore SIGTERM (PID: $$)"

# Ignorer SIGTERM
trap '' TERM

while true; do
    echo "$(date): Je tourne et j'ignore SIGTERM..."
    sleep 2
done
EOF

chmod +x ignore-term.sh
```

---

**Test 1 : kill basique (SIGTERM par défaut)**

```bash
# Lancer un processus
./loop.sh &
# [1] 12345
# Processus démarré (PID: 12345)

# Noter le PID
PID=$!
echo "PID: $PID"

# Attendre quelques secondes
sleep 5

# Tuer avec SIGTERM (signal par défaut)
kill $PID

# Vérifier qu'il est terminé
ps -p $PID
# Aucun résultat = processus arrêté
```

**Par défaut, `kill` envoie SIGTERM (15).**

---

**Test 2 : kill avec signal spécifique**

```bash
# Lancer le processus avec trap
./trap-term.sh &
PID=$!

# Attendre un peu
sleep 3

# Envoyer SIGTERM explicitement
kill -TERM $PID
# OU
kill -15 $PID
# OU
kill -s SIGTERM $PID

# Observer la sortie
# SIGTERM reçu ! Nettoyage...
# Terminé proprement
```

**Trois syntaxes équivalentes :**
- `kill -TERM PID`
- `kill -15 PID`
- `kill -s SIGTERM PID`

---

**Test 3 : SIGKILL - Forcer l'arrêt**

```bash
# Lancer le processus qui ignore SIGTERM
./ignore-term.sh &
PID=$!

# Essayer SIGTERM (ne marchera pas)
kill -TERM $PID
sleep 3
# Le processus continue de tourner

# Vérifier qu'il tourne encore
ps -p $PID
# Oui, toujours là

# Forcer avec SIGKILL
kill -9 $PID
# OU
kill -KILL $PID

# Vérifier qu'il est mort
ps -p $PID
# Plus de processus
```

**SIGKILL (-9) tue TOUJOURS, sans possibilité de défense.**

---

**Test 4 : Autres signaux utiles**

```bash
# Lancer un processus
./loop.sh &
PID=$!

# SIGSTOP : Mettre en pause (stopper)
kill -STOP $PID

# Vérifier l'état
ps -o pid,stat,cmd -p $PID
# PID  STAT CMD
# 1234 T    ./loop.sh
#      ^
#    T = Stopped

# SIGCONT : Reprendre
kill -CONT $PID

# Vérifier l'état
ps -o pid,stat,cmd -p $PID
# PID  STAT CMD
# 1234 S    ./loop.sh
#      ^
#    S = Sleeping (normal)

# Arrêter proprement
kill $PID
```

---

**Test 5 : SIGHUP - Rechargement de configuration**

**SIGHUP est utilisé par beaucoup de démons pour recharger leur config sans redémarrer.**

```bash
# Créer un script qui capture SIGHUP
cat > reload-config.sh << 'EOF'
#!/bin/bash
CONFIG_FILE="config.txt"

# Fonction de chargement de config
load_config() {
    echo "Chargement de la configuration..."
    if [ -f "$CONFIG_FILE" ]; then
        VALUE=$(cat "$CONFIG_FILE")
        echo "Configuration: $VALUE"
    else
        echo "Pas de fichier de config"
    fi
}

# Capturer SIGHUP
trap 'echo ""; echo "SIGHUP reçu ! Rechargement..."; load_config' HUP

# Chargement initial
load_config

# Boucle
while true; do
    sleep 5
done
EOF

chmod +x reload-config.sh

# Créer une config initiale
echo "Version 1.0" > config.txt

# Lancer le script
./reload-config.sh &
PID=$!

# Attendre
sleep 3

# Modifier la config
echo "Version 2.0" > config.txt

# Envoyer SIGHUP
kill -HUP $PID

# Observer : le script recharge la config sans redémarrer
# SIGHUP reçu ! Rechargement...
# Chargement de la configuration...
# Configuration: Version 2.0

# Arrêter
kill $PID
```

**Cas d'usage réel : Nginx**

```bash
# Modifier la config nginx
sudo vim /etc/nginx/nginx.conf

# Recharger sans arrêter le service
sudo nginx -s reload
# OU
sudo kill -HUP $(cat /var/run/nginx.pid)
```

---

**Options de kill :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-l` | Lister tous les signaux | `kill -l` |
| `-s SIGNAL` | Spécifier signal par nom | `kill -s TERM 1234` |
| `-SIGNAL` | Spécifier signal par nom | `kill -TERM 1234` |
| `-NUM` | Spécifier signal par numéro | `kill -15 1234` |

---

### ÉTAPE 3 : Commandes `killall` et `pkill`

**`killall` - Tuer par nom de processus**

**Syntaxe :**

```
killall [OPTIONS] [SIGNAL] NOM_PROCESSUS
```

---

**Test 1 : killall basique**

```bash
# Lancer plusieurs instances
./loop.sh &
./loop.sh &
./loop.sh &

# Vérifier
pgrep -a loop.sh

# Tuer TOUS les loop.sh
killall loop.sh

# Vérifier qu'ils sont tous morts
pgrep loop.sh
# Aucun résultat
```

---

**Test 2 : killall avec signal spécifique**

```bash
# Lancer processus
./loop.sh &
./loop.sh &

# Envoyer SIGTERM explicitement
killall -TERM loop.sh

# Ou SIGKILL
killall -9 loop.sh
```

---

**Test 3 : killall avec options**

```bash
# Lancer processus
./loop.sh &

# Mode interactif (demande confirmation)
killall -i loop.sh
# Kill loop.sh(12345) ? (y/N)

# Mode verbeux
killall -v loop.sh
# Killed loop.sh(12345) with signal 15

# Attendre la fin
killall -w loop.sh
# Attend que tous les processus soient terminés

# Tuer seulement les processus d'un utilisateur
killall -u alice firefox
```

---

**`pkill` - Tuer par pattern**

**Plus flexible que killall !**

**Syntaxe :**

```
pkill [OPTIONS] [SIGNAL] PATTERN
```

---

**Test 1 : pkill basique**

```bash
# Lancer processus
./loop.sh &
./trap-term.sh &

# Tuer tous les processus dont le nom contient "loop"
pkill loop

# Tuer avec pattern regex
pkill "^loop"  # Commence par "loop"
```

---

**Test 2 : pkill avec options avancées**

```bash
# Lancer processus
./loop.sh &

# Tuer seulement les processus de l'utilisateur alice
pkill -u alice loop

# Tuer seulement le processus le plus ancien
pkill -o loop

# Tuer seulement le processus le plus récent
pkill -n loop

# Voir ce qui serait tué (sans tuer)
pgrep -a loop

# Tuer avec signal spécifique
pkill -KILL loop
```

---

**Comparaison kill, killall, pkill :**

| Commande | Cible | Exemple | Flexibilité |
|----------|-------|---------|-------------|
| **kill** | PID spécifique | `kill 1234` | * Précis |
| **killall** | Nom exact | `killall firefox` | ** Nom complet |
| **pkill** | Pattern/regex | `pkill fire` | *** Plus flexible |

---

**[ATTENTION] ATTENTION avec killall et pkill !**

```bash
# DANGEREUX : Peut tuer plus que prévu
killall python
# Tue TOUS les scripts Python !

# Plus sûr : Vérifier d'abord
pgrep -a python
# Voir ce qui serait tué

# Puis tuer
pkill python
```

---

### ÉTAPE 4 : Jobs - Contrôle des tâches

**Qu'est-ce qu'un job ?**

Un **job** (tâche) est un processus lancé depuis le shell qui peut être en :
- **Foreground (premier plan)** : Bloque le terminal
- **Background (arrière-plan)** : Libère le terminal

---

**Créer des processus de test :**

```bash
# Script qui prend du temps
cat > long-task.sh << 'EOF'
#!/bin/bash
echo "Tâche longue démarrée..."
for i in {1..30}; do
    echo "Étape $i/30"
    sleep 1
done
echo "Tâche terminée !"
EOF

chmod +x long-task.sh
```

---

**Test 1 : Lancer en arrière-plan avec `&`**

```bash
# Lancer en arrière-plan
./long-task.sh &

# Résultat :
# [1] 12345
#  ^   ^
# Job  PID
# Tâche longue démarrée...

# Le terminal est libre !
# Tu peux continuer à taper des commandes

# Voir les jobs en cours
jobs

# Résultat :
# [1]+  Running                 ./long-task.sh &
```

---

**Test 2 : Ctrl+Z - Suspendre un processus**

```bash
# Lancer en premier plan (sans &)
./long-task.sh

# La tâche bloque le terminal
# Étape 1/30
# Étape 2/30
# ...

# Appuyer sur Ctrl+Z
# ^Z
# [1]+  Stopped                 ./long-task.sh

# Le processus est mis en PAUSE
# Le terminal est libre

# Vérifier
jobs
# [1]+  Stopped                 ./long-task.sh

# État du processus
ps -o pid,stat,cmd | grep long-task
# 12345 T    ./long-task.sh
#       ^
#     T = Stopped
```

---

**Test 3 : `bg` - Reprendre en arrière-plan**

```bash
# Reprendre le job en arrière-plan
bg

# Ou spécifier le numéro de job
bg %1

# Résultat :
# [1]+ ./long-task.sh &

# Le job reprend en arrière-plan
# Vérifier
jobs
# [1]+  Running                 ./long-task.sh &
```

---

**Test 4 : `fg` - Ramener au premier plan**

```bash
# Lancer en arrière-plan
./long-task.sh &
# [1] 12345

# Ramener au premier plan
fg

# Ou spécifier le numéro de job
fg %1

# Le processus reprend au premier plan
# Le terminal est bloqué jusqu'à la fin
```

---

**Test 5 : Gérer plusieurs jobs**

```bash
# Lancer plusieurs tâches en arrière-plan
./long-task.sh &
# [1] 12345

./loop.sh &
# [2] 12346

./trap-term.sh &
# [3] 12347

# Lister les jobs
jobs

# Résultat :
# [1]   Running                 ./long-task.sh &
# [2]-  Running                 ./loop.sh &
# [3]+  Running                 ./trap-term.sh &
#      ^                                        ^
#    + = Job courant                          - = Job précédent

# Ramener job 2 au premier plan
fg %2

# Tuer job 1
kill %1

# Vérifier
jobs
# [2]+  Running                 ./loop.sh &
# [3]-  Running                 ./trap-term.sh &
```

---

**Commandes de gestion des jobs :**

| Commande | Description | Exemple |
|----------|-------------|---------|
| `&` | Lancer en arrière-plan | `./script.sh &` |
| `Ctrl+Z` | Suspendre (pause) | Appuyer pendant exécution |
| `jobs` | Lister les jobs | `jobs` |
| `fg` | Ramener au premier plan | `fg %1` |
| `bg` | Reprendre en arrière-plan | `bg %1` |
| `%N` | Référencer job N | `kill %1`, `fg %2` |
| `%%` | Job courant (+) | `fg %%` |
| `%+` | Job courant | `fg %+` |
| `%-` | Job précédent | `fg %-` |

---

**Workflow typique :**

```bash
# 1. Lancer une commande longue
find / -name "*.log" 2>/dev/null

# 2. Oups, ça prend trop de temps !
#    Appuyer sur Ctrl+Z
# ^Z
# [1]+  Stopped

# 3. Reprendre en arrière-plan
bg

# 4. Continuer à travailler dans le terminal

# 5. Vérifier de temps en temps
jobs

# 6. Ramener au premier plan si besoin
fg
```

---

### ÉTAPE 5 : `nohup` et `disown` - Processus persistants

**Problème :** Quand tu fermes le terminal, les processus lancés depuis ce terminal sont tués (reçoivent SIGHUP).

**Solutions :**
1. **nohup** : Lancer avec immunité au SIGHUP
2. **disown** : Détacher un job déjà lancé

---

**Test 1 : Comportement par défaut (sans nohup)**

```bash
# Lancer un processus en arrière-plan
./long-task.sh &

# Fermer le terminal ou se déconnecter
# -> Le processus est TUÉ
```

---

**Test 2 : `nohup` - Immunité au SIGHUP**

```bash
# Lancer avec nohup
nohup ./long-task.sh &

# Résultat :
# [1] 12345
# nohup: ignoring input and appending output to 'nohup.out'

# La sortie est redirigée vers nohup.out
cat nohup.out
# Tâche longue démarrée...
# Étape 1/30
# ...

# Maintenant, fermer le terminal
# -> Le processus CONTINUE de tourner !

# Dans un nouveau terminal :
pgrep -a long-task
# 12345 ./long-task.sh
```

**nohup redirige automatiquement :**
- **stdout** et **stderr** -> `nohup.out`
- **stdin** -> `/dev/null`

---

**Test 3 : nohup avec redirection personnalisée**

```bash
# Rediriger vers un fichier spécifique
nohup ./long-task.sh > output.log 2>&1 &

# Vérifier
cat output.log
```

---

**Test 4 : `disown` - Détacher un job**

```bash
# Lancer normalement
./long-task.sh &
# [1] 12345

# Lister les jobs
jobs
# [1]+  Running                 ./long-task.sh &

# Détacher le job du shell
disown %1

# Vérifier
jobs
# Aucun job !

# Mais le processus tourne toujours
pgrep -a long-task
# 12345 ./long-task.sh

# Fermer le terminal
# -> Le processus CONTINUE
```

---

**Test 5 : disown avec options**

```bash
# Lancer plusieurs jobs
./loop.sh &
./trap-term.sh &

# Détacher tous les jobs
disown -a

# Détacher le job courant
disown

# Garder le job mais retirer du contrôle du shell
disown -h %1
```

---

**Comparaison nohup vs disown :**

| Aspect | nohup | disown |
|--------|-------|--------|
| **Quand ?** | Avant de lancer | Après avoir lancé |
| **Syntaxe** | `nohup cmd &` | Lancer puis `disown` |
| **Sortie** | Vers `nohup.out` | Conserve redirection |
| **Flexibilité** | * Prévu à l'avance | ** Après coup |

---

**Workflow pour processus long :**

```bash
# Méthode 1 : nohup (si tu sais que ça sera long)
nohup ./long-process.sh > process.log 2>&1 &

# Méthode 2 : disown (si tu réalises après coup)
./long-process.sh &
# Oups, ça va prendre longtemps...
disown

# Méthode 3 : screen ou tmux (encore mieux !)
screen
./long-process.sh
# Ctrl+A puis D pour détacher
```

---

### ÉTAPE 6 : Ctrl+C vs Ctrl+Z vs Ctrl+\

**Les 3 raccourcis clavier pour contrôler les processus :**

| Raccourci | Signal | Action | Peut être intercepté ? |
|-----------|--------|--------|------------------------|
| **Ctrl+C** | SIGINT (2) | Interrompre/arrêter | [OK] Oui |
| **Ctrl+Z** | SIGTSTP (20) | Suspendre (pause) | [OK] Oui |
| **Ctrl+\** | SIGQUIT (3) | Quitter + core dump | [OK] Oui |

---

**Test 1 : Ctrl+C (SIGINT)**

```bash
# Lancer un processus
./long-task.sh

# Appuyer sur Ctrl+C
# ^C

# Le processus reçoit SIGINT et s'arrête
```

---

**Test 2 : Processus qui intercepte Ctrl+C**

```bash
# Script qui capture SIGINT
cat > trap-int.sh << 'EOF'
#!/bin/bash
echo "Appuie sur Ctrl+C pour voir..."

trap 'echo ""; echo "Ctrl+C intercepté ! Je ne meurs pas !"; echo ""' INT

while true; do
    echo "Je tourne... (Ctrl+C ne marchera pas)"
    sleep 2
done
EOF

chmod +x trap-int.sh
./trap-int.sh

# Appuyer sur Ctrl+C
# Ctrl+C intercepté ! Je ne meurs pas !

# Le processus continue !

# Pour le tuer : Ctrl+Z puis kill
# ^Z
# [1]+  Stopped
kill %1
```

---

**Test 3 : Ctrl+Z (SIGTSTP)**

```bash
# Lancer
./long-task.sh

# Appuyer sur Ctrl+Z
# ^Z
# [1]+  Stopped

# Le processus est en PAUSE
jobs
# [1]+  Stopped                 ./long-task.sh

# Reprendre en arrière-plan
bg

# Reprendre en premier plan
fg
```

---

**Test 4 : Ctrl+\ (SIGQUIT)**

```bash
# Lancer
./long-task.sh

# Appuyer sur Ctrl+\
# ^\Quit (core dumped)

# Génère un core dump (pour debug)
ls -l core
# Si activé : core file créé
```

---

### ÉTAPE 7 : Cas d'usage professionnels

**Cas 1 : Redémarrer un service proprement**

```bash
#!/bin/bash
# restart-service.sh

SERVICE_NAME="myapp"
PIDFILE="/var/run/$SERVICE_NAME.pid"

# Lire le PID
if [ -f "$PIDFILE" ]; then
    PID=$(cat "$PIDFILE")
    
    echo "Arrêt de $SERVICE_NAME (PID: $PID)..."
    
    # Essayer SIGTERM d'abord
    kill -TERM $PID
    
    # Attendre jusqu'à 10 secondes
    for i in {1..10}; do
        if ! ps -p $PID > /dev/null 2>&1; then
            echo "Arrêté proprement"
            break
        fi
        echo "Attente... ($i/10)"
        sleep 1
    done
    
    # Si toujours vivant, forcer
    if ps -p $PID > /dev/null 2>&1; then
        echo "Forçage de l'arrêt avec SIGKILL..."
        kill -9 $PID
        sleep 1
    fi
else
    echo "Pas de PID file trouvé"
fi

# Démarrer le service
echo "Démarrage de $SERVICE_NAME..."
/usr/local/bin/$SERVICE_NAME &
echo $! > "$PIDFILE"
echo "Démarré (PID: $(cat $PIDFILE))"
```

---

**Cas 2 : Tuer tous les processus d'un utilisateur**

```bash
#!/bin/bash
# kill-user-processes.sh

USER=$1

if [ -z "$USER" ]; then
    echo "Usage: $0 <username>"
    exit 1
fi

echo "[ATTENTION]  Arrêt de tous les processus de $USER..."

# Lister d'abord
echo "Processus qui seront arrêtés :"
ps -u $USER

read -p "Continuer ? (y/N) " confirm

if [ "$confirm" = "y" ]; then
    # SIGTERM d'abord
    pkill -TERM -u $USER
    sleep 5
    
    # SIGKILL pour les récalcitrants
    pkill -KILL -u $USER
    
    echo "[OK] Terminé"
else
    echo "Annulé"
fi
```

---

**Cas 3 : Nettoyer les processus zombies**

```bash
#!/bin/bash
# clean-zombies.sh

echo "[ZOMBIE] Recherche de processus zombies..."

# Trouver les zombies
ZOMBIES=$(ps aux | awk '$8=="Z" {print $2}')

if [ -z "$ZOMBIES" ]; then
    echo "[OK] Aucun zombie trouvé"
    exit 0
fi

echo "Zombies trouvés :"
ps aux | awk '$8=="Z"'

echo ""
echo "Pour nettoyer les zombies, il faut redémarrer leur processus PARENT"
echo ""

# Pour chaque zombie, trouver le parent
for zombie in $ZOMBIES; do
    PARENT=$(ps -o ppid= -p $zombie)
    echo "Zombie PID: $zombie | Parent PID: $PARENT"
    ps -p $PARENT -o pid,user,cmd
done

echo ""
echo "Commandes suggérées :"
for zombie in $ZOMBIES; do
    PARENT=$(ps -o ppid= -p $zombie)
    echo "kill -HUP $PARENT  # Ou kill -TERM $PARENT"
done
```

---

**Cas 4 : Tâche longue avec progression**

```bash
#!/bin/bash
# long-job.sh

LOGFILE="long-job-$(date +%Y%m%d_%H%M%S).log"

echo "Démarrage de la tâche longue..."
echo "Log : $LOGFILE"
echo "PID : $$"

# Rediriger sortie vers log
exec > >(tee -a "$LOGFILE") 2>&1

# Capture Ctrl+C
trap 'echo ""; echo "Interruption reçue ! Nettoyage..."; cleanup; exit 130' INT TERM

cleanup() {
    echo "Nettoyage en cours..."
    # Votre code de nettoyage
    echo "Nettoyage terminé"
}

# Tâche longue
for i in {1..100}; do
    echo "[$(date +%H:%M:%S)] Étape $i/100"
    sleep 1
done

echo "Tâche terminée !"
```

**Utilisation :**

```bash
# Lancer avec nohup
nohup ./long-job.sh &

# Ou avec disown
./long-job.sh &
disown

# Suivre la progression
tail -f long-job-*.log
```

---

**Cas 5 : Relancer automatiquement un service qui crash**

```bash
#!/bin/bash
# watchdog.sh

SERVICE_CMD="./myapp"
CHECK_INTERVAL=5

echo "[RECHERCHE] Watchdog démarré pour : $SERVICE_CMD"

while true; do
    # Vérifier si le processus tourne
    if ! pgrep -f "$SERVICE_CMD" > /dev/null; then
        echo "[$(date)] [ATTENTION]  Service mort ! Redémarrage..."
        $SERVICE_CMD &
        echo "[$(date)] [OK] Service redémarré (PID: $!)"
    fi
    
    sleep $CHECK_INTERVAL
done
```

---

### ÉTAPE 8 : Gestion des processus bloqués

**Processus bloqué = Processus dans l'état D (uninterruptible sleep)**

```bash
# Identifier les processus bloqués
ps aux | awk '$8 ~ /D/ {print}'
```

**État D = Attend I/O (disque, NFS, etc.)**

**[ATTENTION] On ne peut PAS tuer un processus en état D avec SIGKILL !**

---

**Diagnostic :**

```bash
# Voir ce que fait le processus
sudo cat /proc/PID/stack

# Exemple de sortie :
# [<0>] wait_on_page_bit_common+0x123/0x456
# [<0>] generic_file_read_iter+0x789/0xabc
```

**Causes fréquentes :**
1. Disque dur défaillant
2. Montage NFS qui ne répond plus
3. Problème réseau avec stockage distant

**Solutions :**
1. Attendre (le processus finira par se débloquer ou crasher)
2. Réparer la cause (disque, NFS, réseau)
3. Rebooter (dernier recours)

---

**Script de diagnostic :**

```bash
#!/bin/bash
# diagnose-stuck-process.sh

PID=$1

if [ -z "$PID" ]; then
    echo "Usage: $0 <PID>"
    exit 1
fi

echo "[RECHERCHE] Diagnostic du processus $PID"
echo "================================"
echo ""

# État
echo "État :"
ps -o pid,stat,cmd -p $PID
echo ""

# Stack trace
echo "Stack trace :"
sudo cat /proc/$PID/stack 2>/dev/null || echo "Pas d'accès au stack"
echo ""

# Fichiers ouverts
echo "Fichiers ouverts :"
sudo lsof -p $PID 2>/dev/null | head -20
echo ""

# Activité I/O
echo "Activité I/O :"
sudo iotop -P -p $PID -b -n 1 2>/dev/null || echo "iotop non installé"
echo ""

# Recommandations
echo "Recommandations :"
STATE=$(ps -o stat= -p $PID | sed 's/[^A-Z]//g')

if [[ "$STATE" == *"D"* ]]; then
    echo "[ATTENTION]  Processus en état D (uninterruptible sleep)"
    echo "   Probablement bloqué sur I/O"
    echo "   Vérifier :"
    echo "   - Santé du disque dur"
    echo "   - Montages NFS"
    echo "   - Connexions réseau"
fi
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : kill fonctionne**

```bash
# Lancer un processus
sleep 30 &
PID=$!

# Tuer
kill $PID

# Vérifier
sleep 1
ps -p $PID > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "[OK] Test 1 OK : kill fonctionne"
else
    echo "[X] Test 1 FAIL"
fi
```

---

**Test 2 : SIGTERM vs SIGKILL**

```bash
# Script qui ignore SIGTERM
cat > test-signals.sh << 'EOF'
#!/bin/bash
trap '' TERM
while true; do sleep 1; done
EOF
chmod +x test-signals.sh

./test-signals.sh &
PID=$!

# SIGTERM ne marche pas
kill -TERM $PID
sleep 2

if ps -p $PID > /dev/null 2>&1; then
    echo "[OK] SIGTERM ignoré (correct)"
    
    # SIGKILL marche
    kill -9 $PID
    sleep 1
    
    if ! ps -p $PID > /dev/null 2>&1; then
        echo "[OK] Test 2 OK : SIGKILL tue toujours"
    fi
fi

rm test-signals.sh
```

---

**Test 3 : Jobs fonctionnent**

```bash
# Lancer en background
sleep 30 &

# Vérifier jobs
COUNT=$(jobs | wc -l)

if [ "$COUNT" -gt 0 ]; then
    echo "[OK] Test 3 OK : jobs fonctionne"
    
    # Nettoyer
    kill %1
fi
```

---

**Test 4 : nohup fonctionne**

```bash
# Lancer avec nohup
nohup sleep 10 &
PID=$!

# Vérifier que nohup.out est créé
sleep 2

if [ -f "nohup.out" ]; then
    echo "[OK] Test 4 OK : nohup crée nohup.out"
    
    # Nettoyer
    kill $PID
    rm nohup.out
fi
```

---

**Test 5 : killall fonctionne**

```bash
# Lancer plusieurs sleep
sleep 100 &
sleep 100 &
sleep 100 &

# Tuer tous
killall sleep

# Vérifier
sleep 1
COUNT=$(pgrep sleep | wc -l)

if [ "$COUNT" -eq 0 ]; then
    echo "[OK] Test 5 OK : killall tue tous les processus"
fi
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : kill ne tue pas le processus

**Symptôme :**
```bash
kill 1234
# Le processus continue de tourner
```

**Causes possibles :**

1. **Processus ignore SIGTERM**
```bash
# Solution : Utiliser SIGKILL
kill -9 1234
```

2. **Mauvais PID**
```bash
# Vérifier le PID
ps aux | grep processus
# Utiliser le bon PID
```

3. **Processus en état D (bloqué sur I/O)**
```bash
ps -o pid,stat,cmd | grep 1234
# Si STAT = D -> Attend I/O, ne peut pas être tué
# Attendre ou résoudre le problème I/O
```

---

#### Erreur 2 : "Operation not permitted"

**Symptôme :**
```bash
kill 1234
# bash: kill: (1234) - Operation not permitted
```

**Cause :** Le processus appartient à un autre utilisateur.

**Solution :**
```bash
# Vérifier le propriétaire
ps -o user,pid,cmd -p 1234

# Utiliser sudo si nécessaire
sudo kill 1234
```

---

#### Erreur 3 : killall tue trop de choses

**Symptôme :**
```bash
killall python
# Tue TOUS les scripts Python, y compris ceux que tu voulais garder !
```

**Solution :**
```bash
# 1. Vérifier d'abord ce qui sera tué
pgrep -a python

# 2. Utiliser pkill avec pattern plus précis
pkill -f "mon_script_specifique.py"

# 3. Ou tuer par PID précis
kill $(pgrep -f "mon_script_specifique.py")
```

---

#### Erreur 4 : Job perdu après fermeture du terminal

**Symptôme :**
```bash
./long-process.sh &
# Fermer le terminal
# -> Processus tué
```

**Solution :**
```bash
# Méthode 1 : nohup
nohup ./long-process.sh &

# Méthode 2 : disown
./long-process.sh &
disown

# Méthode 3 : screen ou tmux
screen
./long-process.sh
# Ctrl+A puis D
```

---

#### Erreur 5 : Trop de processus zombies

**Symptôme :**
```bash
ps aux | grep Z
# Beaucoup de zombies
```

**Cause :** Le processus parent ne nettoie pas ses enfants terminés.

**Solution :**
```bash
# 1. Trouver le parent
ps -o pid,ppid,stat,cmd | grep Z

# 2. Redémarrer le parent
kill -HUP <PPID>

# 3. Si ça ne marche pas, kill le parent
kill -TERM <PPID>
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Signaux principaux**
- **SIGTERM (15)** : Arrêt propre (défaut)
- **SIGKILL (9)** : Arrêt forcé (dernier recours)
- **SIGHUP (1)** : Rechargement config
- **SIGINT (2)** : Ctrl+C
- **SIGTSTP (20)** : Ctrl+Z (pause)

**2. Commandes de kill**
- `kill PID` : Envoyer signal (défaut=SIGTERM)
- `killall nom` : Tuer tous par nom
- `pkill pattern` : Tuer par pattern

**3. Jobs**
- `&` : Lancer en background
- `Ctrl+Z` : Suspendre
- `jobs` : Lister jobs
- `fg` : Ramener foreground
- `bg` : Reprendre background

**4. Processus persistants**
- `nohup cmd &` : Avant de lancer
- `disown` : Après avoir lancé

**5. Bonne pratique d'arrêt**
```bash
# 1. Essayer SIGTERM
kill -TERM PID

# 2. Attendre
sleep 5

# 3. Si toujours vivant, SIGKILL
kill -9 PID
```

**6. Raccourcis clavier**
- **Ctrl+C** : Arrêter (SIGINT)
- **Ctrl+Z** : Suspendre (SIGTSTP)
- **Ctrl+\** : Quitter + core dump (SIGQUIT)

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. timeout - Limiter durée d'exécution**

```bash
# Tuer automatiquement après 10 secondes
timeout 10s ./long-process.sh

# Avec signal personnalisé
timeout -s KILL 10s ./long-process.sh

# Avec signal d'abord TERM, puis KILL
timeout --kill-after=5s 10s ./long-process.sh
```

---

**2. systemd-run - Lancer comme service temporaire**

```bash
# Lancer dans un scope systemd
systemd-run --user ./myapp

# Avec limites de ressources
systemd-run --user -p CPUQuota=50% ./myapp
```

---

**3. Signals personnalisés**

```bash
# Script qui gère SIGUSR1 et SIGUSR2
cat > custom-signals.sh << 'EOF'
#!/bin/bash

trap 'echo "SIGUSR1: Activer debug"; DEBUG=1' USR1
trap 'echo "SIGUSR2: Désactiver debug"; DEBUG=0' USR2

DEBUG=0

while true; do
    if [ "$DEBUG" = "1" ]; then
        echo "[DEBUG] Je tourne..."
    else
        echo "Je tourne..."
    fi
    sleep 2
done
EOF

chmod +x custom-signals.sh
./custom-signals.sh &
PID=$!

# Activer debug
kill -USR1 $PID

# Désactiver debug
kill -USR2 $PID
```

---

**4. systemd pour processus robustes**

```bash
# Créer un service systemd
sudo tee /etc/systemd/system/myapp.service << EOF
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=myuser
ExecStart=/usr/local/bin/myapp
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Activer et démarrer
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
```

---

**5. Monitoring avec pidstat**

```bash
# Installer
sudo apt install sysstat

# Surveiller processus spécifique
pidstat -p 1234 1

# Surveiller tous les processus
pidstat 1
```

---

## [COURS] CONCLUSION DE L'EXERCICE 4.2

**[OK] Félicitations ! Tu maîtrises maintenant la gestion des processus !**

**Ce que tu as appris :**
- Signaux Linux (SIGTERM, SIGKILL, SIGHUP, etc.)
- Commandes kill, killall, pkill
- Gestion des jobs (fg, bg, jobs)
- Raccourcis (Ctrl+C, Ctrl+Z)
- Processus persistants (nohup, disown)
- Arrêt propre vs forcé
- Gestion des processus bloqués
- Scripts de gestion avancés

**Compétences acquises :**
- [OK] Contrôle des processus (niveau avancé)
- [OK] Gestion des jobs
- [OK] Arrêt propre de services
- [OK] Diagnostic de problèmes
- [OK] Automatisation

**Temps moyen de réalisation :** 1h30 - 2h

**Prochaine étape :** Exercice 4.3 - Services et Systemd ! [OUTIL]

---

Je continue avec l'exercice 4.3 sur systemd et les services ?

# [ROUGE] EXERCICE 4.3 : SERVICES ET SYSTEMD (AVANCÉ)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Démarrer/arrêter des services (web, base de données, etc.)
- Configurer des applications pour démarrer au boot
- Analyser les logs des services
- Créer des services personnalisés pour tes applications
- Gérer les dépendances entre services
- Planifier des tâches avec des timers
- Comprendre le processus de démarrage (boot)
- Diagnostiquer les problèmes de services

**Problématique :** Les services système (Apache, MySQL, Nginx, etc.) doivent être gérés de manière robuste avec redémarrage automatique, logs centralisés, et démarrage au boot.

### Cahier des charges

- Comprendre systemd et son rôle
- Gérer les services avec `systemctl`
- Créer des fichiers unit personnalisés
- Analyser les logs avec `journalctl`
- Configurer le démarrage automatique
- Gérer les dépendances entre services
- Créer des timers systemd (équivalent cron)
- Comprendre les targets (runlevels)
- Diagnostiquer les problèmes de boot

### Contraintes

- Commandes : `systemctl`, `journalctl`, `systemd-analyze`
- Fichiers unit dans `/etc/systemd/system/`
- Syntaxe des fichiers `.service`
- Nécessite souvent `sudo`
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre l'architecture de systemd
- [OK] Utiliser `systemctl` pour gérer les services
- [OK] Créer des services personnalisés
- [OK] Configurer le redémarrage automatique
- [OK] Gérer les dépendances
- [OK] Analyser les logs avec `journalctl`
- [OK] Créer des timers systemd
- [OK] Comprendre les targets
- [OK] Diagnostiquer les problèmes
- [OK] Appliquer les bonnes pratiques

---

## [DOCS] PRÉREQUIS

- Exercices 4.1 et 4.2 terminés
- Compréhension des processus
- Accès sudo
- Système avec systemd (Ubuntu 16.04+, Debian 8+, CentOS 7+)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre systemd

**Qu'est-ce que systemd ?**

**systemd** est le système d'init et de gestion de services moderne pour Linux. C'est le premier processus lancé au boot (PID 1) qui :

1. **Démarre le système** (initialisation)
2. **Gère les services** (start, stop, restart)
3. **Supervise les processus** (redémarrage automatique)
4. **Centralise les logs** (journald)
5. **Monte les filesystems**
6. **Gère le réseau**
7. **Planifie des tâches** (timers)

---

**Architecture de systemd :**

```
systemd (PID 1)
    │
    ├─ systemd-journald (logs)
    ├─ systemd-udevd (périphériques)
    ├─ systemd-networkd (réseau)
    ├─ systemd-resolved (DNS)
    ├─ systemd-timesyncd (temps)
    │
    └─ Services utilisateur
        ├─ nginx.service
        ├─ mysql.service
        ├─ ssh.service
        └─ ...
```

---

**Concepts clés :**

**1. Units (Unités)**

Une **unit** est une ressource gérée par systemd.

Types d'units :

| Type | Extension | Description | Exemple |
|------|-----------|-------------|---------|
| **Service** | `.service` | Processus/daemon | `nginx.service` |
| **Socket** | `.socket` | Socket d'écoute | `docker.socket` |
| **Target** | `.target` | Groupe d'units | `multi-user.target` |
| **Timer** | `.timer` | Tâche planifiée | `backup.timer` |
| **Mount** | `.mount` | Point de montage | `home.mount` |
| **Device** | `.device` | Périphérique | `dev-sda.device` |
| **Path** | `.path` | Surveillance fichier | `config.path` |

---

**2. États d'un service**

```
loaded -> active (running) -> inactive
  v         v                   v
 État    Sous-état          État final
```

**États principaux :**

| État | Description |
|------|-------------|
| **loaded** | Unit chargée en mémoire |
| **active** | En cours d'exécution |
| **inactive** | Arrêté |
| **failed** | Échec du démarrage |
| **activating** | En cours de démarrage |
| **deactivating** | En cours d'arrêt |

---

**3. Emplacements des fichiers unit**

| Dossier | Description | Priorité |
|---------|-------------|----------|
| `/etc/systemd/system/` | Configuration admin (prioritaire) | 1 (haute) |
| `/run/systemd/system/` | Runtime (temporaire) | 2 |
| `/lib/systemd/system/` | Units système par défaut | 3 (basse) |
| `/usr/lib/systemd/system/` | Units paquets installés | 3 (basse) |

**Ordre de priorité :** systemd charge d'abord `/etc`, puis `/run`, puis `/lib`.

---

### ÉTAPE 2 : Commande `systemctl` - Gestion des services

**Syntaxe de base :**

```
systemctl [COMMANDE] [UNIT]
```

---

**Créer l'environnement de test :**

```bash
# Créer le dossier de travail
mkdir -p ~/systemd-exercise
cd ~/systemd-exercise
```

---

**Test 1 : Lister tous les services**

```bash
# Tous les services chargés
systemctl list-units --type=service

# Tous les services (même inactifs)
systemctl list-units --type=service --all

# Format plus compact
systemctl list-units --type=service --no-pager

# Seulement les services actifs
systemctl list-units --type=service --state=active

# Seulement les services en échec
systemctl list-units --type=service --state=failed
```

---

**Test 2 : Voir l'état d'un service**

```bash
# État détaillé de SSH
systemctl status ssh

# Ou (selon la distribution)
systemctl status sshd
```

**Résultat typique :**

```
[BLACK_CIRCLE] ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-12-30 08:00:00 UTC; 2h 15min ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1234 (sshd)
      Tasks: 1 (limit: 4915)
     Memory: 5.2M
        CPU: 123ms
     CGroup: /system.slice/ssh.service
             └─1234 /usr/sbin/sshd -D

Dec 30 08:00:00 server systemd[1]: Starting OpenBSD Secure Shell server...
Dec 30 08:00:00 server sshd[1234]: Server listening on 0.0.0.0 port 22.
Dec 30 08:00:00 server systemd[1]: Started OpenBSD Secure Shell server.
```

**Lignes importantes :**

```
Loaded: loaded (/lib/.../ssh.service; enabled; ...)
        v                                  v
    Fichier unit                    Démarrage auto au boot
    
Active: active (running) since Mon 2024-12-30 08:00:00 UTC
        v
    État actuel
    
Main PID: 1234 (sshd)
          v
      PID principal
```

---

**Test 3 : Démarrer/Arrêter un service**

```bash
# Arrêter un service
sudo systemctl stop nginx

# Vérifier
systemctl status nginx
# Active: inactive (dead)

# Démarrer
sudo systemctl start nginx

# Vérifier
systemctl status nginx
# Active: active (running)
```

---

**Test 4 : Redémarrer un service**

```bash
# Redémarrage complet (stop puis start)
sudo systemctl restart nginx

# Recharger la config sans redémarrer (si supporté)
sudo systemctl reload nginx

# Recharger si possible, sinon redémarrer
sudo systemctl reload-or-restart nginx
```

---

**Test 5 : Activer/Désactiver au boot**

```bash
# Vérifier si activé au boot
systemctl is-enabled nginx

# Activer au boot
sudo systemctl enable nginx
# Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service -> /lib/systemd/system/nginx.service

# Désactiver
sudo systemctl disable nginx
# Removed /etc/systemd/system/multi-user.target.wants/nginx.service

# Activer ET démarrer immédiatement
sudo systemctl enable --now nginx

# Désactiver ET arrêter immédiatement
sudo systemctl disable --now nginx
```

**enable = Créer un lien symbolique pour démarrage au boot**

---

**Test 6 : Masquer un service**

```bash
# Masquer (empêcher tout démarrage)
sudo systemctl mask nginx
# Created symlink /etc/systemd/system/nginx.service -> /dev/null

# Essayer de démarrer
sudo systemctl start nginx
# Failed to start nginx.service: Unit nginx.service is masked.

# Démasquer
sudo systemctl unmask nginx
```

**`mask` = Lien vers /dev/null, impossible de démarrer**

---

**Test 7 : Vérifier rapidement l'état**

```bash
# Vérifier si actif (code de retour)
systemctl is-active nginx
# active

# Vérifier si activé au boot
systemctl is-enabled nginx
# enabled

# Vérifier si en échec
systemctl is-failed nginx
# active

# Dans un script
if systemctl is-active --quiet nginx; then
    echo "Nginx est actif"
else
    echo "Nginx est inactif"
fi
```

---

**Commandes principales de systemctl :**

| Commande | Description | Exemple |
|----------|-------------|---------|
| `start` | Démarrer | `systemctl start nginx` |
| `stop` | Arrêter | `systemctl stop nginx` |
| `restart` | Redémarrer | `systemctl restart nginx` |
| `reload` | Recharger config | `systemctl reload nginx` |
| `status` | Voir l'état | `systemctl status nginx` |
| `enable` | Activer au boot | `systemctl enable nginx` |
| `disable` | Désactiver au boot | `systemctl disable nginx` |
| `mask` | Masquer (bloquer) | `systemctl mask nginx` |
| `unmask` | Démasquer | `systemctl unmask nginx` |
| `is-active` | Vérifier si actif | `systemctl is-active nginx` |
| `is-enabled` | Vérifier si enabled | `systemctl is-enabled nginx` |
| `list-units` | Lister les units | `systemctl list-units --type=service` |

---

### ÉTAPE 3 : Créer un service personnalisé

**Créer une application simple à gérer :**

```bash
# Script de l'application
cat > ~/systemd-exercise/myapp.sh << 'EOF'
#!/bin/bash

LOG_FILE="/var/log/myapp.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "MyApp démarrée (PID: $$)"

# Gérer les signaux proprement
trap 'log "Signal SIGTERM reçu, arrêt..."; exit 0' TERM
trap 'log "Signal SIGHUP reçu, rechargement config..."; load_config' HUP

load_config() {
    log "Configuration rechargée"
}

# Boucle principale
while true; do
    log "MyApp tourne..."
    sleep 10
done
EOF

chmod +x ~/systemd-exercise/myapp.sh

# Créer le dossier de logs
sudo mkdir -p /var/log
sudo touch /var/log/myapp.log
sudo chmod 666 /var/log/myapp.log
```

---

**Test 1 : Créer un fichier service basique**

```bash
# Créer le fichier unit
sudo tee /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Custom Application
After=network.target

[Service]
Type=simple
ExecStart=/home/YOUR_USERNAME/systemd-exercise/myapp.sh
Restart=on-failure
RestartSec=10
User=YOUR_USERNAME

[Install]
WantedBy=multi-user.target
EOF

# Remplacer YOUR_USERNAME
sudo sed -i "s/YOUR_USERNAME/$(whoami)/g" /etc/systemd/system/myapp.service

# Recharger systemd (nécessaire après modification de fichiers unit)
sudo systemctl daemon-reload

# Vérifier que le service est reconnu
systemctl status myapp
# [WHITE_CIRCLE] myapp.service - My Custom Application
#      Loaded: loaded (/etc/systemd/system/myapp.service; disabled; ...)
#      Active: inactive (dead)
```

---

**Anatomie d'un fichier .service :**

```ini
[Unit]
Description=My Custom Application
Documentation=https://example.com/docs
After=network.target
Requires=mysql.service
Wants=redis.service

[Service]
Type=simple
User=myuser
Group=mygroup
WorkingDirectory=/opt/myapp
ExecStartPre=/usr/bin/check-config.sh
ExecStart=/usr/bin/myapp
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/usr/bin/myapp --shutdown
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
```

**Section `[Unit]` - Métadonnées et dépendances**

| Directive | Description | Exemple |
|-----------|-------------|---------|
| `Description` | Description du service | `My Application` |
| `Documentation` | URL de doc | `man:nginx(8)` |
| `After` | Démarrer après ces units | `network.target` |
| `Before` | Démarrer avant ces units | `httpd.service` |
| `Requires` | Dépendances strictes | `mysql.service` |
| `Wants` | Dépendances souples | `redis.service` |
| `Conflicts` | Incompatible avec | `apache2.service` |

---

**Section `[Service]` - Configuration du service**

| Directive | Description | Valeurs possibles |
|-----------|-------------|-------------------|
| `Type` | Type de service | `simple`, `forking`, `oneshot`, `notify` |
| `ExecStart` | Commande de démarrage | `/usr/bin/myapp` |
| `ExecStartPre` | Commande avant démarrage | `/usr/bin/check.sh` |
| `ExecStartPost` | Commande après démarrage | `/usr/bin/notify.sh` |
| `ExecReload` | Commande de rechargement | `/bin/kill -HUP $MAINPID` |
| `ExecStop` | Commande d'arrêt | `/usr/bin/myapp --stop` |
| `ExecStopPost` | Commande après arrêt | `/usr/bin/cleanup.sh` |
| `Restart` | Politique de redémarrage | `no`, `on-failure`, `always`, `on-abnormal` |
| `RestartSec` | Délai avant redémarrage | `10` (secondes) |
| `User` | Utilisateur | `www-data` |
| `Group` | Groupe | `www-data` |
| `WorkingDirectory` | Dossier de travail | `/opt/myapp` |
| `Environment` | Variables d'environnement | `VAR=value` |
| `EnvironmentFile` | Fichier de variables | `/etc/myapp/env` |

---

**Types de services :**

| Type | Description | Usage |
|------|-------------|-------|
| **simple** | Le processus spécifié est le processus principal | Applications simples |
| **forking** | Le processus fork et le parent se termine | Daemons traditionnels |
| **oneshot** | Processus court qui se termine | Scripts d'init |
| **notify** | Le processus signale qu'il est prêt | Apps systemd-aware |
| **dbus** | Acquiert un nom sur D-Bus | Services D-Bus |
| **idle** | Démarre quand tous les autres sont terminés | Tâches de fond |

---

**Section `[Install]` - Installation**

| Directive | Description | Exemple |
|-----------|-------------|---------|
| `WantedBy` | Activé pour ce target | `multi-user.target` |
| `RequiredBy` | Requis pour ce target | `graphical.target` |
| `Alias` | Alias du service | `myapp.service` |

---

**Test 2 : Démarrer et tester le service**

```bash
# Démarrer
sudo systemctl start myapp

# Vérifier l'état
systemctl status myapp
# [BLACK_CIRCLE] myapp.service - My Custom Application
#      Loaded: loaded (/etc/systemd/system/myapp.service; disabled; ...)
#      Active: active (running) since ...
#    Main PID: 12345 (myapp.sh)

# Voir les logs
sudo tail -f /var/log/myapp.log
# [2024-12-30 10:00:00] MyApp démarrée (PID: 12345)
# [2024-12-30 10:00:00] MyApp tourne...
# [2024-12-30 10:00:10] MyApp tourne...

# Arrêter
sudo systemctl stop myapp

# Voir les logs
sudo tail /var/log/myapp.log
# [2024-12-30 10:00:20] Signal SIGTERM reçu, arrêt...
```

---

**Test 3 : Service avec redémarrage automatique**

```bash
# Modifier le service pour tester le redémarrage
sudo tee /etc/systemd/system/myapp.service << EOF
[Unit]
Description=My Custom Application
After=network.target

[Service]
Type=simple
ExecStart=$(pwd)/myapp.sh
Restart=always
RestartSec=5
User=$(whoami)

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl start myapp

# Obtenir le PID
PID=$(systemctl show -p MainPID --value myapp)
echo "PID: $PID"

# Tuer le processus
sudo kill -9 $PID

# Attendre 5 secondes et vérifier
sleep 6
systemctl status myapp
# Active: active (running) since ... (nouveau PID !)

# Le service a redémarré automatiquement !
```

**Politiques de redémarrage :**

| Valeur | Description | Quand redémarrer |
|--------|-------------|------------------|
| `no` | Jamais | Jamais |
| `on-failure` | En cas d'échec | Code sortie != 0, signal, timeout |
| `on-abnormal` | Sortie anormale | Signal, timeout (pas code sortie) |
| `on-abort` | Sur signal | Signal seulement |
| `on-watchdog` | Timeout watchdog | Watchdog expire |
| `always` | Toujours | Toujours (même arrêt normal) |

---

**Test 4 : Service avec fichier de configuration**

```bash
# Créer un fichier d'environnement
sudo tee /etc/myapp.env << 'EOF'
LOG_LEVEL=debug
MAX_CONNECTIONS=100
DATABASE_URL=postgresql://localhost/mydb
EOF

# Modifier le service
sudo tee /etc/systemd/system/myapp-env.service << EOF
[Unit]
Description=My App with Environment
After=network.target

[Service]
Type=simple
EnvironmentFile=/etc/myapp.env
ExecStart=$(pwd)/myapp.sh
User=$(whoami)

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl start myapp-env

# Vérifier les variables d'environnement
sudo systemctl show myapp-env --property=Environment
```

---

**Test 5 : Service avec pré/post commandes**

```bash
# Créer des scripts de check et cleanup
cat > ~/systemd-exercise/check-before.sh << 'EOF'
#!/bin/bash
echo "Vérification avant démarrage..."
# Vérifier que le port 8080 est libre
if netstat -tuln | grep -q ":8080 "; then
    echo "Erreur : Port 8080 déjà utilisé"
    exit 1
fi
echo "OK, port disponible"
exit 0
EOF

cat > ~/systemd-exercise/cleanup-after.sh << 'EOF'
#!/bin/bash
echo "Nettoyage après arrêt..."
rm -f /tmp/myapp.pid
echo "Nettoyage terminé"
EOF

chmod +x ~/systemd-exercise/check-before.sh
chmod +x ~/systemd-exercise/cleanup-after.sh

# Service avec pré/post
sudo tee /etc/systemd/system/myapp-hooks.service << EOF
[Unit]
Description=My App with Hooks
After=network.target

[Service]
Type=simple
ExecStartPre=$(pwd)/check-before.sh
ExecStart=$(pwd)/myapp.sh
ExecStopPost=$(pwd)/cleanup-after.sh
User=$(whoami)

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl start myapp-hooks

# Voir les logs
journalctl -u myapp-hooks -n 20
```

---

### ÉTAPE 4 : Journalctl - Logs systemd

**`journalctl` = Outil pour consulter les logs de systemd**

Tous les logs sont centralisés dans le **journal systemd**.

---

**Test 1 : Voir les logs d'un service**

```bash
# Logs du service myapp
journalctl -u myapp

# Dernières 50 lignes
journalctl -u myapp -n 50

# Suivre en temps réel
journalctl -u myapp -f

# Depuis le dernier boot
journalctl -u myapp -b
```

---

**Test 2 : Filtrer par date/heure**

```bash
# Logs d'aujourd'hui
journalctl -u myapp --since today

# Logs depuis hier
journalctl -u myapp --since yesterday

# Logs des 2 dernières heures
journalctl -u myapp --since "2 hours ago"

# Logs entre deux dates
journalctl -u myapp --since "2024-12-30 08:00:00" --until "2024-12-30 12:00:00"

# Logs du dernier boot
journalctl -u myapp -b -1
```

---

**Test 3 : Filtrer par priorité**

```bash
# Seulement les erreurs
journalctl -u myapp -p err

# Erreurs et warnings
journalctl -u myapp -p warning

# Debug et plus
journalctl -u myapp -p debug
```

**Niveaux de priorité :**

| Code | Niveau | Description |
|------|--------|-------------|
| 0 | emerg | Urgence (système inutilisable) |
| 1 | alert | Alerte (action immédiate) |
| 2 | crit | Critique |
| 3 | err | Erreur |
| 4 | warning | Avertissement |
| 5 | notice | Notice |
| 6 | info | Information |
| 7 | debug | Debug |

---

**Test 4 : Format de sortie**

```bash
# Format JSON
journalctl -u myapp -o json-pretty -n 5

# Format court
journalctl -u myapp -o short

# Format verbeux
journalctl -u myapp -o verbose -n 1

# Seulement les messages (pas les métadonnées)
journalctl -u myapp -o cat
```

---

**Test 5 : Logs système globaux**

```bash
# Tous les logs système
journalctl

# Logs du kernel
journalctl -k

# Logs depuis le boot actuel
journalctl -b

# Lister les boots
journalctl --list-boots

# Logs d'un boot spécifique
journalctl -b -1  # Avant-dernier boot
```

---

**Test 6 : Gestion de l'espace disque**

```bash
# Voir l'utilisation du journal
journalctl --disk-usage

# Nettoyer les logs de plus de 7 jours
sudo journalctl --vacuum-time=7d

# Garder seulement 500 MB
sudo journalctl --vacuum-size=500M

# Vérifier l'intégrité
sudo journalctl --verify
```

---

**Options principales de journalctl :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-u` | Service spécifique | `journalctl -u nginx` |
| `-f` | Suivre (follow) | `journalctl -f` |
| `-n` | Nombre de lignes | `journalctl -n 50` |
| `-b` | Boot | `journalctl -b` |
| `-k` | Kernel | `journalctl -k` |
| `-p` | Priorité | `journalctl -p err` |
| `--since` | Depuis | `journalctl --since today` |
| `--until` | Jusqu'à | `journalctl --until "1 hour ago"` |
| `-o` | Format | `journalctl -o json` |
| `--disk-usage` | Espace utilisé | `journalctl --disk-usage` |

---

### ÉTAPE 5 : Dépendances entre services

**Gérer l'ordre de démarrage et les dépendances.**

---

**Test 1 : Dépendance stricte avec `Requires`**

```bash
# Service "database"
sudo tee /etc/systemd/system/mydatabase.service << EOF
[Unit]
Description=My Database Service

[Service]
Type=oneshot
ExecStart=/bin/echo "Database started"
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

# Service "webapp" qui NÉCESSITE database
sudo tee /etc/systemd/system/mywebapp.service << EOF
[Unit]
Description=My Web Application
Requires=mydatabase.service
After=mydatabase.service

[Service]
Type=oneshot
ExecStart=/bin/echo "WebApp started"

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload

# Démarrer webapp
sudo systemctl start mywebapp

# Les deux services démarrent !
systemctl status mydatabase
systemctl status mywebapp

# Arrêter database
sudo systemctl stop mydatabase

# webapp s'arrête aussi !
systemctl status mywebapp
# inactive
```

**`Requires` = Dépendance stricte, si l'un s'arrête, l'autre aussi**

---

**Test 2 : Dépendance souple avec `Wants`**

```bash
# Modifier webapp pour utiliser Wants
sudo tee /etc/systemd/system/mywebapp-wants.service << EOF
[Unit]
Description=My Web Application (wants)
Wants=mydatabase.service
After=mydatabase.service

[Service]
Type=oneshot
ExecStart=/bin/echo "WebApp started"
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl start mywebapp-wants

# Arrêter database
sudo systemctl stop mydatabase

# webapp continue de tourner !
systemctl status mywebapp-wants
# active
```

**`Wants` = Dépendance souple, indépendants après démarrage**

---

**Directives de dépendances :**

| Directive | Type | Comportement |
|-----------|------|--------------|
| `Requires` | Stricte | Si dépendance échoue/stop -> ce service stop aussi |
| `Wants` | Souple | Démarrage préféré mais pas obligatoire |
| `Requisite` | Stricte | Dépendance doit DÉJÀ tourner |
| `BindsTo` | Très stricte | Comme Requires mais plus fort |
| `PartOf` | Hiérarchique | Arrêt/redémarrage propagé |
| `Conflicts` | Incompatible | Ne peut pas tourner en même temps |

---

**Directives d'ordre :**

| Directive | Description |
|-----------|-------------|
| `After` | Démarrer après ces units |
| `Before` | Démarrer avant ces units |

**[ATTENTION] Important :** `After` ne crée PAS de dépendance, juste l'ordre !

```bash
# Exemple : Webapp démarre après database (ordre)
# Mais peut démarrer même si database échoue (pas de dépendance)
After=mydatabase.service
```

**Pour avoir dépendance + ordre :**

```bash
Requires=mydatabase.service
After=mydatabase.service
```

---

### ÉTAPE 6 : Timers systemd - Tâches planifiées

**Timers systemd = Alternative moderne à cron**

**Avantages sur cron :**
- [OK] Intégré avec systemd
- [OK] Logs centralisés (journalctl)
- [OK] Dépendances
- [OK] Gestion des ressources
- [OK] Retry automatique

---

**Test 1 : Créer un timer simple**

```bash
# Script de backup
cat > ~/systemd-exercise/backup.sh << 'EOF'
#!/bin/bash
echo "[$(date)] Backup démarré..."
tar -czf /tmp/backup-$(date +%Y%m%d-%H%M%S).tar.gz ~/systemd-exercise/ 2>/dev/null
echo "[$(date)] Backup terminé"
EOF

chmod +x ~/systemd-exercise/backup.sh

# Service de backup
sudo tee /etc/systemd/system/backup.service << EOF
[Unit]
Description=Backup Task

[Service]
Type=oneshot
ExecStart=$(pwd)/backup.sh
User=$(whoami)
EOF

# Timer pour exécuter toutes les 5 minutes
sudo tee /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Backup Timer
Requires=backup.service

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Unit=backup.service

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload

# Activer et démarrer le timer
sudo systemctl enable backup.timer
sudo systemctl start backup.timer

# Vérifier
systemctl status backup.timer
systemctl list-timers backup.timer

# Voir les exécutions dans les logs
journalctl -u backup.service -f
```

---

**Anatomie d'un fichier .timer :**

```ini
[Timer]
OnBootSec=15min
OnUnitActiveSec=1h
OnCalendar=daily
AccuracySec=1min
RandomizedDelaySec=5min
Persistent=true

[Install]
WantedBy=timers.target
```

**Directives Timer :**

| Directive | Description | Exemple |
|-----------|-------------|---------|
| `OnBootSec` | Délai après boot | `15min` |
| `OnStartupSec` | Délai après démarrage systemd | `10min` |
| `OnActiveSec` | Après activation du timer | `1h` |
| `OnUnitActiveSec` | Après dernière exécution du service | `1h` |
| `OnUnitInactiveSec` | Après dernier arrêt du service | `30min` |
| `OnCalendar` | Calendrier (cron-like) | `daily`, `Mon *-*-* 00:00:00` |
| `AccuracySec` | Précision | `1min` |
| `RandomizedDelaySec` | Délai aléatoire | `5min` |
| `Persistent` | Rattraper exécutions manquées | `true` |

---

**Test 2 : Timer avec calendrier**

```bash
# Timer quotidien à 3h du matin
sudo tee /etc/systemd/system/daily-backup.timer << 'EOF'
[Unit]
Description=Daily Backup Timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target
EOF

# Timer chaque lundi à 10h
sudo tee /etc/systemd/system/weekly-task.timer << 'EOF'
[Unit]
Description=Weekly Task Timer

[Timer]
OnCalendar=Mon *-*-* 10:00:00

[Install]
WantedBy=timers.target
EOF

# Timer toutes les heures
sudo tee /etc/systemd/system/hourly-task.timer << 'EOF'
[Unit]
Description=Hourly Task Timer

[Timer]
OnCalendar=hourly

[Install]
WantedBy=timers.target
EOF
```

**Expressions OnCalendar :**

| Expression | Description | Équivalent |
|------------|-------------|------------|
| `hourly` | Chaque heure | `*-*-* *:00:00` |
| `daily` | Chaque jour 00:00 | `*-*-* 00:00:00` |
| `weekly` | Chaque lundi 00:00 | `Mon *-*-* 00:00:00` |
| `monthly` | 1er du mois 00:00 | `*-*-01 00:00:00` |
| `yearly` | 1er janvier 00:00 | `*-01-01 00:00:00` |

**Format complet :**

```
Jour_Semaine Année-Mois-Jour Heure:Minute:Seconde

Mon,Fri *-*-* 10:00:00    # Lundi et vendredi à 10h
*-*-* 00:00:00            # Tous les jours à minuit
*-*-* 00/2:00:00          # Toutes les 2 heures
```

---

**Test 3 : Lister et gérer les timers**

```bash
# Lister tous les timers
systemctl list-timers

# Lister tous (même inactifs)
systemctl list-timers --all

# Informations détaillées
systemctl status backup.timer

# Voir la prochaine exécution
systemctl list-timers backup.timer

# Déclencher manuellement
sudo systemctl start backup.service

# Désactiver le timer
sudo systemctl stop backup.timer
sudo systemctl disable backup.timer
```

---

### ÉTAPE 7 : Targets - Équivalent des runlevels

**Target = Groupe d'units (point de synchronisation)**

**Targets principaux :**

| Target | Description | Équivalent runlevel |
|--------|-------------|---------------------|
| `poweroff.target` | Arrêt système | 0 |
| `rescue.target` | Mode rescue (single-user) | 1 |
| `multi-user.target` | Multi-utilisateur (CLI) | 3 |
| `graphical.target` | Mode graphique | 5 |
| `reboot.target` | Redémarrage | 6 |

---

**Test 1 : Voir le target actuel**

```bash
# Target actif
systemctl get-default
# graphical.target (si interface graphique)
# ou multi-user.target (si serveur)

# Tous les targets chargés
systemctl list-units --type=target
```

---

**Test 2 : Changer de target**

```bash
# Passer en mode multi-user (sans graphique)
sudo systemctl isolate multi-user.target

# Passer en mode graphique
sudo systemctl isolate graphical.target

# Mode rescue
sudo systemctl isolate rescue.target
```

---

**Test 3 : Changer le target par défaut**

```bash
# Définir multi-user par défaut
sudo systemctl set-default multi-user.target
# Created symlink /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target

# Définir graphical par défaut
sudo systemctl set-default graphical.target
```

---

**Test 4 : Arrêt/Redémarrage système**

```bash
# Redémarrer
sudo systemctl reboot

# Arrêter
sudo systemctl poweroff

# Suspendre
sudo systemctl suspend

# Hibernation
sudo systemctl hibernate
```

---

### ÉTAPE 8 : Analyse et diagnostic

**`systemd-analyze` = Outil d'analyse systemd**

---

**Test 1 : Temps de boot**

```bash
# Temps total de boot
systemd-analyze

# Résultat :
# Startup finished in 2.5s (kernel) + 5.3s (userspace) = 7.8s
```

---

**Test 2 : Services les plus lents au boot**

```bash
# Top 10 des services les plus lents
systemd-analyze blame | head -10

# Résultat :
#  3.456s nginx.service
#  2.123s mysql.service
#  1.789s networking.service
#  ...
```

---

**Test 3 : Graphe de dépendances**

```bash
# Générer un SVG du graphe
systemd-analyze plot > boot-graph.svg

# Ouvrir dans un navigateur
firefox boot-graph.svg
```

---

**Test 4 : Chaîne critique (critical chain)**

```bash
# Voir le chemin critique du boot
systemd-analyze critical-chain

# Pour un service spécifique
systemd-analyze critical-chain nginx.service
```

---

**Test 5 : Vérifier la configuration d'un service**

```bash
# Vérifier la syntaxe
systemd-analyze verify /etc/systemd/system/myapp.service

# Afficher la configuration effective
systemctl cat myapp

# Afficher toutes les propriétés
systemctl show myapp
```

---

### ÉTAPE 9 : Cas d'usage professionnels complets

**Cas 1 : Service web Python (Flask/Django) avec Gunicorn**

```bash
# Application Flask simple
cat > ~/systemd-exercise/flask-app.py << 'EOF'
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello from systemd!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
EOF

# Installer Flask
pip3 install flask gunicorn

# Service systemd
sudo tee /etc/systemd/system/flask-app.service << EOF
[Unit]
Description=Flask Application
After=network.target

[Service]
Type=notify
User=$(whoami)
WorkingDirectory=$(pwd)
Environment="PATH=$(pwd)/.venv/bin:/usr/local/bin:/usr/bin"
ExecStart=/usr/local/bin/gunicorn --bind 0.0.0.0:5000 --workers 3 flask-app:app
ExecReload=/bin/kill -s HUP \$MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable flask-app
sudo systemctl start flask-app

# Tester
curl http://localhost:5000
# Hello from systemd!
```

---

**Cas 2 : Service Node.js avec gestion des logs**

```bash
# App Node.js simple
cat > ~/systemd-exercise/node-app.js << 'EOF'
const http = require('http');

const server = http.createServer((req, res) => {
    console.log(`${new Date().toISOString()} - Request received`);
    res.writeHead(200);
    res.end('Hello from Node.js!');
});

server.listen(3000, () => {
    console.log('Server started on port 3000');
});
EOF

# Service systemd
sudo tee /etc/systemd/system/node-app.service << EOF
[Unit]
Description=Node.js Application
After=network.target

[Service]
Type=simple
User=$(whoami)
WorkingDirectory=$(pwd)
ExecStart=/usr/bin/node node-app.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=node-app

# Sécurité
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/tmp

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl start node-app

# Voir les logs
journalctl -u node-app -f
```

---

**Cas 3 : Service avec plusieurs workers (Redis-like)**

```bash
# Service principal
sudo tee /etc/systemd/system/myapp-master.service << 'EOF'
[Unit]
Description=MyApp Master Service
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/myapp-master
PIDFile=/var/run/myapp/master.pid
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# Service worker (template)
sudo tee /etc/systemd/system/myapp-worker@.service << 'EOF'
[Unit]
Description=MyApp Worker %i
After=myapp-master.service
Requires=myapp-master.service

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp-worker --id=%i
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

# Démarrer master + 3 workers
sudo systemctl start myapp-master
sudo systemctl start myapp-worker@1
sudo systemctl start myapp-worker@2
sudo systemctl start myapp-worker@3
```

---

**Cas 4 : Service avec healthcheck**

```bash
# Script healthcheck
cat > ~/systemd-exercise/healthcheck.sh << 'EOF'
#!/bin/bash
# Vérifier que l'app répond
curl -f http://localhost:5000/health > /dev/null 2>&1
exit $?
EOF

chmod +x ~/systemd-exercise/healthcheck.sh

# Service avec healthcheck
sudo tee /etc/systemd/system/webapp-monitored.service << EOF
[Unit]
Description=Web App with Healthcheck
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/webapp
ExecReload=/bin/kill -HUP \$MAINPID
Restart=on-failure
RestartSec=10

# Watchdog : si pas de réponse en 30s, redémarrer
WatchdogSec=30
ExecStartPost=/bin/sh -c 'while true; do $(pwd)/healthcheck.sh || systemctl restart webapp-monitored; sleep 10; done' &

[Install]
WantedBy=multi-user.target
EOF
```

---

**Cas 5 : Backup automatique avec timer et rotation**

```bash
# Script de backup avec rotation
cat > ~/systemd-exercise/auto-backup.sh << 'EOF'
#!/bin/bash
BACKUP_DIR="/backups"
RETENTION_DAYS=7

# Créer le backup
DATE=$(date +%Y%m%d-%H%M%S)
tar -czf "$BACKUP_DIR/backup-$DATE.tar.gz" /var/www/ 2>/dev/null

# Rotation : supprimer les backups de plus de 7 jours
find "$BACKUP_DIR" -name "backup-*.tar.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup créé : backup-$DATE.tar.gz"
EOF

chmod +x ~/systemd-exercise/auto-backup.sh

# Service
sudo tee /etc/systemd/system/auto-backup.service << EOF
[Unit]
Description=Automatic Backup

[Service]
Type=oneshot
ExecStart=$(pwd)/auto-backup.sh
User=root
EOF

# Timer : tous les jours à 2h du matin
sudo tee /etc/systemd/system/auto-backup.timer << 'EOF'
[Unit]
Description=Daily Backup Timer

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=15min

[Install]
WantedBy=timers.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable auto-backup.timer
sudo systemctl start auto-backup.timer
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : systemd est actif**

```bash
# Vérifier que systemd est le système d'init
if [ "$(ps -p 1 -o comm=)" = "systemd" ]; then
    echo "[OK] Test 1 OK : systemd actif"
else
    echo "[X] Test 1 FAIL : systemd non actif"
fi
```

---

**Test 2 : systemctl fonctionne**

```bash
# Lister les services
if systemctl list-units --type=service > /dev/null 2>&1; then
    echo "[OK] Test 2 OK : systemctl fonctionne"
else
    echo "[X] Test 2 FAIL"
fi
```

---

**Test 3 : Service personnalisé fonctionne**

```bash
# Créer un service de test
sudo tee /etc/systemd/system/test-service.service << 'EOF'
[Unit]
Description=Test Service

[Service]
Type=oneshot
ExecStart=/bin/echo "Test OK"
EOF

sudo systemctl daemon-reload
sudo systemctl start test-service

if systemctl is-active --quiet test-service.service 2>/dev/null || \
   systemctl show test-service.service -p SubState | grep -q "exited"; then
    echo "[OK] Test 3 OK : Service personnalisé fonctionne"
    sudo systemctl stop test-service 2>/dev/null
    sudo rm /etc/systemd/system/test-service.service
    sudo systemctl daemon-reload
else
    echo "[X] Test 3 FAIL"
fi
```

---

**Test 4 : journalctl fonctionne**

```bash
if journalctl -n 1 > /dev/null 2>&1; then
    echo "[OK] Test 4 OK : journalctl fonctionne"
else
    echo "[X] Test 4 FAIL"
fi
```

---

**Test 5 : Timer fonctionne**

```bash
# Vérifier qu'il y a des timers
COUNT=$(systemctl list-timers --all | wc -l)

if [ "$COUNT" -gt 5 ]; then
    echo "[OK] Test 5 OK : Timers présents"
else
    echo "[ATTENTION]  Test 5 : Peu de timers ($COUNT)"
fi
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Failed to start service"

**Symptôme :**
```bash
sudo systemctl start myapp
# Job for myapp.service failed...
```

**Diagnostic :**
```bash
# 1. Voir les logs détaillés
journalctl -u myapp -n 50

# 2. Vérifier le statut
systemctl status myapp

# 3. Vérifier la configuration
systemd-analyze verify /etc/systemd/system/myapp.service
```

**Causes fréquentes :**
- Chemin ExecStart incorrect
- Permission denied
- Port déjà utilisé
- Dépendance non satisfaite

---

#### Erreur 2 : Service ne démarre pas au boot

**Symptôme :**
```bash
# Après reboot, service inactif
systemctl status myapp
# inactive (dead)
```

**Solution :**
```bash
# Vérifier si enabled
systemctl is-enabled myapp
# disabled

# Activer au boot
sudo systemctl enable myapp

# Vérifier le lien symbolique
ls -l /etc/systemd/system/multi-user.target.wants/myapp.service
```

---

#### Erreur 3 : "Unit not found"

**Symptôme :**
```bash
systemctl status myapp
# Unit myapp.service could not be found
```

**Causes :**
1. Fichier unit pas dans le bon dossier
2. Nom incorrect
3. daemon-reload pas fait

**Solution :**
```bash
# Vérifier l'emplacement
ls -l /etc/systemd/system/myapp.service

# Recharger systemd
sudo systemctl daemon-reload

# Vérifier qu'il est reconnu
systemctl list-unit-files | grep myapp
```

---

#### Erreur 4 : Service redémarre en boucle

**Symptôme :**
```bash
systemctl status myapp
# Active: activating (auto-restart)
```

**Diagnostic :**
```bash
# Voir les logs récents
journalctl -u myapp -n 100

# Désactiver temporairement restart
sudo systemctl edit myapp
# Ajouter :
# [Service]
# Restart=no
```

**Cause :** Le service crash immédiatement -> boucle infinie

---

#### Erreur 5 : Modifications ignorées

**Symptôme :**
```bash
# Modifié le fichier .service
# Mais les changements ne sont pas pris en compte
```

**Solution :**
```bash
# TOUJOURS recharger après modification !
sudo systemctl daemon-reload

# Puis redémarrer le service
sudo systemctl restart myapp
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. systemd = Système d'init moderne**
- PID 1, gère tout le système
- Units : service, timer, target, etc.
- Logs centralisés avec journald

**2. systemctl - Commandes essentielles**
- `start/stop/restart` : Contrôle
- `enable/disable` : Boot automatique
- `status` : État détaillé
- `is-active/is-enabled` : Vérifications

**3. Fichier .service - Structure**
```ini
[Unit]
Description=...
After=network.target

[Service]
Type=simple
ExecStart=...
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

**4. journalctl - Logs**
- `-u service` : Service spécifique
- `-f` : Follow (temps réel)
- `--since today` : Filtre date
- `-p err` : Filtre priorité

**5. Timers - Alternative à cron**
- Fichiers .timer + .service
- `OnCalendar` pour calendrier
- `OnUnitActiveSec` pour intervalle

**6. Bonnes pratiques**
- Toujours `daemon-reload` après modification
- `Restart=on-failure` pour robustesse
- Utiliser `After=network.target`
- Logs vers journald
- Sécurité : User non-root, PrivateTmp

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Socket activation**

```ini
# myapp.socket
[Socket]
ListenStream=8080

[Install]
WantedBy=sockets.target

# myapp.service démarre quand connexion arrive
```

---

**2. Sécurité avancée (sandboxing)**

```ini
[Service]
# Isolation filesystem
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadOnlyPaths=/
ReadWritePaths=/var/myapp

# Isolation réseau
PrivateNetwork=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

# Capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Syscalls
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
```

---

**3. Watchdog**

```ini
[Service]
WatchdogSec=30
# App doit envoyer sd_notify(0, "WATCHDOG=1") toutes les 30s
```

---

**4. Resource limits**

```ini
[Service]
# Mémoire
MemoryMax=1G
MemorySwapMax=0

# CPU
CPUQuota=50%

# Tasks
TasksMax=100

# Files
LimitNOFILE=4096
```

---

**5. systemctl advanced**

```bash
# Éditer override
sudo systemctl edit myapp
# Crée /etc/systemd/system/myapp.service.d/override.conf

# Reset failed units
sudo systemctl reset-failed

# Freeze/unfreeze (pause)
sudo systemctl freeze myapp
sudo systemctl thaw myapp

# Masquer temporairement
sudo systemctl mask --runtime myapp
```

---

## [COURS] CONCLUSION DE L'EXERCICE 4.3

**[OK] FÉLICITATIONS ! Tu maîtrises maintenant systemd !**

**Ce que tu as appris :**
- Architecture et concepts de systemd
- Gestion des services avec systemctl
- Création de services personnalisés
- Structure des fichiers .service
- Logs avec journalctl
- Timers systemd (alternative cron)
- Dépendances entre services
- Targets et boot process
- Diagnostic et résolution de problèmes

**Compétences acquises :**
- [OK] Administration systemd (niveau expert)
- [OK] Création de services robustes
- [OK] Automatisation avec timers
- [OK] Diagnostic avancé
- [OK] Bonnes pratiques de production

**Temps moyen de réalisation :** 2-3 heures

---

# [BRAVO] CONCLUSION SECTION 4 : PROCESSUS & SERVICES COMPLÈTE !

**[TROPHEE] FÉLICITATIONS ! Section 4 100% terminée !**

**Récapitulatif des 3 exercices :**

| Exercice | Niveau | Compétences |
|----------|--------|-------------|
| 4.1 | [VERT] Débutant | Comprendre les processus (ps, top, /proc) |
| 4.2 | [JAUNE] Intermédiaire | Gérer les processus (kill, signals, jobs) |
| 4.3 | [ROUGE] Avancé | Services et systemd |

**Niveau atteint : EXPERT en Gestion de Processus et Services ! [RAPIDE]**

---

Tu veux continuer avec la Section 5 (Réseau & Connectivité) ou une autre section ?

# [DOCS] SECTION 5 : RÉSEAU & CONNECTIVITÉ

# [VERT] EXERCICE 5.1 : CONCEPTS RÉSEAU ET DIAGNOSTIC (DÉBUTANT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Vérifier la connectivité réseau des serveurs
- Diagnostiquer les problèmes de connexion
- Identifier les adresses IP et interfaces réseau
- Tester la résolution DNS
- Vérifier les routes réseau
- Analyser la latence et la perte de paquets
- Identifier les services qui écoutent sur les ports
- Comprendre la configuration réseau du système

**Problématique :** Un serveur qui ne répond pas peut avoir un problème réseau (pas de connexion, DNS cassé, firewall bloquant, etc.). Il faut savoir diagnostiquer rapidement.

### Cahier des charges

- Comprendre les concepts réseau de base (IP, masque, gateway, DNS)
- Identifier les interfaces réseau du système
- Vérifier la connectivité avec `ping`
- Tracer les routes avec `traceroute`
- Tester la résolution DNS avec `nslookup` et `dig`
- Voir les connexions actives avec `netstat` et `ss`
- Analyser le trafic réseau
- Diagnostiquer les problèmes courants

### Contraintes

- Commandes : `ip`, `ifconfig`, `ping`, `traceroute`, `nslookup`, `dig`, `netstat`, `ss`, `hostname`
- Concepts : TCP/IP, DNS, routing
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Comprendre les concepts réseau de base
- [OK] Identifier les interfaces réseau
- [OK] Voir la configuration IP
- [OK] Tester la connectivité (ping)
- [OK] Tracer les routes (traceroute)
- [OK] Résoudre les noms DNS
- [OK] Voir les connexions actives
- [OK] Identifier les ports ouverts
- [OK] Diagnostiquer les problèmes réseau
- [OK] Utiliser les outils modernes (ip, ss)

---

## [DOCS] PRÉREQUIS

- Sections 1-4 complètes
- Accès à Internet (pour les tests)
- Compréhension basique du réseau (optionnel)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Concepts réseau de base

**Modèle OSI simplifié (pour Linux) :**

```
7. Application    -> HTTP, SSH, DNS
6. Présentation   v
5. Session        v
4. Transport      -> TCP, UDP
3. Réseau         -> IP, ICMP
2. Liaison        -> Ethernet, WiFi
1. Physique       -> Câbles, signaux
```

---

**Concepts clés :**

**1. Adresse IP (Internet Protocol)**

```
192.168.1.100
└─┬──┘ │  │ │
  │    │  │ └─ Hôte (100)
  │    │  └─── Sous-réseau
  │    └────── Réseau
  └─────────── Classe privée
```

**Types d'adresses :**

| Plage | Type | Usage |
|-------|------|-------|
| `10.0.0.0/8` | Privée | Réseaux internes |
| `172.16.0.0/12` | Privée | Réseaux internes |
| `192.168.0.0/16` | Privée | Réseaux domestiques |
| `127.0.0.1` | Loopback | Localhost (machine locale) |
| `0.0.0.0` | Toutes | Écoute sur toutes les interfaces |

---

**2. Masque de sous-réseau (Subnet Mask)**

Sépare partie réseau et partie hôte.

```
IP:     192.168.1.100
Masque: 255.255.255.0  (/24 en notation CIDR)
        └─────────┬─────────┘
            Réseau (24 bits)
            
Réseau:  192.168.1.0
Hôtes:   192.168.1.1 - 192.168.1.254
Broadcast: 192.168.1.255
```

**Notation CIDR :**

| Notation | Masque | Nombre d'hôtes |
|----------|--------|----------------|
| /24 | 255.255.255.0 | 254 |
| /16 | 255.255.0.0 | 65 534 |
| /8 | 255.0.0.0 | 16 777 214 |

---

**3. Passerelle (Gateway)**

Le routeur qui connecte au réseau externe.

```
[PC 192.168.1.100] -> [Gateway 192.168.1.1] -> [Internet]
```

---

**4. DNS (Domain Name System)**

Traduit noms -> adresses IP.

```
google.com -> 142.250.185.46
```

**Serveurs DNS courants :**
- `8.8.8.8` : Google DNS
- `1.1.1.1` : Cloudflare DNS
- `208.67.222.222` : OpenDNS

---

**5. Ports**

Les applications écoutent sur des ports.

| Port | Service | Description |
|------|---------|-------------|
| 22 | SSH | Connexion sécurisée |
| 80 | HTTP | Web non-chiffré |
| 443 | HTTPS | Web chiffré |
| 53 | DNS | Résolution noms |
| 3306 | MySQL | Base de données |
| 5432 | PostgreSQL | Base de données |
| 6379 | Redis | Cache |

**Format complet :** `IP:PORT` -> `192.168.1.100:80`

---

### ÉTAPE 2 : Commande `ip` - Configuration réseau moderne

**`ip` = Outil moderne de configuration réseau (remplace ifconfig)**

**Syntaxe de base :**

```
ip [OPTIONS] OBJET COMMANDE
```

**Objets :**
- `link` : Interfaces réseau (couche 2)
- `addr` : Adresses IP (couche 3)
- `route` : Table de routage
- `neigh` : Table ARP (voisins)

---

**Test 1 : Voir toutes les interfaces réseau**

```bash
# Lister les interfaces
ip link show

# Ou plus court
ip link
```

**Résultat typique :**

```
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
    link/ether 00:15:5d:01:ca:05 brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff
```

**Colonnes :**

```
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
│   │                   │                    │
│   │                   │                    └─ MTU (Maximum Transmission Unit)
│   │                   └────────────────────── Flags (état)
│   └────────────────────────────────────────── Nom interface
└────────────────────────────────────────────── Index
```

**Flags importants :**
- `UP` : Interface active
- `LOWER_UP` : Câble branché / WiFi connecté
- `BROADCAST` : Supporte le broadcast
- `MULTICAST` : Supporte le multicast
- `LOOPBACK` : Interface loopback (lo)

**Interfaces courantes :**

| Interface | Description |
|-----------|-------------|
| `lo` | Loopback (127.0.0.1) |
| `eth0` | Ethernet (câble) |
| `ens33` | Ethernet (nomenclature moderne) |
| `wlan0` | WiFi |
| `docker0` | Bridge Docker |
| `virbr0` | Bridge KVM/libvirt |

---

**Test 2 : Voir les adresses IP**

```bash
# Toutes les adresses
ip addr show

# Ou plus court
ip a

# Interface spécifique
ip addr show eth0
```

**Résultat typique :**

```
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
    link/ether 00:15:5d:01:ca:05 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe01:ca05/64 scope link
       valid_lft forever preferred_lft forever
```

**Informations :**

```
inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0
     └──────┬──────┘     └──────┬──────┘        └─┬──┘
         IP + CIDR          Broadcast          Interface
```

---

**Test 3 : Voir la table de routage**

```bash
# Table de routage
ip route show

# Ou plus court
ip r
```

**Résultat typique :**

```
default via 192.168.1.1 dev eth0 proto dhcp metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
```

**Explication :**

```
default via 192.168.1.1 dev eth0
└─┬───┘     └────┬────┘     └─┬─┘
  │              │            └─ Interface
  │              └────────────── Gateway (passerelle)
  └───────────────────────────── Route par défaut (Internet)

192.168.1.0/24 dev eth0 ... src 192.168.1.100
└──────┬──────┘         │      └───────┬───────┘
   Réseau local    Interface      IP source
```

**Route par défaut** = Où envoyer les paquets pour les destinations inconnues (Internet).

---

**Test 4 : Statistiques des interfaces**

```bash
# Statistiques (paquets envoyés/reçus)
ip -s link show eth0

# Version détaillée
ip -s -s link show eth0
```

**Résultat :**

```
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP>
    RX: bytes  packets  errors  dropped overrun mcast
    1234567    9876     0       0       0       123
    TX: bytes  packets  errors  dropped carrier collsns
    7654321    8765     0       0       0       0
```

**Colonnes :**
- **RX** : Reçu (receive)
- **TX** : Transmis (transmit)
- **errors** : Erreurs
- **dropped** : Paquets abandonnés

---

**Options utiles de `ip` :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-s` | Statistiques | `ip -s link` |
| `-4` | IPv4 seulement | `ip -4 addr` |
| `-6` | IPv6 seulement | `ip -6 addr` |
| `-c` | Couleurs | `ip -c addr` |
| `-br` | Brief (compact) | `ip -br addr` |

---

**Test 5 : Format brief (plus lisible)**

```bash
# Version compacte
ip -br addr

# Avec couleurs
ip -c -br addr
```

**Résultat :**

```
lo               UNKNOWN        127.0.0.1/8 ::1/128
eth0             UP             192.168.1.100/24 fe80::215:5dff:fe01:ca05/64
wlan0            DOWN
```

Beaucoup plus lisible ! [OK]

---

### ÉTAPE 3 : Commande `ifconfig` - Ancienne méthode

**`ifconfig` = Ancienne commande (deprecated, mais encore utilisée)**

**[ATTENTION] Recommandation : Utiliser `ip` à la place**

---

**Test 1 : ifconfig basique**

```bash
# Installer si nécessaire
sudo apt install net-tools

# Toutes les interfaces
ifconfig

# Interface spécifique
ifconfig eth0
```

**Résultat :**

```
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.100  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::215:5dff:fe01:ca05  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:01:ca:05  txqueuelen 1000  (Ethernet)
        RX packets 12345  bytes 1234567 (1.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9876  bytes 987654 (987.6 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
```

---

**Équivalences ifconfig -> ip :**

| ifconfig | ip | Description |
|----------|----|----|
| `ifconfig` | `ip addr` | Voir IPs |
| `ifconfig eth0` | `ip addr show eth0` | Interface spécifique |
| `ifconfig -a` | `ip link` | Toutes interfaces |
| `route` | `ip route` | Table routage |

---

### ÉTAPE 4 : Commande `ping` - Tester la connectivité

**`ping` = Envoie des paquets ICMP pour tester la connectivité**

**Syntaxe :**

```
ping [OPTIONS] DESTINATION
```

---

**Test 1 : Ping basique**

```bash
# Ping vers Google
ping google.com

# Résultat :
# PING google.com (142.250.185.46) 56(84) bytes of data.
# 64 bytes from par21s23-in-f14.1e100.net (142.250.185.46): icmp_seq=1 ttl=118 time=15.2 ms
# 64 bytes from par21s23-in-f14.1e100.net (142.250.185.46): icmp_seq=2 ttl=118 time=15.5 ms
# ...
```

**Colonnes :**

```
64 bytes from ... icmp_seq=1 ttl=118 time=15.2 ms
│                     │       │       └─────────── Temps de réponse (latence)
│                     │       └─────────────────── TTL (Time To Live)
│                     └─────────────────────────── Numéro de séquence
└───────────────────────────────────────────────── Taille paquet
```

**TTL (Time To Live) :**
- Nombre de sauts (routeurs) maximum
- Décrémenté à chaque routeur
- Permet d'identifier l'OS approximativement :
  - `64` : Linux
  - `128` : Windows
  - `255` : Cisco

**Arrêter ping : Ctrl+C**

---

**Test 2 : Ping avec limite de paquets**

```bash
# Envoyer seulement 4 paquets
ping -c 4 google.com

# Résultat final :
# --- google.com ping statistics ---
# 4 packets transmitted, 4 received, 0% packet loss, time 3004ms
# rtt min/avg/max/mdev = 15.123/15.456/15.789/0.234 ms
```

**Statistiques :**
- **transmitted** : Paquets envoyés
- **received** : Paquets reçus
- **packet loss** : Perte de paquets (% perdus)
- **rtt** : Round Trip Time (temps aller-retour)
  - **min** : Minimum
  - **avg** : Moyenne
  - **max** : Maximum
  - **mdev** : Déviation standard

---

**Test 3 : Ping avec intervalle personnalisé**

```bash
# Ping toutes les 0.2 secondes (nécessite root)
sudo ping -i 0.2 google.com

# Ping toutes les 5 secondes
ping -i 5 google.com
```

---

**Test 4 : Ping flood (test de charge)**

```bash
# Envoyer le maximum de paquets (NÉCESSITE ROOT)
sudo ping -f google.com

# [ATTENTION] À utiliser avec précaution !
# Peut saturer le réseau
```

---

**Test 5 : Taille de paquet personnalisée**

```bash
# Paquet de 1000 bytes
ping -s 1000 google.com

# Paquet de 8000 bytes (test MTU)
ping -s 8000 -M do google.com
# Si échec = MTU trop petit
```

---

**Cas d'usage du ping :**

**1. Vérifier connectivité Internet**

```bash
# Ping vers un serveur public
ping -c 4 8.8.8.8

# Si ça marche : Internet OK
# Si ça échoue : Pas d'Internet
```

---

**2. Vérifier connectivité locale**

```bash
# Ping vers la gateway
ping -c 4 192.168.1.1

# Si ça marche : Réseau local OK
# Si ça échoue : Problème réseau local
```

---

**3. Vérifier DNS**

```bash
# Ping par IP (sans DNS)
ping -c 4 8.8.8.8
# OK

# Ping par nom (avec DNS)
ping -c 4 google.com
# Si échec : Problème DNS
```

---

**4. Mesurer la latence**

```bash
# Latence vers un serveur
ping -c 100 google.com

# Analyser les stats :
# rtt min/avg/max/mdev = 15.1/16.2/25.3/2.1 ms
#         │    │    │    │
#      Meilleur Moyen Pire Variation
```

---

**Options utiles de ping :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-c N` | Nombre de paquets | `ping -c 4 google.com` |
| `-i SEC` | Intervalle | `ping -i 2 google.com` |
| `-s SIZE` | Taille paquet | `ping -s 1000 google.com` |
| `-W SEC` | Timeout | `ping -W 5 google.com` |
| `-q` | Quiet (stats seulement) | `ping -q -c 10 google.com` |
| `-f` | Flood (max speed) | `sudo ping -f google.com` |
| `-4` | IPv4 | `ping -4 google.com` |
| `-6` | IPv6 | `ping -6 google.com` |

---

### ÉTAPE 5 : Commande `traceroute` - Tracer la route

**`traceroute` = Montre le chemin des paquets vers la destination**

```bash
# Installer si nécessaire
sudo apt install traceroute
```

---

**Test 1 : traceroute basique**

```bash
# Tracer la route vers Google
traceroute google.com
```

**Résultat typique :**

```
traceroute to google.com (142.250.185.46), 30 hops max, 60 byte packets
 1  _gateway (192.168.1.1)  1.234 ms  1.456 ms  1.678 ms
 2  10.0.0.1 (10.0.0.1)  5.123 ms  5.456 ms  5.789 ms
 3  * * *
 4  72.14.232.85 (72.14.232.85)  15.123 ms  15.456 ms  15.789 ms
 5  108.170.252.1 (108.170.252.1)  16.234 ms  16.567 ms  16.890 ms
 6  142.250.185.46 (142.250.185.46)  17.123 ms  17.456 ms  17.789 ms
```

**Colonnes :**

```
 1  _gateway (192.168.1.1)  1.234 ms  1.456 ms  1.678 ms
 │      │         │          └────────┬────────┘
 │      │         │            3 mesures de temps
 │      │         └──────────── Adresse IP
 │      └────────────────────── Nom d'hôte
 └───────────────────────────── Numéro de saut (hop)
```

**`* * *`** = Le routeur ne répond pas (firewall ou politique de sécurité)

---

**Test 2 : traceroute avec options**

```bash
# Utiliser ICMP au lieu de UDP
sudo traceroute -I google.com

# Limiter le nombre de sauts
traceroute -m 10 google.com

# Attendre moins longtemps
traceroute -w 2 google.com
```

---

**Alternative : `mtr` (My TraceRoute)**

**`mtr` = traceroute + ping combinés en temps réel**

```bash
# Installer
sudo apt install mtr

# Lancer
mtr google.com
```

**Résultat interactif :**

```
                             My traceroute  [v0.93]
Host                          Loss%   Snt   Last   Avg  Best  Wrst StDev
1. _gateway                    0.0%    10    1.2   1.3   1.1   1.5   0.1
2. 10.0.0.1                    0.0%    10    5.4   5.5   5.3   5.8   0.2
3. ???                       100.0%    10    0.0   0.0   0.0   0.0   0.0
4. 72.14.232.85                0.0%    10   15.6  15.7  15.4  16.2   0.3
```

**Colonnes :**
- **Loss%** : Perte de paquets
- **Snt** : Paquets envoyés
- **Last** : Dernier temps
- **Avg** : Moyenne
- **Best** : Meilleur
- **Wrst** : Pire

**Quitter mtr : `q`**

---

### ÉTAPE 6 : Résolution DNS - nslookup et dig

**DNS = Traduire noms -> IPs**

---

**Commande `host` - Simple**

```bash
# Résolution basique
host google.com

# Résultat :
# google.com has address 142.250.185.46
# google.com has IPv6 address 2a00:1450:4007:80f::200e

# Résolution inverse (IP -> nom)
host 8.8.8.8
# 8.8.8.8.in-addr.arpa domain name pointer dns.google.
```

---

**Commande `nslookup` - Interactif**

```bash
# Résolution simple
nslookup google.com

# Résultat :
# Server:         127.0.0.53
# Address:        127.0.0.53#53
# 
# Non-authoritative answer:
# Name:   google.com
# Address: 142.250.185.46
```

**Lignes :**

```
Server:         127.0.0.53
                └────┬────┘
          Serveur DNS utilisé (systemd-resolved)

Name:   google.com
Address: 142.250.185.46
         └──────┬──────┘
           Résultat
```

---

**nslookup avec serveur DNS spécifique :**

```bash
# Utiliser Google DNS (8.8.8.8)
nslookup google.com 8.8.8.8

# Utiliser Cloudflare DNS (1.1.1.1)
nslookup google.com 1.1.1.1
```

---

**Commande `dig` - Détaillé (recommandé)**

```bash
# Résolution complète
dig google.com

# Résultat plus court
dig google.com +short
# 142.250.185.46
```

**Résultat complet :**

```
; <<>> DiG 9.16.1-Ubuntu <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             300     IN      A       142.250.185.46

;; Query time: 15 msec
;; SERVER: 127.0.0.53#53(127.0.0.53)
;; WHEN: Mon Dec 30 10:00:00 UTC 2024
;; MSG SIZE  rcvd: 55
```

**Sections importantes :**

```
;; ANSWER SECTION:
google.com.             300     IN      A       142.250.185.46
                        v           v           └──────┬──────┘
                      TTL        Type             Adresse IP
                   (5 minutes)
```

**Types d'enregistrements DNS :**

| Type | Description | Exemple |
|------|-------------|---------|
| **A** | IPv4 | `142.250.185.46` |
| **AAAA** | IPv6 | `2a00:1450:4007:80f::200e` |
| **MX** | Serveur mail | `mail.google.com` |
| **CNAME** | Alias | `www -> example.com` |
| **NS** | Serveur DNS | `ns1.google.com` |
| **TXT** | Texte (SPF, DKIM) | `v=spf1 ...` |

---

**Test 1 : Différents types d'enregistrements**

```bash
# Enregistrement A (IPv4)
dig google.com A +short

# Enregistrement AAAA (IPv6)
dig google.com AAAA +short

# Enregistrements MX (Mail)
dig google.com MX +short

# Enregistrements NS (Name Servers)
dig google.com NS +short

# Tous les enregistrements
dig google.com ANY
```

---

**Test 2 : dig avec serveur DNS spécifique**

```bash
# Utiliser Google DNS
dig @8.8.8.8 google.com +short

# Utiliser Cloudflare DNS
dig @1.1.1.1 google.com +short

# Comparer plusieurs DNS
echo "Google DNS:"
dig @8.8.8.8 example.com +short
echo "Cloudflare DNS:"
dig @1.1.1.1 example.com +short
```

---

**Test 3 : dig reverse (IP -> nom)**

```bash
# Résolution inverse
dig -x 8.8.8.8 +short
# dns.google.
```

---

**Test 4 : Trace DNS (voir le processus de résolution)**

```bash
# Tracer la résolution DNS
dig google.com +trace
```

Montre toutes les étapes depuis les serveurs racines !

---

**Fichier `/etc/resolv.conf` - Configuration DNS**

```bash
# Voir la configuration DNS
cat /etc/resolv.conf
```

**Résultat typique :**

```
nameserver 127.0.0.53
options edns0 trust-ad
search local
```

**`nameserver`** = Serveur DNS utilisé (souvent géré par systemd-resolved)

---

**Fichier `/etc/hosts` - Résolution locale**

```bash
# Voir les entrées statiques
cat /etc/hosts
```

**Résultat typique :**

```
127.0.0.1       localhost
127.0.1.1       hostname
192.168.1.10    server.local
```

**`/etc/hosts` est consulté AVANT le DNS !**

---

### ÉTAPE 7 : Connexions actives - netstat et ss

**Voir les connexions réseau, ports ouverts, services qui écoutent.**

---

**Commande `netstat` - Ancienne méthode**

```bash
# Installer si nécessaire
sudo apt install net-tools

# Toutes les connexions
netstat -a

# Seulement les connexions TCP
netstat -t

# Seulement les ports en écoute
netstat -l

# Avec numéros de ports (pas de résolution)
netstat -n

# Avec PIDs et programmes
sudo netstat -p

# Combinaison utile : tout avec PIDs
sudo netstat -tulpn
```

---

**Options principales de netstat :**

| Option | Description |
|--------|-------------|
| `-t` | TCP |
| `-u` | UDP |
| `-l` | Listening (écoute) |
| `-a` | All (toutes) |
| `-n` | Numeric (pas de résolution) |
| `-p` | Program (afficher PIDs) |

---

**Test 1 : Voir les ports en écoute**

```bash
# Ports TCP en écoute
sudo netstat -tlnp
```

**Résultat :**

```
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1234/sshd
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      5678/mysqld
tcp6       0      0 :::80                   :::*                    LISTEN      9012/apache2
```

**Colonnes :**

```
tcp     0.0.0.0:22      0.0.0.0:*       LISTEN      1234/sshd
│         │   │           │              │          └────┬────┘
│         │   │           │              │            PID/Programme
│         │   │           │              └──────────── État
│         │   │           └────────────────────────── Adresse distante
│         │   └────────────────────────────────────── Port local
│         └────────────────────────────────────────── IP locale
└──────────────────────────────────────────────────── Protocole
```

**0.0.0.0** = Écoute sur toutes les interfaces
**127.0.0.1** = Écoute seulement en local (localhost)

---

**Commande `ss` - Moderne (remplace netstat)**

**`ss` = Socket Statistics (plus rapide que netstat)**

```bash
# Toutes les connexions
ss -a

# Ports en écoute TCP
ss -tln

# Ports en écoute TCP avec PIDs
sudo ss -tlnp

# Connexions établies
ss -t state established
```

---

**Test 1 : Voir qui écoute sur un port spécifique**

```bash
# Méthode 1 : netstat
sudo netstat -tlnp | grep :80

# Méthode 2 : ss
sudo ss -tlnp | grep :80

# Méthode 3 : lsof
sudo lsof -i :80

# Méthode 4 : fuser
sudo fuser 80/tcp
```

---

**Test 2 : Statistiques réseau**

```bash
# Statistiques par protocole
ss -s

# Résultat :
# Total: 345
# TCP:   123 (estab 45, closed 78, orphaned 0, timewait 12)
# UDP:   89
# RAW:   3
```

---

**Test 3 : Connexions par état**

```bash
# Connexions établies
ss -t state established

# Connexions en TIME_WAIT
ss -t state time-wait

# Connexions fermées
ss -t state close-wait
```

**États TCP :**

| État | Description |
|------|-------------|
| **LISTEN** | En écoute (serveur) |
| **ESTABLISHED** | Connexion active |
| **TIME_WAIT** | Attente après fermeture |
| **CLOSE_WAIT** | Attente fermeture locale |
| **SYN_SENT** | SYN envoyé (connexion en cours) |
| **FIN_WAIT** | FIN envoyé (fermeture en cours) |

---

**Comparaison netstat vs ss :**

| Fonctionnalité | netstat | ss |
|----------------|---------|-----|
| Vitesse | * Lent | *** Rapide |
| Moderne | [X] Deprecated | [OK] Recommandé |
| Filtres | Limités | Avancés |
| Syntaxe | Simple | Plus complexe |

**Recommandation : Utiliser `ss` sur les systèmes modernes**

---

### ÉTAPE 8 : Hostname et informations système

**Test 1 : Nom d'hôte**

```bash
# Voir le hostname
hostname

# Hostname complet (FQDN)
hostname -f

# Voir toutes les infos
hostnamectl

# Changer le hostname (persistant)
sudo hostnamectl set-hostname nouveau-nom
```

---

**Test 2 : Informations réseau rapides**

```bash
# IP publique (via service externe)
curl ifconfig.me
# Ou
curl icanhazip.com

# IP locale (rapide)
hostname -I

# DNS servers
cat /etc/resolv.conf | grep nameserver
```

---

### ÉTAPE 9 : Diagnostic de problèmes réseau

**Méthodologie de diagnostic :**

```
1. Interface UP ?        -> ip link
2. IP configurée ?       -> ip addr
3. Gateway définie ?     -> ip route
4. Gateway pingable ?    -> ping <gateway>
5. Internet accessible ? -> ping 8.8.8.8
6. DNS fonctionne ?      -> ping google.com
7. Service écoute ?      -> ss -tlnp
8. Firewall bloque ?     -> iptables / ufw
```

---

**Script de diagnostic :**

```bash
#!/bin/bash
# network-diag.sh

echo "[RECHERCHE] Diagnostic Réseau"
echo "==================="
echo ""

# 1. Interfaces
echo "1⃣  Interfaces réseau :"
ip -br addr
echo ""

# 2. Gateway
echo "2⃣  Passerelle (Gateway) :"
GATEWAY=$(ip route | grep default | awk '{print $3}')
echo "   Gateway: $GATEWAY"
echo ""

# 3. Test Gateway
echo "3⃣  Test Gateway :"
if ping -c 2 -W 2 $GATEWAY > /dev/null 2>&1; then
    echo "   [OK] Gateway accessible"
else
    echo "   [X] Gateway inaccessible"
fi
echo ""

# 4. Test Internet (IP)
echo "4⃣  Test Internet (IP) :"
if ping -c 2 -W 2 8.8.8.8 > /dev/null 2>&1; then
    echo "   [OK] Internet accessible"
else
    echo "   [X] Internet inaccessible"
fi
echo ""

# 5. Test DNS
echo "5⃣  Test DNS :"
if ping -c 2 -W 2 google.com > /dev/null 2>&1; then
    echo "   [OK] DNS fonctionne"
else
    echo "   [X] DNS ne fonctionne pas"
fi
echo ""

# 6. Serveurs DNS configurés
echo "6⃣  Serveurs DNS :"
cat /etc/resolv.conf | grep nameserver
echo ""

# 7. Ports en écoute
echo "7⃣  Ports en écoute :"
sudo ss -tlnp | head -10
echo ""

echo "[OK] Diagnostic terminé"
```

---

**Problèmes courants et solutions :**

**1. Pas d'IP (169.254.x.x ou aucune IP)**

```bash
# Vérifier
ip addr show eth0

# Solutions :
# A. Redémarrer l'interface
sudo ip link set eth0 down
sudo ip link set eth0 up

# B. Redémarrer NetworkManager
sudo systemctl restart NetworkManager

# C. Renouveler DHCP
sudo dhclient -r  # Release
sudo dhclient     # Renew
```

---

**2. Ping gateway échoue**

```bash
# Problème : Réseau local cassé

# Vérifications :
# - Câble branché ? (ip link | grep LOWER_UP)
# - Bonne IP/masque ?
# - Bon réseau ?

# Exemple :
# IP: 192.168.1.100/24
# Gateway: 192.168.2.1
# [X] Pas le même réseau !
```

---

**3. Ping 8.8.8.8 marche, ping google.com échoue**

```bash
# Problème : DNS cassé

# Vérifier DNS
cat /etc/resolv.conf

# Test manuel
nslookup google.com 8.8.8.8

# Solution temporaire : Changer DNS
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf

# Solution permanente (Ubuntu/Debian)
sudo vim /etc/systemd/resolved.conf
# [Resolve]
# DNS=8.8.8.8 1.1.1.1

sudo systemctl restart systemd-resolved
```

---

**4. Port censé être ouvert n'apparaît pas**

```bash
# Vérifier qu'il écoute
sudo ss -tlnp | grep :80

# Si absent :
# - Service démarré ? (systemctl status apache2)
# - Config correcte ? (nginx -t)
# - Écoute sur bonne interface ? (0.0.0.0 vs 127.0.0.1)
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : Connectivité Internet**

```bash
if ping -c 2 -W 5 8.8.8.8 > /dev/null 2>&1; then
    echo "[OK] Test 1 OK : Connectivité Internet"
else
    echo "[X] Test 1 FAIL : Pas d'Internet"
fi
```

---

**Test 2 : DNS fonctionne**

```bash
if host google.com > /dev/null 2>&1; then
    echo "[OK] Test 2 OK : DNS fonctionne"
else
    echo "[X] Test 2 FAIL : DNS cassé"
fi
```

---

**Test 3 : Interface réseau active**

```bash
PRIMARY_IF=$(ip route | grep default | awk '{print $5}' | head -1)

if ip link show $PRIMARY_IF | grep -q "state UP"; then
    echo "[OK] Test 3 OK : Interface $PRIMARY_IF UP"
else
    echo "[X] Test 3 FAIL : Interface $PRIMARY_IF DOWN"
fi
```

---

**Test 4 : IP configurée**

```bash
IP=$(ip -4 addr show $PRIMARY_IF | grep -oP '(?<=inet\s)\d+(\.\d+){3}')

if [ -n "$IP" ]; then
    echo "[OK] Test 4 OK : IP configurée ($IP)"
else
    echo "[X] Test 4 FAIL : Pas d'IP"
fi
```

---

**Test 5 : Gateway accessible**

```bash
GATEWAY=$(ip route | grep default | awk '{print $3}')

if ping -c 2 -W 2 $GATEWAY > /dev/null 2>&1; then
    echo "[OK] Test 5 OK : Gateway accessible ($GATEWAY)"
else
    echo "[X] Test 5 FAIL : Gateway inaccessible"
fi
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Commandes modernes vs anciennes**

| Ancien | Moderne | Fonction |
|--------|---------|----------|
| `ifconfig` | `ip addr` | Voir IPs |
| `route` | `ip route` | Table routage |
| `netstat` | `ss` | Connexions |
| `arp` | `ip neigh` | Table ARP |

**2. Commandes essentielles**
- `ip addr` : Voir les IPs
- `ip route` : Voir la gateway
- `ping` : Tester connectivité
- `traceroute` : Tracer route
- `dig` : Résolution DNS
- `ss` : Ports ouverts

**3. Diagnostic en 3 étapes**
1. **Connectivité locale** : `ping gateway`
2. **Connectivité Internet** : `ping 8.8.8.8`
3. **DNS** : `ping google.com`

**4. Fichiers importants**
- `/etc/resolv.conf` : Configuration DNS
- `/etc/hosts` : Résolution locale
- `/etc/network/interfaces` : Config réseau (Debian)
- `/etc/netplan/` : Config réseau (Ubuntu moderne)

**5. Concepts clés**
- **IP** : Adresse unique
- **Masque** : Définit le réseau
- **Gateway** : Routeur vers Internet
- **DNS** : Nom -> IP
- **Port** : Service spécifique

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. iftop - Monitoring bande passante**

```bash
sudo apt install iftop
sudo iftop -i eth0
```

---

**2. nethogs - Usage par processus**

```bash
sudo apt install nethogs
sudo nethogs eth0
```

---

**3. tcpdump - Capture de paquets**

```bash
# Capturer sur eth0
sudo tcpdump -i eth0

# Sauvegarder dans un fichier
sudo tcpdump -i eth0 -w capture.pcap

# Lire un fichier
tcpdump -r capture.pcap
```

---

**4. nmap - Scanner de ports**

```bash
sudo apt install nmap

# Scanner un hôte
nmap 192.168.1.1

# Scanner un réseau
nmap 192.168.1.0/24

# Scanner ports spécifiques
nmap -p 80,443,3306 192.168.1.1
```

---

**5. curl/wget - Test HTTP**

```bash
# Test simple
curl http://example.com

# Voir les headers
curl -I http://example.com

# Temps de réponse détaillés
curl -w "@-" -o /dev/null -s http://example.com << 'EOF'
    time_namelookup:  %{time_namelookup}s\n
       time_connect:  %{time_connect}s\n
    time_appconnect:  %{time_appconnect}s\n
      time_redirect:  %{time_redirect}s\n
   time_pretransfer:  %{time_pretransfer}s\n
 time_starttransfer:  %{time_starttransfer}s\n
                    ----------\n
         time_total:  %{time_total}s\n
EOF
```

---

## [COURS] CONCLUSION DE L'EXERCICE 5.1

**[OK] Félicitations ! Tu maîtrises maintenant le diagnostic réseau !**

**Ce que tu as appris :**
- Concepts réseau (IP, masque, gateway, DNS, ports)
- Commandes modernes (`ip`, `ss`)
- Test de connectivité (`ping`, `traceroute`)
- Résolution DNS (`dig`, `nslookup`)
- Voir les connexions actives
- Diagnostic de problèmes réseau
- Méthodologie de troubleshooting

**Compétences acquises :**
- [OK] Configuration réseau (niveau intermédiaire)
- [OK] Diagnostic de connectivité
- [OK] Résolution DNS
- [OK] Analyse des connexions
- [OK] Troubleshooting réseau

**Temps moyen de réalisation :** 1h30 - 2h

**Prochaine étape :** Exercice 5.2 - Configuration réseau et firewall ! [HOT]

---

Je continue avec l'exercice 5.2 ?

# [JAUNE] EXERCICE 5.2 : CONFIGURATION RÉSEAU ET FIREWALL (INTERMÉDIAIRE/AVANCÉ)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Configurer des serveurs avec IP statique
- Gérer plusieurs interfaces réseau
- Configurer des firewalls pour sécuriser les serveurs
- Ouvrir/fermer des ports spécifiques
- Créer des règles de filtrage réseau
- Protéger contre les attaques (brute force, DDoS)
- Configurer le NAT et le port forwarding
- Gérer les connexions VPN

**Problématique :** Un serveur mal configuré ou sans firewall est vulnérable aux attaques. Il faut savoir configurer le réseau de manière sécurisée et contrôler précisément le trafic entrant/sortant.

### Cahier des charges

- Configurer une IP statique
- Gérer la configuration réseau (NetworkManager, netplan)
- Comprendre et configurer iptables
- Utiliser UFW (Uncomplicated Firewall)
- Créer des règles de firewall avancées
- Bloquer des IPs malveillantes
- Configurer le NAT et le masquerading
- Sécuriser un serveur web
- Diagnostiquer les problèmes de firewall

### Contraintes

- Commandes : `ip`, `iptables`, `ufw`, `nmcli`, `netplan`
- Nécessite sudo
- Attention aux erreurs (peut couper la connexion SSH !)
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Configurer une IP statique
- [OK] Gérer NetworkManager et netplan
- [OK] Comprendre iptables
- [OK] Utiliser UFW efficacement
- [OK] Créer des règles de firewall
- [OK] Sécuriser un serveur
- [OK] Bloquer des attaques
- [OK] Configurer le NAT
- [OK] Diagnostiquer les problèmes
- [OK] Appliquer les bonnes pratiques

---

## [DOCS] PRÉREQUIS

- Exercice 5.1 terminé (Diagnostic réseau)
- Compréhension des concepts réseau
- Accès sudo
- [ATTENTION] **Attention** : Tester sur VM ou machine non-critique

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Configuration réseau - Méthodes

**Il existe plusieurs méthodes selon la distribution :**

| Distribution | Outil principal | Fichiers |
|--------------|----------------|----------|
| Ubuntu 18.04+ | Netplan | `/etc/netplan/*.yaml` |
| Debian/Ubuntu ancien | interfaces | `/etc/network/interfaces` |
| RHEL/CentOS | NetworkManager | `/etc/sysconfig/network-scripts/` |
| Arch | systemd-networkd | `/etc/systemd/network/` |

**[ATTENTION] Important : Une seule méthode à la fois !**

---

### ÉTAPE 2 : Configuration avec Netplan (Ubuntu moderne)

**Netplan = Frontend pour NetworkManager ou systemd-networkd**

---

**Test 1 : Voir la configuration actuelle**

```bash
# Lister les fichiers netplan
ls -la /etc/netplan/

# Voir la configuration
cat /etc/netplan/*.yaml
```

**Exemple de configuration DHCP :**

```yaml
# /etc/netplan/01-netcfg.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: true
```

---

**Test 2 : Configurer une IP statique avec netplan**

```bash
# Créer/éditer le fichier de configuration
sudo tee /etc/netplan/01-static-config.yaml << 'EOF'
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
      addresses:
        - 192.168.1.100/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 1.1.1.1
EOF

# Vérifier la syntaxe
sudo netplan try

# Si OK après 120 secondes, appliquer
sudo netplan apply
```

**[ATTENTION] `netplan try` = Applique et revient en arrière si problème (sécurité SSH)**

---

**Anatomie du fichier netplan :**

```yaml
network:
  version: 2                    # Version du format
  renderer: networkd            # Backend (networkd ou NetworkManager)
  ethernets:                    # Type d'interface
    eth0:                       # Nom de l'interface
      dhcp4: no                 # Pas de DHCP IPv4
      dhcp6: no                 # Pas de DHCP IPv6
      addresses:                # Adresses statiques
        - 192.168.1.100/24      # IP/CIDR
        - 10.0.0.5/8            # Plusieurs IPs possibles
      routes:                   # Routes
        - to: default           # Route par défaut
          via: 192.168.1.1      # Gateway
        - to: 10.10.0.0/16      # Route spécifique
          via: 192.168.1.254
      nameservers:              # Serveurs DNS
        addresses:
          - 8.8.8.8
          - 1.1.1.1
        search:                 # Domaines de recherche
          - local
          - example.com
```

---

**Test 3 : Configuration WiFi avec netplan**

```yaml
network:
  version: 2
  renderer: networkd
  wifis:
    wlan0:
      dhcp4: true
      access-points:
        "NomDuWiFi":
          password: "MotDePasseWiFi"
```

---

**Test 4 : Configuration VLAN**

```yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: no
  vlans:
    vlan10:
      id: 10
      link: eth0
      addresses:
        - 192.168.10.100/24
```

---

**Commandes netplan utiles :**

| Commande | Description |
|----------|-------------|
| `netplan try` | Tester (revient en arrière après 120s) |
| `netplan apply` | Appliquer |
| `netplan generate` | Générer config backend |
| `netplan --debug generate` | Debug |

---

### ÉTAPE 3 : Configuration avec NetworkManager

**NetworkManager = Gestionnaire réseau graphique et CLI**

---

**Test 1 : nmcli - Interface en ligne de commande**

```bash
# Voir toutes les connexions
nmcli connection show

# Voir les périphériques
nmcli device status

# Voir les détails d'une connexion
nmcli connection show "Wired connection 1"
```

---

**Test 2 : Créer une connexion statique**

```bash
# Supprimer l'ancienne connexion (si existe)
sudo nmcli connection delete "Wired connection 1"

# Créer nouvelle connexion statique
sudo nmcli connection add \
    type ethernet \
    con-name "static-eth0" \
    ifname eth0 \
    ipv4.addresses 192.168.1.100/24 \
    ipv4.gateway 192.168.1.1 \
    ipv4.dns "8.8.8.8 1.1.1.1" \
    ipv4.method manual

# Activer la connexion
sudo nmcli connection up "static-eth0"

# Vérifier
ip addr show eth0
```

---

**Test 3 : Modifier une connexion existante**

```bash
# Changer l'IP
sudo nmcli connection modify "static-eth0" ipv4.addresses 192.168.1.150/24

# Changer le DNS
sudo nmcli connection modify "static-eth0" ipv4.dns "1.1.1.1 8.8.8.8"

# Ajouter un DNS supplémentaire
sudo nmcli connection modify "static-eth0" +ipv4.dns 9.9.9.9

# Réactiver pour appliquer
sudo nmcli connection down "static-eth0"
sudo nmcli connection up "static-eth0"
```

---

**Test 4 : Configuration WiFi avec nmcli**

```bash
# Scanner les réseaux WiFi
nmcli device wifi list

# Se connecter à un réseau
sudo nmcli device wifi connect "NomDuWiFi" password "MotDePasse"

# Créer une connexion WiFi statique
sudo nmcli connection add \
    type wifi \
    con-name "wifi-home" \
    ifname wlan0 \
    ssid "NomDuWiFi" \
    wifi-sec.key-mgmt wpa-psk \
    wifi-sec.psk "MotDePasse" \
    ipv4.addresses 192.168.1.200/24 \
    ipv4.gateway 192.168.1.1 \
    ipv4.method manual
```

---

**Commandes nmcli utiles :**

| Commande | Description |
|----------|-------------|
| `nmcli con show` | Lister connexions |
| `nmcli dev status` | État des périphériques |
| `nmcli con up NAME` | Activer connexion |
| `nmcli con down NAME` | Désactiver connexion |
| `nmcli con add ...` | Créer connexion |
| `nmcli con modify ...` | Modifier connexion |
| `nmcli con delete NAME` | Supprimer connexion |

---

### ÉTAPE 4 : Configuration manuelle temporaire (ip)

**Configuration non-persistante (perdue au reboot) :**

---

**Test 1 : Ajouter/supprimer une IP**

```bash
# Ajouter une IP
sudo ip addr add 192.168.1.200/24 dev eth0

# Vérifier
ip addr show eth0

# Supprimer une IP
sudo ip addr del 192.168.1.200/24 dev eth0
```

---

**Test 2 : Activer/désactiver une interface**

```bash
# Désactiver
sudo ip link set eth0 down

# Activer
sudo ip link set eth0 up
```

---

**Test 3 : Ajouter/supprimer une route**

```bash
# Ajouter route par défaut
sudo ip route add default via 192.168.1.1

# Ajouter route spécifique
sudo ip route add 10.0.0.0/8 via 192.168.1.254

# Supprimer route
sudo ip route del 10.0.0.0/8

# Supprimer route par défaut
sudo ip route del default
```

---

### ÉTAPE 5 : Comprendre iptables

**iptables = Firewall Linux (netfilter)**

**Architecture :**

```
Paquets entrants -> PREROUTING -> ROUTING -> FORWARD -> POSTROUTING -> Paquets sortants
                                    v
                                  INPUT
                                    v
                               Application
                                    v
                                 OUTPUT
```

---

**Les 3 tables principales :**

| Table | Usage | Chaînes |
|-------|-------|---------|
| **filter** | Filtrage (défaut) | INPUT, OUTPUT, FORWARD |
| **nat** | NAT/Masquerading | PREROUTING, POSTROUTING, OUTPUT |
| **mangle** | Modification paquets | PREROUTING, OUTPUT, etc. |

---

**Les chaînes principales :**

| Chaîne | Description |
|--------|-------------|
| **INPUT** | Paquets entrants (vers le serveur) |
| **OUTPUT** | Paquets sortants (depuis le serveur) |
| **FORWARD** | Paquets transitant (routage) |
| **PREROUTING** | Avant routage (NAT) |
| **POSTROUTING** | Après routage (Masquerading) |

---

**Actions (targets) :**

| Target | Description |
|--------|-------------|
| **ACCEPT** | Accepter le paquet |
| **DROP** | Rejeter silencieusement |
| **REJECT** | Rejeter avec message |
| **LOG** | Logger dans syslog |
| **MASQUERADE** | NAT dynamique |
| **DNAT** | Destination NAT |
| **SNAT** | Source NAT |

---

**Test 1 : Voir les règles actuelles**

```bash
# Voir toutes les règles (table filter)
sudo iptables -L -v -n

# Avec numéros de ligne
sudo iptables -L -v -n --line-numbers

# Table NAT
sudo iptables -t nat -L -v -n

# Format compact
sudo iptables -S
```

**Options :**
- `-L` : List (lister)
- `-v` : Verbose (détaillé)
- `-n` : Numeric (pas de résolution DNS)
- `-t` : Table
- `-S` : Dump rules (format script)

---

**Test 2 : Règles de base**

```bash
# Autoriser le trafic loopback (localhost)
sudo iptables -A INPUT -i lo -j ACCEPT

# Autoriser connexions établies
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Autoriser SSH (port 22)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Autoriser HTTP (port 80)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# Autoriser HTTPS (port 443)
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Bloquer tout le reste
sudo iptables -A INPUT -j DROP
```

**Syntaxe :**

```
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
         │  │     │        │        │
         │  │     │        │        └─ Action
         │  │     │        └────────── Port destination
         │  │     └─────────────────── Protocole
         │  └───────────────────────── Chaîne
         └──────────────────────────── Append (ajouter)
```

**Options principales :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-A` | Append (ajouter à la fin) | `-A INPUT` |
| `-I` | Insert (insérer au début) | `-I INPUT 1` |
| `-D` | Delete (supprimer) | `-D INPUT 1` |
| `-R` | Replace (remplacer) | `-R INPUT 1` |
| `-F` | Flush (vider toutes) | `-F INPUT` |
| `-P` | Policy (politique par défaut) | `-P INPUT DROP` |
| `-p` | Protocol | `-p tcp` |
| `-s` | Source IP | `-s 192.168.1.100` |
| `-d` | Destination IP | `-d 10.0.0.1` |
| `-i` | Interface entrante | `-i eth0` |
| `-o` | Interface sortante | `-o eth1` |
| `--dport` | Port destination | `--dport 80` |
| `--sport` | Port source | `--sport 1024:65535` |
| `-m` | Module | `-m conntrack` |
| `-j` | Jump (target) | `-j ACCEPT` |

---

**Test 3 : Règles avancées**

```bash
# Autoriser ping (ICMP)
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# Limiter les connexions SSH (protection brute force)
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

# Bloquer une IP spécifique
sudo iptables -A INPUT -s 203.0.113.50 -j DROP

# Bloquer un réseau
sudo iptables -A INPUT -s 198.51.100.0/24 -j DROP

# Logger les paquets droppés
sudo iptables -A INPUT -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
sudo iptables -A INPUT -j DROP
```

---

**Test 4 : Politique par défaut**

```bash
# Voir la politique actuelle
sudo iptables -L | grep policy

# Définir politique par défaut : TOUT ACCEPTER (permissif)
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT

# Définir politique par défaut : TOUT BLOQUER (restrictif)
sudo iptables -P INPUT DROP
sudo iptables -P OUTPUT DROP
sudo iptables -P FORWARD DROP
```

**[ATTENTION] ATTENTION avec DROP par défaut : Risque de se bloquer soi-même !**

---

**Test 5 : Sauvegarder et restaurer**

```bash
# Sauvegarder les règles actuelles
sudo iptables-save > /tmp/iptables-backup.rules

# Restaurer
sudo iptables-restore < /tmp/iptables-backup.rules

# Rendre persistant (Ubuntu/Debian)
sudo apt install iptables-persistent
sudo netfilter-persistent save
sudo netfilter-persistent reload
```

---

**Test 6 : Réinitialiser complètement**

```bash
# Vider toutes les règles
sudo iptables -F
sudo iptables -t nat -F
sudo iptables -t mangle -F

# Supprimer les chaînes personnalisées
sudo iptables -X

# Remettre politique ACCEPT
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
```

---

### ÉTAPE 6 : UFW - Uncomplicated Firewall

**UFW = Frontend simple pour iptables**

**Avantages :**
- [OK] Syntaxe simple
- [OK] Profils d'application
- [OK] Logs clairs
- [OK] Recommandé pour débutants

---

**Test 1 : Installation et activation**

```bash
# Installer (si nécessaire)
sudo apt install ufw

# Vérifier l'état
sudo ufw status

# Activer
sudo ufw enable

# Désactiver
sudo ufw disable

# État détaillé
sudo ufw status verbose
```

---

**Test 2 : Règles de base UFW**

```bash
# Politique par défaut
sudo ufw default deny incoming   # Bloquer entrant
sudo ufw default allow outgoing  # Autoriser sortant

# Autoriser SSH (IMPORTANT avant d'activer !)
sudo ufw allow 22/tcp
# Ou
sudo ufw allow ssh

# Autoriser HTTP et HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Ou
sudo ufw allow 'Apache Full'

# Activer UFW
sudo ufw enable

# Vérifier
sudo ufw status numbered
```

---

**Test 3 : Règles avancées**

```bash
# Autoriser un port spécifique
sudo ufw allow 3306/tcp comment 'MySQL'

# Autoriser depuis une IP spécifique
sudo ufw allow from 192.168.1.100

# Autoriser depuis un réseau
sudo ufw allow from 192.168.1.0/24

# Autoriser depuis une IP vers un port
sudo ufw allow from 192.168.1.100 to any port 22

# Autoriser depuis une IP vers une interface
sudo ufw allow in on eth0 from 192.168.1.0/24

# Bloquer une IP
sudo ufw deny from 203.0.113.50

# Autoriser plage de ports
sudo ufw allow 6000:6007/tcp
```

---

**Test 4 : Profils d'application**

```bash
# Lister les profils disponibles
sudo ufw app list

# Voir les détails d'un profil
sudo ufw app info 'Apache Full'

# Autoriser un profil
sudo ufw allow 'Nginx Full'

# Créer un profil personnalisé
sudo tee /etc/ufw/applications.d/myapp << 'EOF'
[MyApp]
title=My Custom Application
description=My application ports
ports=8080,8443/tcp
EOF

# Recharger les profils
sudo ufw app update MyApp

# Utiliser
sudo ufw allow MyApp
```

---

**Test 5 : Gérer les règles**

```bash
# Lister avec numéros
sudo ufw status numbered

# Supprimer règle par numéro
sudo ufw delete 3

# Supprimer règle par syntaxe
sudo ufw delete allow 80/tcp

# Insérer règle à une position
sudo ufw insert 1 allow from 192.168.1.100
```

---

**Test 6 : Logs**

```bash
# Activer les logs
sudo ufw logging on

# Niveau de log
sudo ufw logging low     # Moins verbeux
sudo ufw logging medium  # Moyen
sudo ufw logging high    # Très verbeux
sudo ufw logging full    # Tout

# Voir les logs
sudo tail -f /var/log/ufw.log

# Ou avec journalctl
sudo journalctl -u ufw -f
```

---

**Test 7 : Réinitialiser UFW**

```bash
# Désactiver
sudo ufw disable

# Réinitialiser toutes les règles
sudo ufw reset

# Reconfigurer
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
```

---

**Commandes UFW principales :**

| Commande | Description |
|----------|-------------|
| `ufw enable` | Activer |
| `ufw disable` | Désactiver |
| `ufw status` | Voir l'état |
| `ufw allow PORT` | Autoriser port |
| `ufw deny PORT` | Bloquer port |
| `ufw delete RULE` | Supprimer règle |
| `ufw reset` | Réinitialiser |
| `ufw app list` | Lister profils |

---

### ÉTAPE 7 : Cas d'usage professionnels

**Cas 1 : Serveur web (Apache/Nginx)**

```bash
# Configuration UFW pour serveur web
sudo ufw default deny incoming
sudo ufw default allow outgoing

# SSH (TOUJOURS en premier !)
sudo ufw allow 22/tcp

# HTTP et HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Limiter SSH (protection brute force)
sudo ufw limit 22/tcp

# Activer
sudo ufw enable

# Vérifier
sudo ufw status verbose
```

---

**Cas 2 : Serveur de base de données**

```bash
# MySQL accessible seulement depuis les serveurs web
sudo ufw allow from 192.168.1.10 to any port 3306 comment 'WebServer1'
sudo ufw allow from 192.168.1.11 to any port 3306 comment 'WebServer2'

# PostgreSQL
sudo ufw allow from 192.168.1.0/24 to any port 5432
```

---

**Cas 3 : Protection contre brute force SSH**

**Méthode 1 : UFW limit**

```bash
# Limite à 6 connexions en 30 secondes
sudo ufw limit 22/tcp
```

---

**Méthode 2 : fail2ban**

```bash
# Installer
sudo apt install fail2ban

# Créer configuration locale
sudo tee /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
EOF

# Redémarrer
sudo systemctl restart fail2ban

# Voir les IPs bannies
sudo fail2ban-client status sshd

# Débannir une IP
sudo fail2ban-client set sshd unbanip 203.0.113.50
```

---

**Cas 4 : NAT et masquerading (routeur/gateway)**

```bash
# Activer le forwarding IP
sudo sysctl -w net.ipv4.ip_forward=1

# Rendre permanent
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

# Masquerading avec iptables
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT

# Sauvegarder
sudo netfilter-persistent save
```

**Schéma :**

```
Internet <--> [eth0] Serveur Gateway [eth1] <--> Réseau Local
            ^ IP publique         ^ 192.168.1.1
                                  └-> Clients 192.168.1.x
```

---

**Cas 5 : Port forwarding (redirection de port)**

```bash
# Rediriger port 80 public vers serveur interne port 8080
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080

# Autoriser le forward
sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 8080 -j ACCEPT

# Schéma :
# Internet:80 -> Gateway -> 192.168.1.100:8080
```

---

**Cas 6 : Bloquer les pays (GeoIP)**

```bash
# Installer xtables-addons
sudo apt install xtables-addons-common

# Télécharger la base GeoIP
sudo mkdir -p /usr/share/xt_geoip
cd /tmp
wget https://download.db-ip.com/free/dbip-country-lite-$(date +%Y-%m).csv.gz
gunzip dbip-country-lite-*.csv.gz
/usr/lib/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip/ dbip-country-lite-*.csv

# Bloquer un pays (exemple : CN = Chine)
sudo iptables -A INPUT -m geoip --src-cc CN -j DROP

# Autoriser seulement certains pays
sudo iptables -A INPUT -m geoip ! --src-cc FR,US,GB -j DROP
```

---

**Cas 7 : Rate limiting (protection DDoS)**

```bash
# Limiter les nouvelles connexions HTTP
sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m limit --limit 100/minute --limit-burst 200 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j DROP

# Limiter les pings (ICMP flood)
sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 5 -j ACCEPT
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
```

---

**Cas 8 : Firewall complet pour serveur web**

```bash
#!/bin/bash
# firewall-webserver.sh

# Réinitialiser
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

# Politique par défaut
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP

# Loopback
iptables -A INPUT -i lo -j ACCEPT

# Connexions établies
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# SSH (limité)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# HTTP/HTTPS (avec rate limiting)
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m limit --limit 100/minute --limit-burst 200 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -m limit --limit 100/minute --limit-burst 200 -j ACCEPT

# Ping (limité)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 5 -j ACCEPT

# Logs
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPT-INPUT-DROP: " --log-level 7

# Sauvegarder
iptables-save > /etc/iptables/rules.v4

echo "[OK] Firewall configuré"
```

---

### ÉTAPE 8 : Diagnostic de problèmes firewall

**Problème 1 : Service inaccessible malgré être en écoute**

```bash
# 1. Vérifier que le service écoute
sudo ss -tlnp | grep :80

# 2. Vérifier le firewall UFW
sudo ufw status

# 3. Si bloqué, autoriser
sudo ufw allow 80/tcp

# 4. Vérifier iptables directement
sudo iptables -L -n -v | grep :80

# 5. Tester en local
curl localhost:80

# 6. Tester depuis l'extérieur
curl http://<IP-PUBLIQUE>
```

---

**Problème 2 : SSH bloqué après activation firewall**

```bash
# [ATTENTION] TOUJOURS autoriser SSH AVANT d'activer le firewall !

# Si déjà bloqué et accès console/KVM disponible :
sudo ufw disable
sudo ufw allow 22/tcp
sudo ufw enable

# Ou avec iptables :
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4
```

---

**Problème 3 : Connexion lente/timeouts**

```bash
# Vérifier si le firewall drop au lieu de reject
# DROP = Timeout (attend)
# REJECT = Réponse immédiate

# Changer DROP en REJECT pour les tests
sudo iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

# Ou avec UFW (par défaut = reject)
```

---

**Problème 4 : Règles ne s'appliquent pas**

```bash
# 1. Vérifier l'ordre des règles
sudo iptables -L -n -v --line-numbers

# 2. Les règles sont évaluées dans l'ordre !
# Si ACCEPT avant, les suivantes sont ignorées

# 3. Insérer au bon endroit
sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT

# 4. Ou réorganiser avec UFW
sudo ufw status numbered
sudo ufw delete 5
sudo ufw insert 1 allow 80/tcp
```

---

**Script de diagnostic firewall :**

```bash
#!/bin/bash
# firewall-diag.sh

echo "[RECHERCHE] Diagnostic Firewall"
echo "====================="
echo ""

# 1. UFW actif ?
echo "1⃣  UFW :"
if systemctl is-active --quiet ufw; then
    echo "   [OK] UFW actif"
    sudo ufw status verbose
else
    echo "   [ATTENTION]  UFW inactif"
fi
echo ""

# 2. iptables règles
echo "2⃣  iptables INPUT :"
sudo iptables -L INPUT -n -v --line-numbers | head -20
echo ""

# 3. Politique par défaut
echo "3⃣  Politiques par défaut :"
sudo iptables -L | grep policy
echo ""

# 4. Ports en écoute vs autorisés
echo "4⃣  Ports en écoute :"
sudo ss -tlnp | awk 'NR==1 || /LISTEN/'
echo ""

echo "5⃣  Règles UFW pour ces ports :"
for port in $(sudo ss -tlnp | awk '/LISTEN/ {split($4,a,":"); print a[2]}' | sort -u); do
    echo "   Port $port:"
    sudo ufw status | grep -i "$port" || echo "     [X] Pas de règle UFW"
done

echo ""
echo "[OK] Diagnostic terminé"
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : UFW fonctionne**

```bash
if sudo ufw status | grep -q "Status: active"; then
    echo "[OK] Test 1 OK : UFW actif"
else
    echo "[X] Test 1 FAIL : UFW inactif"
fi
```

---

**Test 2 : SSH autorisé**

```bash
if sudo ufw status | grep -q "22.*ALLOW"; then
    echo "[OK] Test 2 OK : SSH autorisé"
else
    echo "[ATTENTION]  Test 2 : SSH peut-être bloqué"
fi
```

---

**Test 3 : Politique par défaut correcte**

```bash
# Vérifier politique DROP/DENY sur INPUT
if sudo iptables -L | grep "Chain INPUT" | grep -q "DROP\|DENY"; then
    echo "[OK] Test 3 OK : Politique restrictive"
else
    echo "[ATTENTION]  Test 3 : Politique permissive"
fi
```

---

**Test 4 : IP statique configurée**

```bash
# Vérifier qu'on n'a pas une IP 169.254.x.x (APIPA)
IP=$(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v 127.0.0.1 | head -1)

if [[ ! "$IP" =~ ^169\.254\. ]]; then
    echo "[OK] Test 4 OK : IP valide ($IP)"
else
    echo "[X] Test 4 FAIL : IP auto-assignée"
fi
```

---

**Test 5 : Règles iptables présentes**

```bash
COUNT=$(sudo iptables -L INPUT -n | grep -c ACCEPT)

if [ "$COUNT" -gt 2 ]; then
    echo "[OK] Test 5 OK : $COUNT règles iptables"
else
    echo "[ATTENTION]  Test 5 : Peu de règles ($COUNT)"
fi
```

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : Bloqué dehors après activation du firewall

**Symptôme :**
```bash
sudo ufw enable
# Connection to server closed by remote host
```

**Cause :** SSH pas autorisé avant activation

**Prévention :**
```bash
# TOUJOURS faire ça AVANT d'activer
sudo ufw allow 22/tcp
sudo ufw enable
```

**Solution si bloqué :**
- Accès console/KVM nécessaire
- Ou attendre le reboot si règles non-persistantes

---

#### Erreur 2 : Conflit UFW et iptables

**Symptôme :**
```bash
# Règles iptables et UFW en même temps
# Comportement incohérent
```

**Solution :**
```bash
# Choisir UN SEUL outil

# Option 1 : Utiliser UFW
sudo systemctl enable ufw
sudo systemctl start ufw

# Option 2 : Utiliser iptables-persistent
sudo systemctl disable ufw
sudo apt install iptables-persistent
```

---

#### Erreur 3 : Service web inaccessible

**Diagnostic :**
```bash
# 1. Service écoute ?
sudo ss -tlnp | grep :80

# 2. Firewall autorise ?
sudo ufw status | grep 80

# 3. Test local OK ?
curl localhost

# 4. IP publique accessible ?
curl http://<IP_PUBLIQUE>
```

**Solution :**
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
```

---

#### Erreur 4 : Règles perdues après reboot

**Symptôme :**
```bash
# Après reboot, iptables vide
```

**Solution :**
```bash
# Installer iptables-persistent
sudo apt install iptables-persistent

# Sauvegarder les règles
sudo netfilter-persistent save

# Ou manuellement
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
```

---

#### Erreur 5 : NAT ne fonctionne pas

**Symptôme :**
```bash
# Clients internes n'ont pas Internet
```

**Vérifications :**
```bash
# 1. IP forwarding activé ?
cat /proc/sys/net/ipv4/ip_forward
# Doit être 1

# 2. Règle masquerading présente ?
sudo iptables -t nat -L POSTROUTING -n -v

# 3. Routing correct ?
ip route
```

**Solution :**
```bash
# Activer forwarding
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf

# Ajouter masquerading
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
sudo iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

# Sauvegarder
sudo netfilter-persistent save
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Configuration réseau**

| Outil | Distribution | Usage |
|-------|--------------|-------|
| Netplan | Ubuntu 18.04+ | Configuration YAML |
| NetworkManager | RHEL/Fedora | GUI + nmcli |
| ip | Toutes | Temporaire |

**2. Firewall - Deux options**

| Outil | Niveau | Recommandation |
|-------|--------|----------------|
| UFW | Simple | [OK] Débutants/Intermédiaires |
| iptables | Avancé | * Experts |

**3. Règles UFW essentielles**
```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable
```

**4. Ordre des règles**
- [OK] Les règles sont évaluées dans l'ordre
- [OK] Première règle qui match = appliquée
- [OK] Mettre les règles spécifiques AVANT les générales

**5. Sécurité**
- [OK] TOUJOURS autoriser SSH avant d'activer
- [OK] Tester avec `netplan try` ou en console
- [OK] Principe du moindre privilège
- [OK] Logger les événements suspects
- [OK] Sauvegarder les règles

**6. Bonnes pratiques**
- Politique par défaut : DROP/DENY
- Autoriser seulement ce qui est nécessaire
- Limiter SSH (fail2ban, UFW limit)
- Rate limiting pour services publics
- Logs activés

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. nftables - Successeur d'iptables**

```bash
# Installer
sudo apt install nftables

# Syntaxe plus claire
sudo nft add table inet filter
sudo nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
sudo nft add rule inet filter input tcp dport 22 accept

# Lister
sudo nft list ruleset
```

---

**2. firewalld - Alternative à UFW**

```bash
sudo apt install firewalld

# Zones
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-port=8080/tcp
sudo firewall-cmd --runtime-to-permanent
```

---

**3. Suricata/Snort - IDS/IPS**

```bash
# Système de détection d'intrusion
sudo apt install suricata

# Configurer
sudo vim /etc/suricata/suricata.yaml

# Démarrer
sudo systemctl start suricata
```

---

**4. VPN avec WireGuard**

```bash
sudo apt install wireguard

# Générer clés
wg genkey | tee privatekey | wg pubkey > publickey

# Configurer
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
PrivateKey = <PRIVATE_KEY>
Address = 10.0.0.1/24
ListenPort = 51820

[Peer]
PublicKey = <PEER_PUBLIC_KEY>
AllowedIPs = 10.0.0.2/32
EOF

# Activer
sudo wg-quick up wg0
```

---

**5. Scripts d'automatisation**

```bash
#!/bin/bash
# auto-firewall-update.sh
# Met à jour les règles depuis une liste d'IPs malveillantes

BLOCKLIST_URL="https://example.com/badips.txt"
BLOCKLIST_FILE="/tmp/badips.txt"

# Télécharger
wget -q -O "$BLOCKLIST_FILE" "$BLOCKLIST_URL"

# Créer chaîne
sudo iptables -N BLOCKLIST 2>/dev/null || sudo iptables -F BLOCKLIST

# Ajouter à INPUT
sudo iptables -D INPUT -j BLOCKLIST 2>/dev/null
sudo iptables -I INPUT -j BLOCKLIST

# Ajouter IPs
while read ip; do
    sudo iptables -A BLOCKLIST -s "$ip" -j DROP
done < "$BLOCKLIST_FILE"

echo "[OK] Blocklist mise à jour ($(wc -l < $BLOCKLIST_FILE) IPs)"
```

---

## [COURS] CONCLUSION DE L'EXERCICE 5.2

**[OK] FÉLICITATIONS ! Tu maîtrises maintenant la configuration réseau et firewall !**

**Ce que tu as appris :**
- Configuration IP statique (netplan, nmcli)
- Gestion NetworkManager
- Architecture iptables
- Utilisation UFW
- Règles de firewall avancées
- NAT et port forwarding
- Protection contre attaques
- Diagnostic de problèmes
- Bonnes pratiques de sécurité

**Compétences acquises :**
- [OK] Configuration réseau (niveau avancé)
- [OK] Administration firewall
- [OK] Sécurisation serveurs
- [OK] Troubleshooting réseau/firewall
- [OK] Protection contre attaques

**Temps moyen de réalisation :** 2-3 heures

---

# [BRAVO] CONCLUSION SECTION 5 : RÉSEAU & CONNECTIVITÉ COMPLÈTE !

**[TROPHEE] FÉLICITATIONS ! Section 5 100% terminée !**

**Récapitulatif des 2 exercices :**

| Exercice | Niveau | Compétences |
|----------|--------|-------------|
| 5.1 | [VERT] Débutant | Diagnostic réseau (ip, ping, dig, ss) |
| 5.2 | [JAUNE] Intermédiaire | Configuration et firewall |

**Niveau atteint : EXPERT en Réseau Linux ! [WEB]**

---

Tu as maintenant une maîtrise complète du réseau Linux ! Tu veux continuer avec la **Section 6 : Archives & Compression** ?


# [DOCS] SECTION 6 : ARCHIVES & COMPRESSION

# [VERT] EXERCICE 6.1 : ARCHIVES ET COMPRESSION DE BASE (DÉBUTANT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Créer des backups de fichiers et dossiers
- Compresser des logs pour économiser l'espace disque
- Extraire des archives reçues de différentes sources
- Transférer des données entre serveurs
- Archiver des projets terminés
- Gérer des packages et distributions
- Optimiser l'espace de stockage

**Problématique :** Les données non compressées prennent beaucoup d'espace. Les logs, backups et transferts nécessitent une compression efficace. Il faut savoir créer, extraire et gérer différents types d'archives.

### Cahier des charges

- Comprendre les concepts d'archive vs compression
- Créer des archives tar
- Compresser avec gzip, bzip2, xz
- Utiliser tar avec compression (tar.gz, tar.bz2, tar.xz)
- Créer et extraire des fichiers zip
- Lister le contenu des archives
- Extraire partiellement
- Gérer les permissions et chemins

### Contraintes

- Commandes : `tar`, `gzip`, `bzip2`, `xz`, `zip`, `unzip`
- Comprendre les ratios de compression
- Performance vs taux de compression
- Temps estimé : 1h30 - 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Différencier archive et compression
- [OK] Créer des archives tar
- [OK] Compresser avec différents algorithmes
- [OK] Utiliser tar avec compression
- [OK] Gérer les archives zip
- [OK] Extraire tout ou partie d'une archive
- [OK] Préserver les permissions
- [OK] Choisir le bon format
- [OK] Optimiser l'espace disque
- [OK] Automatiser les backups

---

## [DOCS] PRÉREQUIS

- Sections 1-5 complètes
- Compréhension du système de fichiers
- Permissions (section 3)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre Archive vs Compression

**Concepts clés :**

**Archive** = Regrouper plusieurs fichiers en un seul fichier
- [OK] Un seul fichier au lieu de multiples
- [OK] Préserve la structure (dossiers, permissions)
- [X] Pas de gain d'espace

**Compression** = Réduire la taille d'un fichier
- [OK] Économise de l'espace
- [OK] Plus rapide à transférer
- [X] Nécessite décompression pour utiliser

**Archive + Compression** = Le meilleur des deux mondes !

---

**Analogie :**

```
Archive = Boîte qui contient plusieurs objets
Compression = Aspirer l'air de la boîte pour la rendre plus petite
Archive compressée = Boîte sous vide avec plusieurs objets dedans
```

---

**Formats courants sur Linux :**

| Format | Extension | Archive | Compression | Usage |
|--------|-----------|---------|-------------|-------|
| **tar** | `.tar` | [OK] | [X] | Archive seule |
| **gzip** | `.gz` | [X] | [OK] | Compression rapide |
| **bzip2** | `.bz2` | [X] | [OK] | Compression moyenne |
| **xz** | `.xz` | [X] | [OK] | Compression maximale |
| **tar.gz** | `.tar.gz` / `.tgz` | [OK] | [OK] | Standard Linux |
| **tar.bz2** | `.tar.bz2` / `.tbz2` | [OK] | [OK] | Meilleure compression |
| **tar.xz** | `.tar.xz` / `.txz` | [OK] | [OK] | Compression max |
| **zip** | `.zip` | [OK] | [OK] | Standard Windows/multi-plateforme |
| **rar** | `.rar` | [OK] | [OK] | Propriétaire (Windows) |
| **7z** | `.7z` | [OK] | [OK] | Compression élevée |

---

**Comparaison des algorithmes :**

| Algo | Vitesse | Compression | Mémoire | Usage typique |
|------|---------|-------------|---------|---------------|
| **gzip** | *** Rapide | ** Moyenne | *** Faible | Logs, backups quotidiens |
| **bzip2** | ** Moyen | *** Bonne | ** Moyenne | Backups importants |
| **xz** | * Lent | **** Excellente | * Élevée | Archives à long terme |
| **zip** | *** Rapide | ** Moyenne | *** Faible | Multi-plateforme |

---

### ÉTAPE 2 : Créer l'environnement de test

```bash
# Créer le dossier de travail
mkdir -p ~/archive-exercise
cd ~/archive-exercise

# Créer une structure de test
mkdir -p project/{src,docs,config,data}

# Fichiers source
cat > project/src/main.py << 'EOF'
#!/usr/bin/env python3
def main():
    print("Hello, World!")

if __name__ == "__main__":
    main()
EOF

cat > project/src/utils.py << 'EOF'
def add(a, b):
    return a + b

def multiply(a, b):
    return a * b
EOF

# Documentation
cat > project/docs/README.md << 'EOF'
# Mon Projet

## Description
Ceci est un projet de test pour l'exercice d'archivage.

## Installation
```bash
pip install -r requirements.txt
```

## Usage
```bash
python src/main.py
```
EOF

# Configuration
cat > project/config/settings.ini << 'EOF'
[database]
host = localhost
port = 5432
database = mydb

[server]
port = 8080
debug = false
EOF

# Données
for i in {1..10}; do
    echo "Line $i: $(date) - Random data $(openssl rand -hex 16)" >> project/data/data.txt
done

# Fichier volumineux pour test de compression
dd if=/dev/urandom of=project/data/largefile.bin bs=1M count=10 2>/dev/null

# Voir la structure
tree project/ 2>/dev/null || find project/ -type f

# Voir la taille totale
du -sh project/
```

---

### ÉTAPE 3 : Commande `tar` - Créer des archives

**tar = Tape ARchive (historiquement pour bandes magnétiques)**

**Syntaxe de base :**

```
tar [OPTIONS] [ARCHIVE] [FICHIERS]
```

**Options principales (style ancien) :**

| Option | Description |
|--------|-------------|
| **c** | Create (créer) |
| **x** | eXtract (extraire) |
| **t** | lisT (lister) |
| **v** | Verbose (verbeux) |
| **f** | File (fichier) |
| **z** | gZip compression |
| **j** | bZip2 compression |
| **J** | xZ compression |
| **p** | Preserve permissions |
| **C** | Change directory |

---

**Test 1 : Créer une archive tar simple**

```bash
# Créer une archive (sans compression)
tar -cvf project.tar project/

# Options :
# -c : create
# -v : verbose (afficher les fichiers)
# -f : file (nom du fichier)
```

**Résultat :**

```
project/
project/src/
project/src/main.py
project/src/utils.py
project/docs/
project/docs/README.md
project/config/
project/config/settings.ini
project/data/
project/data/data.txt
project/data/largefile.bin
```

**Vérifier la taille :**

```bash
# Comparer les tailles
du -sh project/
du -sh project.tar

# Exemple :
# project/     11M
# project.tar  11M
# -> Pas de compression, juste regroupement
```

---

**Test 2 : Lister le contenu d'une archive**

```bash
# Lister sans extraire
tar -tvf project.tar

# Options :
# -t : list
# -v : verbose (détails)
# -f : file
```

**Résultat :**

```
drwxr-xr-x user/user         0 2024-12-30 10:00 project/
drwxr-xr-x user/user         0 2024-12-30 10:00 project/src/
-rw-r--r-- user/user       123 2024-12-30 10:00 project/src/main.py
-rw-r--r-- user/user        89 2024-12-30 10:00 project/src/utils.py
...
```

---

**Test 3 : Extraire une archive**

```bash
# Supprimer le dossier original
rm -rf project/

# Extraire
tar -xvf project.tar

# Options :
# -x : extract
# -v : verbose
# -f : file

# Vérifier
ls -la project/
```

---

**Test 4 : Extraire dans un dossier spécifique**

```bash
# Créer dossier de destination
mkdir extracted

# Extraire dans ce dossier
tar -xvf project.tar -C extracted/

# Option -C : Change to directory

# Vérifier
ls extracted/
```

---

**Test 5 : Extraire seulement certains fichiers**

```bash
# Extraire seulement un fichier
tar -xvf project.tar project/src/main.py

# Extraire seulement un dossier
tar -xvf project.tar project/docs/

# Avec pattern (wildcard)
tar -xvf project.tar --wildcards '*.py'
```

---

**Test 6 : Ajouter des fichiers à une archive existante**

```bash
# Créer un nouveau fichier
echo "New file" > newfile.txt

# Ajouter à l'archive existante
tar -rvf project.tar newfile.txt

# Option -r : append (ajouter)

# Vérifier
tar -tvf project.tar | grep newfile
```

---

**Test 7 : Exclure des fichiers**

```bash
# Créer archive en excluant certains fichiers
tar -cvf project-light.tar project/ --exclude='*.bin' --exclude='data'

# Vérifier qu'ils sont exclus
tar -tvf project-light.tar
```

---

**Test 8 : Préserver les permissions**

```bash
# Créer fichier avec permissions spéciales
touch special.sh
chmod 755 special.sh

# Archiver en préservant permissions
tar -cvpf archive-perms.tar special.sh

# Option -p : preserve permissions

# Extraire
tar -xvpf archive-perms.tar

# Vérifier permissions préservées
ls -l special.sh
```

---

### ÉTAPE 4 : Compression avec tar (tar.gz, tar.bz2, tar.xz)

**tar peut compresser directement pendant l'archivage !**

---

**Test 1 : Créer une archive tar.gz (gzip)**

```bash
# Méthode 1 : Option -z
tar -czvf project.tar.gz project/

# Méthode 2 : Détection automatique (tar moderne)
tar -cavf project.tar.gz project/

# Options :
# -z : gzip compression
# -a : auto-detect compression from extension

# Comparer les tailles
ls -lh project.tar project.tar.gz
```

**Résultat typique :**

```
-rw-r--r-- 1 user user 11M Dec 30 10:00 project.tar
-rw-r--r-- 1 user user 5.2M Dec 30 10:00 project.tar.gz
```

**Compression ≈ 50% !**

---

**Test 2 : Créer une archive tar.bz2 (bzip2)**

```bash
# Option -j
tar -cjvf project.tar.bz2 project/

# Ou avec auto-detect
tar -cavf project.tar.bz2 project/

# Comparer
ls -lh project.tar.gz project.tar.bz2
```

**Résultat typique :**

```
-rw-r--r-- 1 user user 5.2M Dec 30 10:00 project.tar.gz
-rw-r--r-- 1 user user 4.8M Dec 30 10:00 project.tar.bz2
```

**bzip2 compresse un peu mieux que gzip**

---

**Test 3 : Créer une archive tar.xz (xz/LZMA)**

```bash
# Option -J (majuscule !)
tar -cJvf project.tar.xz project/

# Ou avec auto-detect
tar -cavf project.tar.xz project/

# Comparer
ls -lh project.tar.{gz,bz2,xz}
```

**Résultat typique :**

```
-rw-r--r-- 1 user user 5.2M Dec 30 10:00 project.tar.gz
-rw-r--r-- 1 user user 4.8M Dec 30 10:00 project.tar.bz2
-rw-r--r-- 1 user user 4.3M Dec 30 10:00 project.tar.xz
```

**xz compresse le mieux mais plus lent**

---

**Test 4 : Extraire les archives compressées**

```bash
# tar.gz
tar -xzvf project.tar.gz

# tar.bz2
tar -xjvf project.tar.bz2

# tar.xz
tar -xJvf project.tar.xz

# Ou avec auto-detect (tar moderne)
tar -xavf project.tar.gz
tar -xavf project.tar.bz2
tar -xavf project.tar.xz

# Encore plus simple (détection automatique sans -a)
tar -xvf project.tar.gz
tar -xvf project.tar.bz2
tar -xvf project.tar.xz
```

**tar moderne détecte automatiquement le type de compression !**

---

**Test 5 : Lister le contenu des archives compressées**

```bash
# tar.gz
tar -tzvf project.tar.gz | head

# tar.bz2
tar -tjvf project.tar.bz2 | head

# tar.xz
tar -tJvf project.tar.xz | head

# Ou auto-detect
tar -tvf project.tar.gz | head
```

---

**Comparaison pratique :**

```bash
# Script de comparaison
cat > compare-compression.sh << 'EOF'
#!/bin/bash

echo "[GRAPHIQUE] Comparaison des méthodes de compression"
echo "==========================================="
echo ""

# Taille originale
ORIG_SIZE=$(du -sb project/ | awk '{print $1}')
echo "Taille originale : $(numfmt --to=iec $ORIG_SIZE)"
echo ""

# tar (sans compression)
echo "Creating project.tar..."
time tar -cf project.tar project/ 2>&1 | grep real
TAR_SIZE=$(stat -c%s project.tar)
echo "Taille : $(numfmt --to=iec $TAR_SIZE)"
echo "Ratio : $(echo "scale=2; $TAR_SIZE * 100 / $ORIG_SIZE" | bc)%"
echo ""

# tar.gz
echo "Creating project.tar.gz..."
time tar -czf project.tar.gz project/ 2>&1 | grep real
GZ_SIZE=$(stat -c%s project.tar.gz)
echo "Taille : $(numfmt --to=iec $GZ_SIZE)"
echo "Ratio : $(echo "scale=2; $GZ_SIZE * 100 / $ORIG_SIZE" | bc)%"
echo ""

# tar.bz2
echo "Creating project.tar.bz2..."
time tar -cjf project.tar.bz2 project/ 2>&1 | grep real
BZ2_SIZE=$(stat -c%s project.tar.bz2)
echo "Taille : $(numfmt --to=iec $BZ2_SIZE)"
echo "Ratio : $(echo "scale=2; $BZ2_SIZE * 100 / $ORIG_SIZE" | bc)%"
echo ""

# tar.xz
echo "Creating project.tar.xz..."
time tar -cJf project.tar.xz project/ 2>&1 | grep real
XZ_SIZE=$(stat -c%s project.tar.xz)
echo "Taille : $(numfmt --to=iec $XZ_SIZE)"
echo "Ratio : $(echo "scale=2; $XZ_SIZE * 100 / $ORIG_SIZE" | bc)%"
echo ""

echo "Résumé :"
echo "--------"
ls -lh project.tar*
EOF

chmod +x compare-compression.sh
./compare-compression.sh
```

---

### ÉTAPE 5 : Compression de fichiers individuels

**gzip, bzip2, xz peuvent compresser des fichiers individuels**

---

**Test 1 : gzip - Compression rapide**

```bash
# Créer un fichier test
cp project/data/data.txt test.txt

# Compresser
gzip test.txt
# Crée test.txt.gz et SUPPRIME test.txt

# Vérifier
ls -lh test.txt*

# Décompresser
gunzip test.txt.gz
# Recrée test.txt et SUPPRIME test.txt.gz

# Ou avec gzip -d
gzip -d test.txt.gz
```

**[ATTENTION] Par défaut, gzip REMPLACE le fichier original !**

---

**Test 2 : gzip - Garder l'original**

```bash
# Compresser en gardant l'original
gzip -k test.txt

# Option -k : keep (garder)

# Maintenant on a les deux
ls -l test.txt*
```

---

**Test 3 : gzip - Niveau de compression**

```bash
# Copier plusieurs fois
cp test.txt test1.txt
cp test.txt test2.txt
cp test.txt test3.txt

# Compression rapide (niveau 1)
gzip -1 test1.txt

# Compression par défaut (niveau 6)
gzip test2.txt

# Compression maximale (niveau 9)
gzip -9 test3.txt

# Comparer
ls -lh test*.txt.gz
```

**Niveaux : 1 (rapide, moins compressé) à 9 (lent, plus compressé)**

---

**Test 4 : bzip2 - Meilleure compression**

```bash
# Compresser avec bzip2
bzip2 -k test.txt
# Crée test.txt.bz2

# Décompresser
bunzip2 test.txt.bz2
# Ou
bzip2 -d test.txt.bz2

# Comparer avec gzip
gzip -k test.txt
ls -lh test.txt.{gz,bz2}
```

---

**Test 5 : xz - Compression maximale**

```bash
# Compresser avec xz
xz -k test.txt
# Crée test.txt.xz

# Décompresser
unxz test.txt.xz
# Ou
xz -d test.txt.xz

# Comparer tous
gzip -k test.txt
bzip2 -k test.txt
xz -k test.txt

ls -lh test.txt*
```

---

**Test 6 : Voir le contenu sans décompresser**

```bash
# Créer fichier compressé
echo -e "Line 1\nLine 2\nLine 3" | gzip > lines.txt.gz

# Voir le contenu
zcat lines.txt.gz
# Line 1
# Line 2
# Line 3

# Pour bzip2
bzcat lines.txt.bz2

# Pour xz
xzcat lines.txt.xz

# Avec less
zless lines.txt.gz
bzless lines.txt.bz2
xzless lines.txt.xz

# Avec grep
zgrep "Line 2" lines.txt.gz
```

---

**Commandes de compression/décompression :**

| Format | Compresser | Décompresser | Cat | Less | Grep |
|--------|------------|--------------|-----|------|------|
| **gzip** | `gzip` | `gunzip` / `gzip -d` | `zcat` | `zless` | `zgrep` |
| **bzip2** | `bzip2` | `bunzip2` / `bzip2 -d` | `bzcat` | `bzless` | `bzgrep` |
| **xz** | `xz` | `unxz` / `xz -d` | `xzcat` | `xzless` | `xzgrep` |

---

### ÉTAPE 6 : Archives ZIP

**ZIP = Format multi-plateforme (Windows, Linux, Mac)**

---

**Test 1 : Créer une archive zip**

```bash
# Installer zip/unzip si nécessaire
sudo apt install zip unzip

# Créer archive
zip -r project.zip project/

# Option -r : recursive

# Vérifier
ls -lh project.zip
```

---

**Test 2 : Lister le contenu**

```bash
# Lister
unzip -l project.zip

# Ou avec plus de détails
unzip -v project.zip
```

---

**Test 3 : Extraire**

```bash
# Extraire tout
unzip project.zip

# Extraire dans un dossier spécifique
unzip project.zip -d extracted/

# Extraire seulement certains fichiers
unzip project.zip "project/src/*"

# Extraire sans écraser
unzip -n project.zip
```

---

**Test 4 : Tester l'intégrité**

```bash
# Tester sans extraire
unzip -t project.zip

# Résultat :
# testing: project/                 OK
# testing: project/src/             OK
# testing: project/src/main.py      OK
# ...
# No errors detected in compressed data of project.zip.
```

---

**Test 5 : Créer archive protégée par mot de passe**

```bash
# Créer avec mot de passe
zip -r -e project-secure.zip project/
# Enter password: ********
# Verify password: ********

# Extraire (demande le mot de passe)
unzip project-secure.zip
# [project-secure.zip] project/src/main.py password:
```

---

**Test 6 : Niveau de compression**

```bash
# Compression rapide (0 = store, pas de compression)
zip -r -0 project-store.zip project/

# Compression par défaut (6)
zip -r project.zip project/

# Compression maximale (9)
zip -r -9 project-max.zip project/

# Comparer
ls -lh project*.zip
```

---

**Test 7 : Mettre à jour une archive**

```bash
# Créer archive
zip -r project.zip project/

# Modifier un fichier
echo "Updated" >> project/src/main.py

# Mettre à jour seulement les fichiers modifiés
zip -r -u project.zip project/

# Option -u : update (met à jour seulement si plus récent)
```

---

**Test 8 : Exclure des fichiers**

```bash
# Exclure par pattern
zip -r project.zip project/ -x "*.bin" "*/data/*"

# Vérifier
unzip -l project.zip | grep -E "\.bin|data"
```

---

### ÉTAPE 7 : Cas d'usage professionnels

**Cas 1 : Backup quotidien de dossier web**

```bash
#!/bin/bash
# backup-web.sh

WEB_DIR="/var/www/html"
BACKUP_DIR="/backups/web"
DATE=$(date +%Y%m%d)
BACKUP_FILE="web-backup-$DATE.tar.gz"

# Créer dossier backup si nécessaire
mkdir -p "$BACKUP_DIR"

# Créer backup
echo "[SYNC] Création backup de $WEB_DIR..."
tar -czf "$BACKUP_DIR/$BACKUP_FILE" -C /var/www html/

# Vérifier
if [ $? -eq 0 ]; then
    SIZE=$(du -h "$BACKUP_DIR/$BACKUP_FILE" | cut -f1)
    echo "[OK] Backup créé : $BACKUP_FILE ($SIZE)"
else
    echo "[X] Erreur lors du backup"
    exit 1
fi

# Supprimer les backups de plus de 7 jours
find "$BACKUP_DIR" -name "web-backup-*.tar.gz" -mtime +7 -delete
echo "[SUPPRIMER]  Anciens backups supprimés"
```

---

**Cas 2 : Archiver et compresser les logs**

```bash
#!/bin/bash
# compress-logs.sh

LOG_DIR="/var/log"
ARCHIVE_DIR="/var/log/archive"

# Créer dossier archive
mkdir -p "$ARCHIVE_DIR"

# Trouver logs de plus de 7 jours
find "$LOG_DIR" -name "*.log" -mtime +7 -type f | while read logfile; do
    # Nom du fichier sans chemin
    filename=$(basename "$logfile")
    
    # Compresser
    gzip -9 "$logfile"
    
    # Déplacer vers archive
    mv "${logfile}.gz" "$ARCHIVE_DIR/"
    
    echo "[OK] Archivé : $filename"
done

# Supprimer les logs compressés de plus de 30 jours
find "$ARCHIVE_DIR" -name "*.log.gz" -mtime +30 -delete
echo "[SUPPRIMER]  Anciens logs supprimés (> 30 jours)"
```

---

**Cas 3 : Backup incrémental avec tar**

```bash
#!/bin/bash
# incremental-backup.sh

SOURCE="/home/user/documents"
BACKUP_DIR="/backups/documents"
SNAPSHOT_FILE="$BACKUP_DIR/snapshot.file"

mkdir -p "$BACKUP_DIR"

# Backup complet le dimanche
if [ $(date +%u) -eq 7 ]; then
    # Backup complet
    BACKUP_FILE="full-backup-$(date +%Y%m%d).tar.gz"
    
    tar --create \
        --gzip \
        --file="$BACKUP_DIR/$BACKUP_FILE" \
        --listed-incremental="$SNAPSHOT_FILE" \
        "$SOURCE"
    
    echo "[OK] Backup complet : $BACKUP_FILE"
else
    # Backup incrémental
    BACKUP_FILE="incr-backup-$(date +%Y%m%d).tar.gz"
    
    tar --create \
        --gzip \
        --file="$BACKUP_DIR/$BACKUP_FILE" \
        --listed-incremental="$SNAPSHOT_FILE" \
        "$SOURCE"
    
    echo "[OK] Backup incrémental : $BACKUP_FILE"
fi
```

---

**Cas 4 : Distribution de code source**

```bash
#!/bin/bash
# create-release.sh

PROJECT_NAME="myapp"
VERSION="1.0.0"
RELEASE_DIR="releases"

mkdir -p "$RELEASE_DIR"

# Créer archive tar.gz (Linux standard)
tar --exclude='.git' \
    --exclude='*.pyc' \
    --exclude='__pycache__' \
    --exclude='node_modules' \
    -czf "$RELEASE_DIR/${PROJECT_NAME}-${VERSION}.tar.gz" \
    .

# Créer archive zip (multi-plateforme)
zip -r "$RELEASE_DIR/${PROJECT_NAME}-${VERSION}.zip" . \
    -x "*.git*" "*.pyc" "*__pycache__*" "*node_modules*"

# Créer checksums
cd "$RELEASE_DIR"
sha256sum *.tar.gz *.zip > SHA256SUMS

echo "[OK] Releases créées :"
ls -lh "$RELEASE_DIR"
cat "$RELEASE_DIR/SHA256SUMS"
```

---

**Cas 5 : Backup de base de données**

```bash
#!/bin/bash
# backup-database.sh

DB_NAME="myapp_db"
DB_USER="backup_user"
BACKUP_DIR="/backups/database"
DATE=$(date +%Y%m%d-%H%M%S)

mkdir -p "$BACKUP_DIR"

# Dump de la base
mysqldump -u "$DB_USER" -p "$DB_NAME" > "/tmp/${DB_NAME}-${DATE}.sql"

# Compresser
gzip -9 "/tmp/${DB_NAME}-${DATE}.sql"

# Déplacer vers backup
mv "/tmp/${DB_NAME}-${DATE}.sql.gz" "$BACKUP_DIR/"

echo "[OK] Backup DB créé : ${DB_NAME}-${DATE}.sql.gz"

# Garder seulement les 30 derniers backups
cd "$BACKUP_DIR"
ls -t ${DB_NAME}-*.sql.gz | tail -n +31 | xargs -r rm

echo "[SUPPRIMER]  Anciens backups nettoyés"
```

---

**Cas 6 : Extraire et patcher**

```bash
#!/bin/bash
# apply-patch.sh

ARCHIVE="app-v1.0.tar.gz"
PATCH_FILE="update-v1.1.patch"
INSTALL_DIR="/opt/myapp"

# Extraire dans temporaire
TEMP_DIR=$(mktemp -d)
tar -xzf "$ARCHIVE" -C "$TEMP_DIR"

# Appliquer le patch
cd "$TEMP_DIR"
patch -p1 < "$PATCH_FILE"

# Créer nouvelle archive
tar -czf "app-v1.1.tar.gz" .

# Déployer
sudo tar -xzf "app-v1.1.tar.gz" -C "$INSTALL_DIR"

# Nettoyer
rm -rf "$TEMP_DIR"

echo "[OK] Patch appliqué et déployé"
```

---

### ÉTAPE 8 : Bonnes pratiques

**1. Choisir le bon format**

```bash
# Backups locaux rapides -> tar.gz
tar -czf backup.tar.gz /data/

# Backups à long terme -> tar.xz
tar -cJf archive.tar.xz /project/

# Distribution multi-plateforme -> zip
zip -r release.zip project/

# Logs quotidiens -> gzip
gzip -9 /var/log/app.log
```

---

**2. Toujours tester les archives**

```bash
# Pour tar
tar -tzf backup.tar.gz > /dev/null && echo "[OK] OK" || echo "[X] Corrompu"

# Pour zip
unzip -t backup.zip && echo "[OK] OK" || echo "[X] Corrompu"

# Vérifier intégrité avec checksum
sha256sum backup.tar.gz > backup.tar.gz.sha256
sha256sum -c backup.tar.gz.sha256
```

---

**3. Exclure les fichiers inutiles**

```bash
# Ne pas archiver :
# - Fichiers temporaires
# - Cache
# - node_modules, __pycache__
# - .git

tar -czf project.tar.gz project/ \
    --exclude='*.tmp' \
    --exclude='*~' \
    --exclude='.git' \
    --exclude='node_modules' \
    --exclude='__pycache__' \
    --exclude='*.pyc'
```

---

**4. Préserver les métadonnées**

```bash
# Préserver permissions, ownership, timestamps
tar -cpzf backup.tar.gz /data/

# Options :
# -p : preserve permissions
# -c : create
# -z : gzip
# -f : file
```

---

**5. Automatiser avec cron**

```bash
# Éditer crontab
crontab -e

# Backup quotidien à 2h du matin
0 2 * * * /usr/local/bin/backup-web.sh >> /var/log/backup.log 2>&1

# Compression des logs tous les dimanches à 3h
0 3 * * 0 /usr/local/bin/compress-logs.sh >> /var/log/compress.log 2>&1
```

---

**6. Sécuriser les archives**

```bash
# Chiffrer avec GPG
tar -czf - /data/ | gpg -c > backup.tar.gz.gpg

# Déchiffrer
gpg -d backup.tar.gz.gpg | tar -xzf -

# Ou avec openssl
tar -czf - /data/ | openssl enc -aes-256-cbc -salt -out backup.tar.gz.enc

# Déchiffrer
openssl enc -d -aes-256-cbc -in backup.tar.gz.enc | tar -xzf -
```

---

### ÉTAPE 9 : Outils avancés

**1. rsync avec compression**

```bash
# Synchronisation avec compression à la volée
rsync -avz /source/ /destination/

# Options :
# -a : archive mode
# -v : verbose
# -z : compress during transfer
```

---

**2. pigz - gzip parallèle (multi-thread)**

```bash
# Installer
sudo apt install pigz

# Utiliser tous les CPU cores
tar -c project/ | pigz > project.tar.gz

# Avec tar directement
tar -I pigz -cf project.tar.gz project/

# Beaucoup plus rapide sur machines multi-cores !
```

---

**3. pv - Barre de progression**

```bash
# Installer
sudo apt install pv

# Avec tar
tar -czf - project/ | pv > project.tar.gz

# Avec extraction
pv project.tar.gz | tar -xzf -

# Affiche vitesse et temps restant
```

---

**4. 7zip - Compression maximale**

```bash
# Installer
sudo apt install p7zip-full

# Créer archive
7z a project.7z project/

# Avec compression maximale
7z a -mx=9 project.7z project/

# Extraire
7z x project.7z

# Lister
7z l project.7z
```

---

**5. zstd - Compression moderne (Facebook)**

```bash
# Installer
sudo apt install zstd

# Compresser
tar -I zstd -cf project.tar.zst project/

# Ou
tar -cf - project/ | zstd > project.tar.zst

# Extraction
tar -I zstd -xf project.tar.zst

# Balance vitesse/compression excellente !
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : tar fonctionne**

```bash
# Créer dossier test
mkdir test-dir
echo "test" > test-dir/file.txt

# Archiver
tar -cf test.tar test-dir/

# Vérifier
if tar -tf test.tar | grep -q "file.txt"; then
    echo "[OK] Test 1 OK : tar fonctionne"
else
    echo "[X] Test 1 FAIL"
fi

rm -rf test-dir test.tar
```

---

**Test 2 : Compression gzip fonctionne**

```bash
# Créer fichier
echo "Test compression" > test.txt
ORIG_SIZE=$(stat -c%s test.txt)

# Compresser
gzip test.txt

# Vérifier que c'est plus petit
GZ_SIZE=$(stat -c%s test.txt.gz)

if [ "$GZ_SIZE" -lt "$ORIG_SIZE" ]; then
    echo "[OK] Test 2 OK : gzip compresse"
else
    echo "[X] Test 2 FAIL"
fi

rm test.txt.gz
```

---

**Test 3 : tar.gz fonctionne**

```bash
# Créer et archiver
mkdir test-dir
echo "content" > test-dir/file.txt
tar -czf test.tar.gz test-dir/

# Extraire
rm -rf test-dir
tar -xzf test.tar.gz

# Vérifier
if [ -f "test-dir/file.txt" ]; then
    echo "[OK] Test 3 OK : tar.gz fonctionne"
else
    echo "[X] Test 3 FAIL"
fi

rm -rf test-dir test.tar.gz
```

---

**Test 4 : zip fonctionne**

```bash
# Créer et zipper
mkdir test-dir
echo "content" > test-dir/file.txt
zip -r test.zip test-dir/ > /dev/null

# Extraire
rm -rf test-dir
unzip -q test.zip

# Vérifier
if [ -f "test-dir/file.txt" ]; then
    echo "[OK] Test 4 OK : zip fonctionne"
else
    echo "[X] Test 4 FAIL"
fi

rm -rf test-dir test.zip
```

---

**Test 5 : Comparaison des taux de compression**

```bash
# Créer fichier répétitif (compresse bien)
for i in {1..1000}; do
    echo "This is line number $i with repetitive content"
done > compressible.txt

ORIG=$(stat -c%s compressible.txt)

# Compresser avec différents algos
gzip -k compressible.txt
bzip2 -k compressible.txt
xz -k compressible.txt

# Calculer ratios
GZ_RATIO=$(echo "scale=2; $(stat -c%s compressible.txt.gz) * 100 / $ORIG" | bc)
BZ2_RATIO=$(echo "scale=2; $(stat -c%s compressible.txt.bz2) * 100 / $ORIG" | bc)
XZ_RATIO=$(echo "scale=2; $(stat -c%s compressible.txt.xz) * 100 / $ORIG" | bc)

echo "Ratios de compression :"
echo "  gzip : ${GZ_RATIO}%"
echo "  bzip2: ${BZ2_RATIO}%"
echo "  xz   : ${XZ_RATIO}%"

# xz devrait être le meilleur
if (( $(echo "$XZ_RATIO < $GZ_RATIO" | bc -l) )); then
    echo "[OK] Test 5 OK : xz compresse mieux que gzip"
fi

rm compressible.txt*
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Archive vs Compression**
- **Archive** (tar) = Regrouper fichiers
- **Compression** (gzip/bzip2/xz) = Réduire taille
- **Combiné** (tar.gz) = Le meilleur des deux

**2. Commandes tar essentielles**

| Action | Commande | Description |
|--------|----------|-------------|
| Créer | `tar -cf file.tar dir/` | Archive simple |
| Créer gz | `tar -czf file.tar.gz dir/` | Archive + gzip |
| Créer bz2 | `tar -cjf file.tar.bz2 dir/` | Archive + bzip2 |
| Créer xz | `tar -cJf file.tar.xz dir/` | Archive + xz |
| Lister | `tar -tf file.tar` | Voir contenu |
| Extraire | `tar -xf file.tar` | Extraire tout |

**3. Algorithmes de compression**

| Algo | Vitesse | Taux | Usage |
|------|---------|------|-------|
| gzip | *** | ** | Standard, rapide |
| bzip2 | ** | *** | Bon compromis |
| xz | * | **** | Maximum, lent |

**4. Format selon l'usage**
- **Backups locaux** : `.tar.gz`
- **Archivage long terme** : `.tar.xz`
- **Multi-plateforme** : `.zip`
- **Logs** : `.gz` (fichiers individuels)

**5. Options importantes**
- `-v` : Verbose (voir progression)
- `-k` : Keep (garder original)
- `-p` : Preserve (permissions)
- `-C` : Change dir (extraire ailleurs)
- `--exclude` : Exclure fichiers

**6. Bonnes pratiques**
- [OK] Toujours tester les archives
- [OK] Créer des checksums
- [OK] Exclure fichiers inutiles
- [OK] Automatiser avec scripts/cron
- [OK] Chiffrer les données sensibles

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Compression différentielle (rsync)**

```bash
# Backup incrémental efficace
rsync -avz --delete /source/ /backup/
```

---

**2. Déduplication (borg)**

```bash
# Installer BorgBackup
sudo apt install borgbackup

# Initialiser repo
borg init --encryption=repokey /path/to/repo

# Créer backup
borg create /path/to/repo::backup-$(date +%Y%m%d) /data/

# Lister archives
borg list /path/to/repo

# Restaurer
borg extract /path/to/repo::backup-20241230
```

---

**3. Split de grosses archives**

```bash
# Créer archive et split en morceaux de 100MB
tar -czf - bigdir/ | split -b 100M - bigarchive.tar.gz.part

# Reconstituer
cat bigarchive.tar.gz.part* | tar -xzf -
```

---

**4. Compression réseau optimisée**

```bash
# Transfer avec compression à la volée
tar -czf - /source/ | ssh user@remote 'tar -xzf - -C /destination/'

# Avec pv pour progression
tar -cf - /source/ | pv | ssh user@remote 'tar -xf - -C /destination/'
```

---

**5. Archives montables (squashfs)**

```bash
# Créer filesystem compressé read-only
mksquashfs /source/ archive.sqsh

# Monter sans extraire
sudo mount -t squashfs archive.sqsh /mnt/

# Utiliser les fichiers
ls /mnt/

# Démonter
sudo umount /mnt/
```

---

## [COURS] CONCLUSION DE L'EXERCICE 6.1

**[OK] Félicitations ! Tu maîtrises maintenant les archives et la compression !**

**Ce que tu as appris :**
- Différence archive/compression
- Créer des archives tar
- Compresser avec gzip/bzip2/xz
- Utiliser tar avec compression (tar.gz, tar.bz2, tar.xz)
- Gérer les archives zip
- Extraire tout ou partie
- Cas d'usage professionnels (backups, logs, releases)
- Scripts d'automatisation
- Bonnes pratiques

**Compétences acquises :**
- [OK] Archivage et compression (niveau avancé)
- [OK] Gestion des backups
- [OK] Optimisation espace disque
- [OK] Automatisation
- [OK] Distribution de logiciels

**Temps moyen de réalisation :** 1h30 - 2h

---

C'était un exercice complet sur les archives et la compression ! 

Tu veux continuer avec la **Section 7 : Variables & Environnement** ou tu préfères un récapitulatif de tout ce qu'on a vu jusqu'à présent ?

# [DOCS] SECTION 7 : VARIABLES & ENVIRONNEMENT

# [VERT] EXERCICE 7.1 : VARIABLES BASH ET ENVIRONNEMENT (DÉBUTANT/INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et développeur de scripts et tu dois quotidiennement :
- Écrire des scripts bash automatisés
- Configurer l'environnement des utilisateurs
- Gérer les variables d'environnement pour les applications
- Personnaliser le shell (prompt, aliases, fonctions)
- Débugger des problèmes de configuration
- Gérer les PATH et bibliothèques
- Configurer des applications via variables d'environnement

**Problématique :** Les variables sont essentielles en scripting et pour la configuration système. Il faut comprendre les variables locales vs environnement, les fichiers de configuration du shell, et comment les applications utilisent les variables d'environnement.

### Cahier des charges

- Comprendre les variables bash (locales et d'environnement)
- Déclarer et utiliser des variables
- Exporter des variables vers l'environnement
- Comprendre les variables spéciales ($?, $!, $$, etc.)
- Configurer le PATH
- Gérer les fichiers de configuration (.bashrc, .profile, etc.)
- Créer des aliases et fonctions
- Personnaliser le prompt (PS1)
- Utiliser les variables pour la configuration d'applications

### Contraintes

- Shell : bash (Bourne Again Shell)
- Fichiers : `.bashrc`, `.bash_profile`, `.profile`, `/etc/environment`
- Commandes : `export`, `env`, `set`, `declare`
- Temps estimé : 2-2h30

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Créer et utiliser des variables
- [OK] Différencier variables locales et d'environnement
- [OK] Exporter des variables
- [OK] Utiliser les variables spéciales
- [OK] Manipuler des chaînes et tableaux
- [OK] Configurer le PATH
- [OK] Personnaliser le shell
- [OK] Créer des aliases et fonctions
- [OK] Comprendre les fichiers de config
- [OK] Débugger les problèmes de variables

---

## [DOCS] PRÉREQUIS

- Sections 1-6 complètes
- Notions de base du terminal
- Éditeur de texte (vim, nano)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre les variables bash

**Variable = Conteneur pour stocker une valeur**

**Deux types principaux :**

1. **Variables locales** : Disponibles seulement dans le shell courant
2. **Variables d'environnement** : Disponibles pour le shell ET ses processus enfants

---

**Créer l'environnement de test :**

```bash
# Créer le dossier de travail
mkdir -p ~/variables-exercise
cd ~/variables-exercise
```

---

**Test 1 : Variables locales basiques**

```bash
# Déclarer une variable (PAS d'espace autour du =)
NAME="Alice"
AGE=30
CITY="Paris"

# Afficher (avec $)
echo $NAME
# Alice

echo "Nom: $NAME, Age: $AGE, Ville: $CITY"
# Nom: Alice, Age: 30, Ville: Paris

# Avec accolades (plus clair, recommandé)
echo "Nom: ${NAME}, Age: ${AGE}"
```

**[ATTENTION] IMPORTANT : Pas d'espace autour du `=` !**

```bash
# [X] FAUX
NAME = "Alice"    # Erreur de syntaxe

# [OK] CORRECT
NAME="Alice"
```

---

**Test 2 : Types de valeurs**

```bash
# Chaînes (strings)
TEXT="Hello World"
PATH_VAR="/usr/local/bin"

# Nombres (bash traite tout comme string, mais peut faire des calculs)
NUMBER=42
RESULT=$((NUMBER + 8))
echo $RESULT
# 50

# Avec espaces (guillemets obligatoires)
MESSAGE="Ceci est un message avec espaces"

# Sans guillemets (échoue)
MESSAGE=Ceci est un message    # Erreur !
```

---

**Test 3 : Lire une variable**

```bash
# Lecture interactive
read -p "Entrez votre nom : " USERNAME
echo "Bonjour, $USERNAME !"

# Lecture silencieuse (mot de passe)
read -s -p "Entrez mot de passe : " PASSWORD
echo ""
echo "Mot de passe capturé (${#PASSWORD} caractères)"

# Lecture avec timeout
read -t 5 -p "Vous avez 5 secondes : " ANSWER
```

---

**Test 4 : Variables vides et non définies**

```bash
# Variable vide
EMPTY=""
echo "Variable vide : '${EMPTY}'"

# Variable non définie
echo "Variable non définie : '${UNDEFINED}'"
# Affiche rien

# Tester si définie
if [ -z "$EMPTY" ]; then
    echo "EMPTY est vide"
fi

if [ -z "$UNDEFINED" ]; then
    echo "UNDEFINED est vide ou non définie"
fi

# Avec valeur par défaut
echo "${UNDEFINED:-valeur_par_defaut}"
# valeur_par_defaut
```

---

**Test 5 : Portée des variables**

```bash
# Variable locale au shell
LOCAL_VAR="Je suis locale"

# Lancer un sous-shell
bash -c 'echo "Dans sous-shell: $LOCAL_VAR"'
# Affiche rien -> variable pas visible

# La variable n'existe que dans le shell parent
echo "Dans shell parent: $LOCAL_VAR"
# Je suis locale
```

---

### ÉTAPE 2 : Variables d'environnement

**Variables d'environnement = Visibles par tous les processus enfants**

---

**Test 1 : Exporter une variable**

```bash
# Variable locale
LOCAL="Local only"

# Variable d'environnement (export)
export GLOBAL="Global variable"

# Vérifier dans sous-shell
bash -c 'echo "LOCAL: $LOCAL"'
# Affiche rien

bash -c 'echo "GLOBAL: $GLOBAL"'
# Global variable
```

**`export` = Rend la variable disponible pour les processus enfants**

---

**Test 2 : Voir toutes les variables d'environnement**

```bash
# Méthode 1 : env
env

# Méthode 2 : printenv
printenv

# Variable spécifique
printenv PATH
printenv HOME
printenv USER

# Méthode 3 : echo
echo $PATH
echo $HOME
```

---

**Test 3 : Variables d'environnement importantes**

```bash
# Afficher les variables système courantes
cat << 'EOF'
USER     : $USER
HOME     : $HOME
SHELL    : $SHELL
PATH     : $PATH
PWD      : $PWD
OLDPWD   : $OLDPWD
HOSTNAME : $HOSTNAME
LANG     : $LANG
TERM     : $TERM
EOF

# Avec valeurs
echo "USER     : $USER"
echo "HOME     : $HOME"
echo "SHELL    : $SHELL"
echo "PATH     : $PATH"
echo "PWD      : $PWD"
echo "HOSTNAME : $HOSTNAME"
```

**Variables système importantes :**

| Variable | Description | Exemple |
|----------|-------------|---------|
| **USER** | Nom utilisateur | `alice` |
| **HOME** | Dossier home | `/home/alice` |
| **SHELL** | Shell par défaut | `/bin/bash` |
| **PATH** | Chemins exécutables | `/usr/bin:/bin:/usr/local/bin` |
| **PWD** | Dossier courant | `/home/alice/project` |
| **OLDPWD** | Dossier précédent | `/home/alice` |
| **HOSTNAME** | Nom de la machine | `server01` |
| **LANG** | Langue système | `en_US.UTF-8` |
| **TERM** | Type de terminal | `xterm-256color` |
| **EDITOR** | Éditeur par défaut | `vim` |

---

**Test 4 : Différence entre set, env et export**

```bash
# set : Affiche TOUTES les variables (locales + environnement + fonctions)
set | head -20

# env : Affiche seulement les variables d'environnement
env | head -20

# export : Déclare ET exporte
export MY_VAR="value"

# Ou en deux étapes
MY_VAR="value"
export MY_VAR
```

---

**Test 5 : Supprimer une variable**

```bash
# Créer variable
TEST_VAR="test"
echo $TEST_VAR
# test

# Supprimer (unset)
unset TEST_VAR
echo $TEST_VAR
# Affiche rien

# Supprimer variable d'environnement
export TEST_ENV="environnement"
unset TEST_ENV
```

---

### ÉTAPE 3 : Variables spéciales bash

**Variables automatiques créées par bash**

---

**Test 1 : Variables de position ($1, $2, etc.)**

```bash
# Créer un script
cat > test-args.sh << 'EOF'
#!/bin/bash

echo "Nom du script : $0"
echo "Premier argument : $1"
echo "Deuxième argument : $2"
echo "Troisième argument : $3"
echo "Tous les arguments : $@"
echo "Tous les arguments (string) : $*"
echo "Nombre d'arguments : $#"
EOF

chmod +x test-args.sh

# Tester
./test-args.sh arg1 arg2 arg3
```

**Résultat :**

```
Nom du script : ./test-args.sh
Premier argument : arg1
Deuxième argument : arg2
Troisième argument : arg3
Tous les arguments : arg1 arg2 arg3
Tous les arguments (string) : arg1 arg2 arg3
Nombre d'arguments : 3
```

**Variables de position :**

| Variable | Description |
|----------|-------------|
| `$0` | Nom du script |
| `$1, $2, $3...` | Arguments (1er, 2ème, 3ème...) |
| `$#` | Nombre d'arguments |
| `$@` | Tous les arguments (tableau) |
| `$*` | Tous les arguments (string) |

---

**Test 2 : Code de retour ($?)**

```bash
# Commande qui réussit
ls /home
echo "Code retour: $?"
# Code retour: 0

# Commande qui échoue
ls /nonexistent 2>/dev/null
echo "Code retour: $?"
# Code retour: 2

# Dans un script
cat > test-exit.sh << 'EOF'
#!/bin/bash

if ls "$1" > /dev/null 2>&1; then
    echo "[OK] Le fichier/dossier existe"
    exit 0
else
    echo "[X] Le fichier/dossier n'existe pas"
    exit 1
fi
EOF

chmod +x test-exit.sh

# Tester
./test-exit.sh /home
echo "Exit code: $?"
# Exit code: 0

./test-exit.sh /nonexistent
echo "Exit code: $?"
# Exit code: 1
```

**$? = Code de retour de la dernière commande**
- `0` = Succès
- `!= 0` = Erreur

---

**Test 3 : PID du shell ($$) et processus en background ($!)**

```bash
# PID du shell courant
echo "PID du shell : $$"

# Lancer processus en background
sleep 30 &
echo "PID du dernier background : $!"

# Créer un fichier PID
cat > create-pidfile.sh << 'EOF'
#!/bin/bash
PIDFILE="/tmp/myapp.pid"
echo $$ > "$PIDFILE"
echo "PID $$ enregistré dans $PIDFILE"
sleep 60
rm "$PIDFILE"
EOF

chmod +x create-pidfile.sh
./create-pidfile.sh &
cat /tmp/myapp.pid
```

---

**Test 4 : Autres variables spéciales**

```bash
# Créer script de démonstration
cat > special-vars.sh << 'EOF'
#!/bin/bash

echo "=== Variables spéciales ==="
echo "\$0 (nom script)    : $0"
echo "\$$ (PID)           : $$"
echo "\$PPID (PID parent) : $PPID"
echo "\$BASH_VERSION      : $BASH_VERSION"
echo "\$BASH_PID          : $BASHPID"
echo "\$HOSTNAME          : $HOSTNAME"
echo "\$RANDOM            : $RANDOM"
echo "\$LINENO (ligne)    : $LINENO"
echo "\$SECONDS (uptime)  : $SECONDS seconds"

# Attendre 2 secondes
sleep 2
echo "\$SECONDS après 2s  : $SECONDS seconds"
EOF

chmod +x special-vars.sh
./special-vars.sh
```

**Variables spéciales utiles :**

| Variable | Description |
|----------|-------------|
| `$$` | PID du shell courant |
| `$!` | PID du dernier processus background |
| `$?` | Code retour dernière commande |
| `$_` | Dernier argument de la commande précédente |
| `$PPID` | PID du processus parent |
| `$RANDOM` | Nombre aléatoire (0-32767) |
| `$LINENO` | Numéro de ligne dans le script |
| `$SECONDS` | Secondes depuis démarrage du script |

---

### ÉTAPE 4 : Manipulation de chaînes

**Bash peut manipuler les chaînes directement**

---

**Test 1 : Longueur d'une chaîne**

```bash
STRING="Hello World"

# Longueur
echo ${#STRING}
# 11
```

---

**Test 2 : Extraction de sous-chaînes**

```bash
STRING="Hello World"

# Extraire depuis position (0-indexed)
echo ${STRING:0:5}
# Hello

echo ${STRING:6:5}
# World

# Depuis la fin
echo ${STRING: -5}
# World
```

---

**Test 3 : Remplacement**

```bash
STRING="Hello World"

# Remplacer première occurrence
echo ${STRING/o/O}
# HellO World

# Remplacer toutes les occurrences
echo ${STRING//o/O}
# HellO WOrld

# Supprimer
echo ${STRING//o/}
# Hell Wrld
```

---

**Test 4 : Suppression de patterns**

```bash
FILENAME="document.tar.gz"

# Supprimer depuis la fin (shortest match)
echo ${FILENAME%.*}
# document.tar

# Supprimer depuis la fin (longest match)
echo ${FILENAME%%.*}
# document

# Supprimer depuis le début (shortest match)
PATH="/usr/local/bin/script.sh"
echo ${PATH#*/}
# usr/local/bin/script.sh

# Supprimer depuis le début (longest match)
echo ${PATH##*/}
# script.sh
```

**Mnémotechnique :**
- `%` = Depuis la FIN (comme touche % en haut du clavier)
- `#` = Depuis le DÉBUT (comme touche # en bas)
- Un symbole = shortest match
- Deux symboles = longest match

---

**Test 5 : Majuscules/minuscules**

```bash
TEXT="Hello World"

# Tout en majuscules
echo ${TEXT^^}
# HELLO WORLD

# Tout en minuscules
echo ${TEXT,,}
# hello world

# Première lettre en majuscule
echo ${TEXT^}
# Hello World

# Première lettre en minuscule
echo ${TEXT,}
# hello World
```

---

**Test 6 : Valeurs par défaut**

```bash
# Si variable vide ou non définie, utiliser valeur par défaut
echo ${UNDEFINED:-"valeur par défaut"}
# valeur par défaut

# Assigner si non défini
echo ${NEW_VAR:="assigned value"}
# assigned value
echo $NEW_VAR
# assigned value

# Erreur si non défini
# echo ${REQUIRED:?"Cette variable est requise"}
# bash: REQUIRED: Cette variable est requise
```

---

### ÉTAPE 5 : Tableaux

**Bash supporte les tableaux (arrays)**

---

**Test 1 : Tableaux indexés**

```bash
# Déclarer un tableau
FRUITS=("pomme" "banane" "orange")

# Accéder aux éléments (0-indexed)
echo ${FRUITS[0]}
# pomme

echo ${FRUITS[1]}
# banane

# Tous les éléments
echo ${FRUITS[@]}
# pomme banane orange

# Nombre d'éléments
echo ${#FRUITS[@]}
# 3

# Ajouter un élément
FRUITS+=("fraise")
echo ${FRUITS[@]}
# pomme banane orange fraise
```

---

**Test 2 : Boucler sur un tableau**

```bash
# Méthode 1
for fruit in "${FRUITS[@]}"; do
    echo "Fruit : $fruit"
done

# Méthode 2 : avec index
for i in "${!FRUITS[@]}"; do
    echo "Index $i : ${FRUITS[$i]}"
done
```

---

**Test 3 : Tableaux associatifs (dictionnaires)**

```bash
# Déclarer (bash 4+)
declare -A USERS

# Assigner
USERS[alice]="Admin"
USERS[bob]="Developer"
USERS[charlie]="Designer"

# Accéder
echo ${USERS[alice]}
# Admin

# Toutes les clés
echo ${!USERS[@]}
# alice bob charlie

# Toutes les valeurs
echo ${USERS[@]}
# Admin Developer Designer

# Boucler
for user in "${!USERS[@]}"; do
    echo "$user : ${USERS[$user]}"
done
```

---

### ÉTAPE 6 : La variable PATH

**PATH = Liste de dossiers où chercher les exécutables**

---

**Test 1 : Voir le PATH**

```bash
# Afficher PATH
echo $PATH

# Plus lisible (un dossier par ligne)
echo $PATH | tr ':' '\n'
```

**Résultat typique :**

```
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
```

**Comment ça marche :**

```
Quand tu tapes : ls
Bash cherche dans l'ordre :
1. /usr/local/sbin/ls -> Pas trouvé
2. /usr/local/bin/ls -> Pas trouvé
3. /usr/sbin/ls -> Pas trouvé
4. /usr/bin/ls -> [OK] Trouvé !
5. Exécute /usr/bin/ls
```

---

**Test 2 : Ajouter un dossier au PATH**

```bash
# Créer un dossier pour nos scripts
mkdir -p ~/bin

# Créer un script test
cat > ~/bin/hello << 'EOF'
#!/bin/bash
echo "Hello from ~/bin/hello"
EOF

chmod +x ~/bin/hello

# Essayer de l'exécuter (échoue)
hello
# bash: hello: command not found

# Ajouter ~/bin au PATH (temporaire)
export PATH="$HOME/bin:$PATH"

# Maintenant ça marche
hello
# Hello from ~/bin/hello

# Vérifier le PATH
echo $PATH | tr ':' '\n' | head -5
```

**Syntaxe :** `export PATH="nouveau_dossier:$PATH"`
- Ajoute `nouveau_dossier` AU DÉBUT
- `:` = Séparateur
- `$PATH` = PATH existant

---

**Test 3 : Ordre dans le PATH**

```bash
# Créer deux versions du même script
mkdir -p ~/bin-custom
cat > ~/bin-custom/mycommand << 'EOF'
#!/bin/bash
echo "Version 1 (bin-custom)"
EOF

mkdir -p ~/bin-other
cat > ~/bin-other/mycommand << 'EOF'
#!/bin/bash
echo "Version 2 (bin-other)"
EOF

chmod +x ~/bin-custom/mycommand
chmod +x ~/bin-other/mycommand

# Ajouter les deux au PATH (ordre important !)
export PATH="$HOME/bin-custom:$HOME/bin-other:$PATH"

# Quelle version s'exécute ?
mycommand
# Version 1 (bin-custom)

# La PREMIÈRE trouvée dans le PATH s'exécute
which mycommand
# /home/user/bin-custom/mycommand
```

---

**Test 4 : PATH permanent (.bashrc)**

```bash
# Ajouter à la fin de ~/.bashrc
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc

# Recharger
source ~/.bashrc

# Vérifier
echo $PATH | grep "$HOME/bin"
```

---

### ÉTAPE 7 : Fichiers de configuration du shell

**Plusieurs fichiers sont chargés au démarrage du shell**

---

**Fichiers de configuration :**

**Pour bash :**

| Fichier | Type | Quand chargé | Usage |
|---------|------|--------------|-------|
| `/etc/profile` | Système | Login shell | Config globale |
| `/etc/bash.bashrc` | Système | Tous les shells | Config globale bash |
| `~/.bash_profile` | User | Login shell | Config utilisateur (login) |
| `~/.bash_login` | User | Login shell | Alternative à .bash_profile |
| `~/.profile` | User | Login shell | Config POSIX (tous shells) |
| `~/.bashrc` | User | Interactive shell | Config bash interactive |
| `~/.bash_logout` | User | Logout | Exécuté à la déconnexion |

---

**Login shell vs Interactive shell :**

**Login shell** = Premier shell après connexion
- SSH
- Console (Ctrl+Alt+F1)
- `bash -l`

**Interactive shell** = Nouveau terminal dans une session
- Nouveau terminal dans GUI
- `bash` (sans -l)

---

**Test 1 : Identifier le type de shell**

```bash
# Vérifier si login shell
cat > check-shell.sh << 'EOF'
#!/bin/bash

if shopt -q login_shell; then
    echo "[OK] Login shell"
else
    echo "[X] Non-login shell"
fi

if [[ $- == *i* ]]; then
    echo "[OK] Interactive shell"
else
    echo "[X] Non-interactive shell"
fi
EOF

chmod +x check-shell.sh
./check-shell.sh
```

---

**Test 2 : Ordre de chargement**

```bash
# Créer des fichiers avec messages
echo 'echo "Loading ~/.bash_profile"' >> ~/.bash_profile
echo 'echo "Loading ~/.bashrc"' >> ~/.bashrc

# Login shell
bash -l
# Loading ~/.bash_profile

# Interactive shell
bash
# Loading ~/.bashrc
```

---

**Test 3 : Configuration recommandée**

```bash
# ~/.bash_profile (si existe) ou ~/.profile
# Charger ~/.bashrc
cat >> ~/.bash_profile << 'EOF'
# Charger ~/.bashrc si existe
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
EOF

# ~/.bashrc - Configuration principale
cat >> ~/.bashrc << 'EOF'

# ===== Configuration personnelle =====

# PATH
export PATH="$HOME/bin:$PATH"

# Variables d'environnement
export EDITOR=vim
export VISUAL=vim
export PAGER=less

# Historique
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoredups:erasedups

# Aliases (voir section suivante)
alias ll='ls -lah'
alias la='ls -A'
alias l='ls -CF'

# Prompt personnalisé (voir section suivante)
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

EOF
```

---

### ÉTAPE 8 : Aliases

**Alias = Raccourci pour une commande**

---

**Test 1 : Créer des aliases**

```bash
# Alias simple
alias ll='ls -lah'

# Utiliser
ll

# Alias avec options
alias grep='grep --color=auto'
alias df='df -h'
alias du='du -h'

# Alias avec sudo
alias update='sudo apt update && sudo apt upgrade'
```

---

**Test 2 : Voir tous les aliases**

```bash
# Lister tous
alias

# Voir un alias spécifique
alias ll
```

---

**Test 3 : Supprimer un alias**

```bash
# Créer
alias test='echo test'

# Supprimer
unalias test

# Supprimer tous
unalias -a
```

---

**Test 4 : Aliases utiles pour admin système**

```bash
# Ajouter à ~/.bashrc
cat >> ~/.bashrc << 'EOF'

# ===== Aliases =====

# Navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'

# ls amélioré
alias ll='ls -lah'
alias la='ls -A'
alias l='ls -CF'
alias lt='ls -ltrh'  # Trié par date

# grep avec couleur
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'

# Confirmation avant destruction
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# Disque
alias df='df -h'
alias du='du -h'
alias free='free -h'

# Processus
alias psg='ps aux | grep -v grep | grep -i -e VSZ -e'
alias ports='netstat -tulanp'

# Système
alias update='sudo apt update && sudo apt upgrade'
alias install='sudo apt install'
alias remove='sudo apt remove'

# Sécurité (ne pas logger les commandes sensibles)
alias history-off='set +o history'
alias history-on='set -o history'

# Git (si utilisé)
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline'

EOF

# Recharger
source ~/.bashrc
```

---

### ÉTAPE 9 : Fonctions bash

**Fonctions = Comme aliases mais plus puissantes (arguments, logique)**

---

**Test 1 : Fonction simple**

```bash
# Définir fonction
hello() {
    echo "Hello, $1!"
}

# Utiliser
hello Alice
# Hello, Alice!

hello Bob
# Hello, Bob!
```

---

**Test 2 : Fonction avec logique**

```bash
# Créer dossier et y aller
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Utiliser
mkcd test-dir
pwd
# /home/user/test-dir
```

---

**Test 3 : Fonctions utiles pour admin système**

```bash
# Ajouter à ~/.bashrc
cat >> ~/.bashrc << 'EOF'

# ===== Fonctions =====

# Extraire n'importe quelle archive
extract() {
    if [ -f "$1" ]; then
        case "$1" in
            *.tar.bz2)   tar xjf "$1"     ;;
            *.tar.gz)    tar xzf "$1"     ;;
            *.bz2)       bunzip2 "$1"     ;;
            *.rar)       unrar x "$1"     ;;
            *.gz)        gunzip "$1"      ;;
            *.tar)       tar xf "$1"      ;;
            *.tbz2)      tar xjf "$1"     ;;
            *.tgz)       tar xzf "$1"     ;;
            *.zip)       unzip "$1"       ;;
            *.Z)         uncompress "$1"  ;;
            *.7z)        7z x "$1"        ;;
            *)           echo "'$1' cannot be extracted" ;;
        esac
    else
        echo "'$1' is not a valid file"
    fi
}

# Backup rapide d'un fichier
backup() {
    cp "$1" "$1.backup-$(date +%Y%m%d-%H%M%S)"
}

# Chercher un processus
psgrep() {
    ps aux | grep -v grep | grep -i -e VSZ -e "$@"
}

# Créer et aller dans dossier
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Taille totale d'un dossier
dirsize() {
    du -sh "$1"
}

# Trouver les plus gros fichiers
bigfiles() {
    find . -type f -exec du -h {} \; | sort -rh | head -n ${1:-10}
}

EOF

source ~/.bashrc
```

---

**Test 4 : Fonction vs Alias - Quand utiliser quoi ?**

```bash
# Alias : Simple, pas d'arguments complexes
alias ll='ls -lah'

# Fonction : Arguments, logique, conditions
extract() {
    # Logique complexe avec if/case
}
```

**Règle :**
- **Alias** : Commande simple avec options fixes
- **Fonction** : Logique, arguments, conditions

---

### ÉTAPE 10 : Personnaliser le prompt (PS1)

**PS1 = Prompt String 1 (ce qui s'affiche avant ta commande)**

---

**Test 1 : Prompt basique**

```bash
# Voir PS1 actuel
echo $PS1

# Prompt simple
PS1="$ "
# $

# Avec nom utilisateur
PS1="\u$ "
# alice$

# Avec hostname
PS1="\u@\h$ "
# alice@server$

# Avec dossier courant
PS1="\u@\h:\w$ "
# alice@server:~/project$
```

---

**Test 2 : Codes spéciaux PS1**

| Code | Description | Exemple |
|------|-------------|---------|
| `\u` | Nom utilisateur | `alice` |
| `\h` | Hostname (court) | `server` |
| `\H` | Hostname (complet) | `server.example.com` |
| `\w` | Dossier courant (complet) | `~/project/src` |
| `\W` | Dossier courant (basename) | `src` |
| `\d` | Date | `Mon Dec 30` |
| `\t` | Heure (24h) | `10:30:45` |
| `\T` | Heure (12h) | `10:30:45` |
| `\@` | Heure (am/pm) | `10:30 AM` |
| `\$` | `#` si root, `$` sinon | `$` |
| `\n` | Nouvelle ligne | |
| `\[` | Début séquence non-imprimable | (pour couleurs) |
| `\]` | Fin séquence non-imprimable | (pour couleurs) |

---

**Test 3 : Prompt avec couleurs**

```bash
# Codes couleur ANSI
# Format : \[\033[CODEm\]

# Prompt vert
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# Explication :
# \[\033[01;32m\] = Vert gras (début)
# \u@\h = user@host
# \[\033[00m\] = Reset couleur
# : = Séparateur
# \[\033[01;34m\] = Bleu gras (début)
# \w = Dossier
# \[\033[00m\] = Reset
# \$ = $ ou #
```

**Codes couleur :**

| Code | Couleur |
|------|---------|
| 30 | Noir |
| 31 | Rouge |
| 32 | Vert |
| 33 | Jaune |
| 34 | Bleu |
| 35 | Magenta |
| 36 | Cyan |
| 37 | Blanc |
| 01 | Gras |
| 00 | Reset |

---

**Test 4 : Prompt avec Git branch**

```bash
# Fonction pour afficher la branche Git
git_branch() {
    git branch 2>/dev/null | grep '^*' | sed 's/* //'
}

# Prompt avec Git
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]'
PS1+=' \[\033[01;31m\]$(git_branch)\[\033[00m\]\$ '

# Résultat (si dans repo Git) :
# alice@server:~/project main$
```

---

**Test 5 : Prompt avec code de retour**

```bash
# Afficher code retour en rouge si erreur
PS1='$(if [ $? -eq 0 ]; then echo "\[\033[01;32m\][OK]"; else echo "\[\033[01;31m\][X]"; fi)\[\033[00m\] '
PS1+='\[\033[01;34m\]\w\[\033[00m\]\$ '

# [OK] si succès (vert)
# [X] si erreur (rouge)
```

---

**Test 6 : Prompt complet professionnel**

```bash
# Ajouter à ~/.bashrc
cat >> ~/.bashrc << 'EOF'

# ===== Prompt personnalisé =====

# Fonction pour Git branch
parse_git_branch() {
    git branch 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}

# Couleurs
COLOR_RED='\[\033[01;31m\]'
COLOR_GREEN='\[\033[01;32m\]'
COLOR_YELLOW='\[\033[01;33m\]'
COLOR_BLUE='\[\033[01;34m\]'
COLOR_PURPLE='\[\033[01;35m\]'
COLOR_CYAN='\[\033[01;36m\]'
COLOR_WHITE='\[\033[01;37m\]'
COLOR_RESET='\[\033[00m\]'

# Prompt
if [ "$USER" = "root" ]; then
    # Prompt root en rouge
    PS1="${COLOR_RED}\u@\h${COLOR_RESET}:${COLOR_BLUE}\w${COLOR_RESET}"
else
    # Prompt utilisateur en vert
    PS1="${COLOR_GREEN}\u@\h${COLOR_RESET}:${COLOR_BLUE}\w${COLOR_RESET}"
fi

# Ajouter Git branch si dans repo
PS1+="${COLOR_YELLOW}\$(parse_git_branch)${COLOR_RESET}\$ "

EOF

source ~/.bashrc
```

---

### ÉTAPE 11 : Variables pour configuration d'applications

**Beaucoup d'applications utilisent les variables d'environnement**

---

**Test 1 : Variables d'environnement courantes**

```bash
# Éditeur par défaut
export EDITOR=vim
export VISUAL=vim

# Pager (pour man, less, etc.)
export PAGER=less

# Langue
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

# Timezone
export TZ=Europe/Paris

# Proxy HTTP (si nécessaire)
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
export NO_PROXY=localhost,127.0.0.1
```

---

**Test 2 : Configuration Python**

```bash
# Virtual env Python
export VIRTUAL_ENV_DISABLE_PROMPT=1
export PIPENV_VENV_IN_PROJECT=1

# Python path
export PYTHONPATH=/opt/myapp/lib:$PYTHONPATH
```

---

**Test 3 : Configuration Node.js**

```bash
# Node modules path
export NODE_PATH=/usr/local/lib/node_modules

# npm config
export NPM_CONFIG_PREFIX=~/.npm-global
```

---

**Test 4 : Configuration Java**

```bash
# Java home
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64

# Maven
export M2_HOME=/opt/maven
export PATH=$M2_HOME/bin:$PATH
```

---

**Test 5 : Configuration de bases de données**

```bash
# PostgreSQL
export PGDATA=/var/lib/postgresql/data
export PGUSER=postgres
export PGDATABASE=mydb

# MySQL
export MYSQL_HOST=localhost
export MYSQL_USER=root
export MYSQL_DATABASE=mydb
```

---

**Test 6 : Configuration d'applications avec .env**

```bash
# Créer fichier .env
cat > .env << 'EOF'
DATABASE_URL=postgresql://user:pass@localhost/mydb
SECRET_KEY=super-secret-key-123
DEBUG=true
API_KEY=abc123xyz789
EOF

# Charger dans le shell
set -a  # Auto-export variables
source .env
set +a  # Désactiver auto-export

# Vérifier
echo $DATABASE_URL
echo $API_KEY
```

---

### ÉTAPE 12 : Débugger les problèmes de variables

**Test 1 : Tracer l'exécution d'un script**

```bash
# Script de test
cat > debug-test.sh << 'EOF'
#!/bin/bash
set -x  # Activer trace mode

NAME="Alice"
AGE=30

echo "Nom: $NAME"
echo "Age: $AGE"

RESULT=$((AGE + 10))
echo "Dans 10 ans: $RESULT"

set +x  # Désactiver trace mode
EOF

chmod +x debug-test.sh
./debug-test.sh
```

**Résultat :**

```
+ NAME=Alice
+ AGE=30
+ echo 'Nom: Alice'
Nom: Alice
+ echo 'Age: 30'
Age: 30
+ RESULT=40
+ echo 'Dans 10 ans: 40'
Dans 10 ans: 40
+ set +x
```

---

**Test 2 : Options de debug**

```bash
# En début de script
set -x  # Trace (afficher commandes)
set -e  # Exit on error
set -u  # Error sur variable non définie
set -o pipefail  # Erreur dans pipes

# Ou en une ligne
set -euxo pipefail

# Désactiver
set +x
set +e
set +u
set +o pipefail
```

---

**Test 3 : Vérifier si variable est définie**

```bash
# Tester si définie
if [ -z "${VAR+x}" ]; then
    echo "VAR n'est pas définie"
else
    echo "VAR est définie : $VAR"
fi

# Tester si non vide
if [ -n "$VAR" ]; then
    echo "VAR est non vide"
fi
```

---

**Test 4 : Afficher toutes les variables**

```bash
# Variables d'environnement
env | sort

# Toutes les variables (locales + env)
set | sort

# Variables spécifiques
env | grep PATH
set | grep BASH
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : Variables fonctionnent**

```bash
NAME="Test"
if [ "$NAME" = "Test" ]; then
    echo "[OK] Test 1 OK : Variables fonctionnent"
else
    echo "[X] Test 1 FAIL"
fi
```

---

**Test 2 : Export fonctionne**

```bash
export TEST_VAR="exported"
RESULT=$(bash -c 'echo $TEST_VAR')

if [ "$RESULT" = "exported" ]; then
    echo "[OK] Test 2 OK : Export fonctionne"
else
    echo "[X] Test 2 FAIL"
fi
```

---

**Test 3 : PATH contient ~/bin**

```bash
mkdir -p ~/bin
export PATH="$HOME/bin:$PATH"

if echo $PATH | grep -q "$HOME/bin"; then
    echo "[OK] Test 3 OK : ~/bin dans PATH"
else
    echo "[X] Test 3 FAIL"
fi
```

---

**Test 4 : Alias fonctionne**

```bash
alias testcmd='echo "alias works"'
RESULT=$(testcmd)

if [ "$RESULT" = "alias works" ]; then
    echo "[OK] Test 4 OK : Alias fonctionne"
else
    echo "[X] Test 4 FAIL"
fi

unalias testcmd
```

---

**Test 5 : .bashrc existe et est chargé**

```bash
if [ -f ~/.bashrc ]; then
    echo "[OK] Test 5 OK : .bashrc existe"
else
    echo "[ATTENTION]  Test 5 : .bashrc n'existe pas"
fi
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Déclaration de variables**
```bash
VAR="value"        # Variable locale
export VAR="value" # Variable d'environnement
```

**2. Utilisation**
```bash
echo $VAR
echo ${VAR}  # Préférable (plus clair)
```

**3. Variables spéciales**

| Variable | Description |
|----------|-------------|
| `$0` | Nom du script |
| `$1, $2...` | Arguments |
| `$#` | Nombre d'arguments |
| `$@` | Tous les arguments |
| `$?` | Code retour |
| `$$` | PID |
| `$!` | PID dernier background |

**4. PATH**
```bash
export PATH="$HOME/bin:$PATH"
```

**5. Fichiers config**
- `~/.bashrc` : Config bash interactive
- `~/.bash_profile` : Login shell
- `~/.profile` : POSIX (tous shells)

**6. Aliases vs Fonctions**
- **Alias** : Simple, pas d'arguments
- **Fonction** : Logique complexe, arguments

**7. Bonnes pratiques**
- [OK] Utiliser `${VAR}` au lieu de `$VAR`
- [OK] Exporter seulement ce qui est nécessaire
- [OK] Documenter les variables importantes
- [OK] Tester avec `set -u` (erreur si non définie)
- [OK] Quotes autour des variables : `"$VAR"`

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Variables read-only**

```bash
readonly CONST="Cannot change"
declare -r CONST2="Also constant"
```

---

**2. Variables de type spécifique**

```bash
declare -i NUMBER=42     # Integer
declare -a ARRAY         # Array
declare -A ASSOC_ARRAY   # Associative array
declare -x EXPORTED      # Export
declare -r READONLY      # Read-only
```

---

**3. Heredoc avec variables**

```bash
cat << EOF
Hello, $USER
Your home is: $HOME
EOF
```

---

**4. Sourcing intelligent**

```bash
# Charger fichier seulement s'il existe
[ -f ~/.bash_aliases ] && . ~/.bash_aliases
```

---

**5. Configuration avancée avec direnv**

```bash
# Installer direnv
sudo apt install direnv

# Ajouter à ~/.bashrc
eval "$(direnv hook bash)"

# Créer .envrc dans projet
echo 'export DATABASE_URL=postgresql://localhost/mydb' > .envrc
direnv allow

# Variables chargées automatiquement en entrant dans le dossier !
```

---

## [COURS] CONCLUSION DE L'EXERCICE 7.1

**[OK] FÉLICITATIONS ! Tu maîtrises maintenant les variables et l'environnement bash !**

**Ce que tu as appris :**
- Variables locales et d'environnement
- Variables spéciales ($?, $$, $1, etc.)
- Manipulation de chaînes et tableaux
- Configuration du PATH
- Fichiers de configuration (.bashrc, .profile)
- Aliases et fonctions
- Personnalisation du prompt (PS1)
- Variables pour applications
- Debugging

**Compétences acquises :**
- [OK] Variables bash (niveau avancé)
- [OK] Configuration shell
- [OK] Scripting bash
- [OK] Personnalisation environnement
- [OK] Troubleshooting

**Temps moyen de réalisation :** 2-2h30

---

# [BRAVO] BRAVO ! TU AS TERMINÉ 7 SECTIONS COMPLÈTES !

**Sections terminées :**
1. [OK] Commandes de base
2. [OK] Texte et manipulation
3. [OK] Permissions et propriété
4. [OK] Processus et services
5. [OK] Réseau et connectivité
6. [OK] Archives et compression
7. [OK] Variables et environnement

**Tu es maintenant un expert Linux ! [RAPIDE]**

Tu veux :
- Un récapitulatif général de tout ce qu'on a vu ?
- Un projet final pratique qui combine toutes les compétences ?
- Ou autre chose ?

# [DOCS] SECTION 8 : RECHERCHE DE FICHIERS

# [VERT] EXERCICE 8.1 : RECHERCHE AVEC FIND, LOCATE & AUTRES (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Trouver des fichiers spécifiques (logs, configs, scripts)
- Localiser des fichiers volumineux qui remplissent le disque
- Chercher des fichiers par date de modification
- Trouver des fichiers avec permissions incorrectes
- Nettoyer les fichiers temporaires
- Auditer les fichiers SUID/SGID
- Rechercher du contenu dans des fichiers
- Identifier les doublons

**Problématique :** Dans un système Linux avec des milliers de fichiers, il faut pouvoir localiser rapidement ce qu'on cherche : par nom, taille, date, contenu, permissions, propriétaire, etc.

### Cahier des charges

- Maîtriser la commande `find`
- Utiliser `locate` et `updatedb`
- Chercher des exécutables avec `which` et `whereis`
- Combiner find avec d'autres commandes
- Rechercher par critères multiples
- Exécuter des actions sur les résultats
- Optimiser les performances de recherche
- Créer des scripts de maintenance

### Contraintes

- Commandes : `find`, `locate`, `which`, `whereis`, `type`
- Tests avec opérateurs logiques
- Actions avec `-exec` et `-delete`
- Temps estimé : 2h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Utiliser find avec tous ses critères
- [OK] Rechercher par nom, taille, date, permissions
- [OK] Combiner plusieurs critères
- [OK] Exécuter des actions sur les résultats
- [OK] Utiliser locate pour recherches rapides
- [OK] Trouver des commandes et exécutables
- [OK] Optimiser les performances
- [OK] Créer des scripts de maintenance
- [OK] Auditer la sécurité du système
- [OK] Nettoyer automatiquement

---

## [DOCS] PRÉREQUIS

- Sections 1-7 complètes
- Compréhension du système de fichiers
- Notions de permissions

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer l'environnement de test

```bash
# Créer le dossier de travail
mkdir -p ~/find-exercise
cd ~/find-exercise

# Créer une structure de fichiers test
mkdir -p {documents,images,videos,backup,temp,logs}

# Fichiers avec différentes extensions
touch documents/rapport.txt
touch documents/facture.pdf
touch documents/contrat.docx
touch documents/note.md

# Images
touch images/photo1.jpg
touch images/photo2.png
touch images/logo.svg

# Videos
touch videos/demo.mp4
touch videos/tutorial.avi

# Logs
touch logs/app.log
touch logs/system.log
touch logs/error.log

# Fichiers temporaires
touch temp/cache1.tmp
touch temp/session2.tmp

# Backup
touch backup/backup-2024-01-01.tar.gz
touch backup/backup-2024-06-15.tar.gz
touch backup/backup-2024-12-30.tar.gz

# Fichiers cachés
touch .hidden-config
touch .secret-key

# Fichiers avec différentes tailles
dd if=/dev/zero of=documents/small.dat bs=1K count=10 2>/dev/null
dd if=/dev/zero of=documents/medium.dat bs=1M count=5 2>/dev/null
dd if=/dev/zero of=documents/large.dat bs=1M count=50 2>/dev/null

# Fichiers avec différentes permissions
touch documents/readonly.txt
chmod 444 documents/readonly.txt

touch documents/executable.sh
chmod 755 documents/executable.sh

touch documents/private.key
chmod 600 documents/private.key

# Fichiers avec différentes dates (modifier mtime)
touch -d "2024-01-01" logs/old.log
touch -d "2024-06-15" logs/medium.log
touch -d "2024-12-30" logs/recent.log

# Créer des liens
ln -s documents/rapport.txt lien-rapport.txt
ln documents/facture.pdf lien-dur-facture.pdf

# Voir la structure
tree . 2>/dev/null || find . -type f | head -20

echo ""
echo "[OK] Environnement de test créé"
du -sh .
```

---

### ÉTAPE 2 : Commande `find` - Recherche par nom

**find = Chercher des fichiers dans une hiérarchie de dossiers**

**Syntaxe de base :**

```
find [CHEMIN] [TESTS] [ACTIONS]
```

---

**Test 1 : find basique**

```bash
# Tous les fichiers dans le dossier courant
find .

# Tous les fichiers dans /home
find /home

# Limite à un niveau (pas récursif)
find . -maxdepth 1

# Au moins 2 niveaux de profondeur
find . -mindepth 2
```

---

**Test 2 : Recherche par nom (-name)**

```bash
# Fichier spécifique
find . -name "rapport.txt"

# Avec wildcard (pattern)
find . -name "*.txt"

# Tous les fichiers .log
find . -name "*.log"

# Plusieurs extensions
find . -name "*.txt" -o -name "*.md"
```

**[ATTENTION] Important : `-name` est sensible à la casse**

---

**Test 3 : Recherche insensible à la casse (-iname)**

```bash
# Créer fichiers test
touch README.md
touch readme.txt
touch ReadMe.doc

# Sensible à la casse
find . -name "readme*"
# Trouve seulement readme.txt

# Insensible à la casse
find . -iname "readme*"
# Trouve README.md, readme.txt, ReadMe.doc
```

---

**Test 4 : Recherche par pattern**

```bash
# Commence par "backup"
find . -name "backup*"

# Termine par ".log"
find . -name "*.log"

# Contient "error" quelque part
find . -name "*error*"

# Un seul caractère (?)
find . -name "photo?.jpg"
# Trouve photo1.jpg, photo2.jpg

# Plage de caractères
find . -name "backup-2024-[0-9][0-9]-*.tar.gz"
```

---

**Test 5 : Recherche par chemin (-path)**

```bash
# Chemin contenant "documents"
find . -path "*/documents/*"

# Chemin se terminant par "backup"
find . -path "*backup"

# Exclure un chemin
find . -path "./temp" -prune -o -print
# -prune = ne pas descendre dans ce dossier
```

---

### ÉTAPE 3 : Recherche par type (-type)

**Types de fichiers :**

| Type | Description |
|------|-------------|
| `f` | Fichier régulier |
| `d` | Dossier (directory) |
| `l` | Lien symbolique |
| `c` | Fichier caractère (character device) |
| `b` | Fichier bloc (block device) |
| `p` | Pipe (FIFO) |
| `s` | Socket |

---

**Test 1 : Recherche par type**

```bash
# Seulement les fichiers
find . -type f

# Seulement les dossiers
find . -type d

# Seulement les liens symboliques
find . -type l

# Compter
find . -type f | wc -l  # Nombre de fichiers
find . -type d | wc -l  # Nombre de dossiers
```

---

**Test 2 : Combinaison type + nom**

```bash
# Fichiers .txt seulement (pas les dossiers)
find . -type f -name "*.txt"

# Dossiers contenant "log"
find . -type d -name "*log*"

# Liens symboliques cassés
find . -type l ! -exec test -e {} \; -print
```

---

### ÉTAPE 4 : Recherche par taille (-size)

**Unités de taille :**

| Suffixe | Unité | Exemple |
|---------|-------|---------|
| `c` | Bytes | `100c` = 100 bytes |
| `k` | Kilobytes | `10k` = 10 KB |
| `M` | Megabytes | `5M` = 5 MB |
| `G` | Gigabytes | `1G` = 1 GB |

**Opérateurs :**
- `+` : Plus grand que
- `-` : Plus petit que
- Pas de préfixe : Exactement

---

**Test 1 : Recherche par taille**

```bash
# Fichiers de plus de 1 MB
find . -type f -size +1M

# Fichiers de moins de 10 KB
find . -type f -size -10k

# Fichiers vides (0 bytes)
find . -type f -size 0

# Fichiers entre 1 MB et 10 MB
find . -type f -size +1M -size -10M
```

---

**Test 2 : Trouver les plus gros fichiers**

```bash
# 10 plus gros fichiers
find . -type f -exec du -h {} + | sort -rh | head -10

# Plus lisible avec printf
find . -type f -printf '%s %p\n' | sort -rn | head -10 | awk '{
    size = $1;
    if (size > 1073741824) printf "%.2f GB\t", size/1073741824;
    else if (size > 1048576) printf "%.2f MB\t", size/1048576;
    else if (size > 1024) printf "%.2f KB\t", size/1024;
    else printf "%d B\t", size;
    for (i=2; i<=NF; i++) printf "%s ", $i;
    printf "\n";
}'
```

---

**Test 3 : Nettoyer les fichiers volumineux**

```bash
# Fichiers de plus de 100 MB dans /tmp
find /tmp -type f -size +100M -ls

# Afficher avec taille lisible
find /tmp -type f -size +100M -exec du -h {} \;

# Supprimer (avec confirmation)
find /tmp -type f -size +100M -exec rm -i {} \;
```

---

### ÉTAPE 5 : Recherche par date

**Options de temps :**

| Option | Description | Mesure |
|--------|-------------|---------|
| `-mtime` | Modification time | Jours |
| `-atime` | Access time | Jours |
| `-ctime` | Change time (metadata) | Jours |
| `-mmin` | Modification time | Minutes |
| `-amin` | Access time | Minutes |
| `-cmin` | Change time | Minutes |
| `-newer` | Plus récent qu'un fichier | - |

**Opérateurs :**
- `+N` : Plus de N jours/minutes
- `-N` : Moins de N jours/minutes
- `N` : Exactement N jours/minutes

---

**Test 1 : Recherche par date de modification**

```bash
# Modifiés dans les dernières 24h
find . -type f -mtime -1

# Modifiés il y a plus de 7 jours
find . -type f -mtime +7

# Modifiés il y a exactement 7 jours
find . -type f -mtime 7

# Modifiés dans les dernières 60 minutes
find . -type f -mmin -60

# Modifiés dans les dernières 10 minutes
find . -type f -mmin -10
```

---

**Test 2 : Recherche par date d'accès**

```bash
# Accédés dans les dernières 24h
find . -type f -atime -1

# Pas accédés depuis plus de 30 jours
find . -type f -atime +30

# Fichiers "dormants" (pas accédés depuis 1 an)
find . -type f -atime +365
```

---

**Test 3 : Comparaison avec fichier de référence**

```bash
# Créer fichier de référence
touch reference-date

# Fichiers plus récents que reference-date
find . -type f -newer reference-date

# Fichiers plus anciens (avec !)
find . -type f ! -newer reference-date
```

---

**Test 4 : Plage de dates**

```bash
# Fichiers modifiés entre 7 et 14 jours
find . -type f -mtime +7 -mtime -14

# Fichiers de la semaine dernière (7 à 1 jours)
find . -type f -mtime -7 -mtime +1
```

---

**Test 5 : Nettoyer les vieux fichiers**

```bash
# Logs de plus de 30 jours
find /var/log -name "*.log" -type f -mtime +30

# Fichiers temporaires de plus de 7 jours
find /tmp -type f -mtime +7 -delete

# Fichiers backup de plus de 90 jours
find ./backup -name "*.tar.gz" -mtime +90 -delete

# [ATTENTION] ATTENTION : -delete est définitif ! Toujours tester sans -delete d'abord
```

---

### ÉTAPE 6 : Recherche par permissions

**Test 1 : Recherche par permissions exactes**

```bash
# Permissions exactes 644
find . -type f -perm 644

# Permissions exactes 755
find . -type f -perm 755

# Permissions exactes 600
find . -type f -perm 600
```

---

**Test 2 : Recherche par bits de permissions**

```bash
# Au moins ces permissions (-)
find . -type f -perm -644  # Au moins rw-r--r--

# N'importe lequel de ces bits (/)
find . -type f -perm /222  # Write bit pour owner OU group OU others

# Fichiers exécutables par owner
find . -type f -perm -100

# Fichiers lisibles par tous
find . -type f -perm -444
```

---

**Test 3 : Fichiers avec permissions problématiques**

```bash
# Fichiers world-writable (dangereux !)
find . -type f -perm -002

# Fichiers world-writable ET exécutables (très dangereux !)
find . -type f -perm -002 -perm -001

# Dossiers world-writable sans sticky bit
find . -type d -perm -002 ! -perm -1000
```

---

**Test 4 : Fichiers SUID et SGID**

```bash
# Fichiers SUID (bit 4000)
find / -type f -perm -4000 2>/dev/null

# Fichiers SGID (bit 2000)
find / -type f -perm -2000 2>/dev/null

# SUID ET SGID
find / -type f -perm -6000 2>/dev/null

# SUID pas owned by root (suspect !)
find / -type f -perm -4000 ! -user root 2>/dev/null
```

---

**Test 5 : Audit de sécurité**

```bash
# Script d'audit de permissions
cat > audit-permissions.sh << 'EOF'
#!/bin/bash

echo "[RECHERCHE] Audit de Permissions"
echo "======================"
echo ""

# World-writable files
echo "[ATTENTION]  Fichiers world-writable :"
find / -type f -perm -002 2>/dev/null | head -10
echo ""

# SUID files
echo "[SECURISE] Fichiers SUID :"
find / -type f -perm -4000 2>/dev/null | head -10
echo ""

# SGID files
echo "[SECURISE] Fichiers SGID :"
find / -type f -perm -2000 2>/dev/null | head -10
echo ""

# Files without owner
echo "[UTILISATEUR] Fichiers sans propriétaire :"
find / -nouser -o -nogroup 2>/dev/null | head -10
echo ""

echo "[OK] Audit terminé"
EOF

chmod +x audit-permissions.sh
# ./audit-permissions.sh  # Nécessite sudo pour scan complet
```

---

### ÉTAPE 7 : Recherche par propriétaire

**Test 1 : Recherche par utilisateur**

```bash
# Fichiers appartenant à un utilisateur
find . -user alice

# Fichiers appartenant à l'utilisateur courant
find . -user $(whoami)

# Fichiers appartenant à root
find /home -user root 2>/dev/null
```

---

**Test 2 : Recherche par groupe**

```bash
# Fichiers appartenant au groupe
find . -group developers

# Fichiers du groupe www-data
find /var/www -group www-data
```

---

**Test 3 : Fichiers orphelins**

```bash
# Fichiers sans propriétaire (UID n'existe plus)
find / -nouser 2>/dev/null

# Fichiers sans groupe (GID n'existe plus)
find / -nogroup 2>/dev/null

# Les deux
find / \( -nouser -o -nogroup \) 2>/dev/null
```

---

**Test 4 : Recherche par UID/GID**

```bash
# Par UID
find . -uid 1000

# Par GID
find . -gid 1000

# UID supérieur à 1000 (utilisateurs normaux)
find /home -uid +1000
```

---

### ÉTAPE 8 : Opérateurs logiques

**Opérateurs :**

| Opérateur | Description |
|-----------|-------------|
| `-a` ou `-and` | ET (implicite) |
| `-o` ou `-or` | OU |
| `!` ou `-not` | NON |
| `\( ... \)` | Groupement |

---

**Test 1 : Opérateur ET (implicite)**

```bash
# Fichiers .txt ET modifiés récemment
find . -name "*.txt" -mtime -7

# Équivalent avec -and explicite
find . -name "*.txt" -and -mtime -7

# Multiple conditions
find . -type f -name "*.log" -size +1M -mtime +7
```

---

**Test 2 : Opérateur OU (-o)**

```bash
# Fichiers .txt OU .md
find . -name "*.txt" -o -name "*.md"

# Fichiers .jpg OU .png OU .svg
find . \( -name "*.jpg" -o -name "*.png" -o -name "*.svg" \)

# Note : Parenthèses nécessaires pour groupement
```

---

**Test 3 : Opérateur NON (!)**

```bash
# Tous les fichiers SAUF .txt
find . -type f ! -name "*.txt"

# Fichiers pas modifiés dans les 30 derniers jours
find . -type f ! -mtime -30

# Fichiers qui ne sont PAS exécutables
find . -type f ! -perm -111
```

---

**Test 4 : Combinaisons complexes**

```bash
# (Extension .txt OU .md) ET modifiés récemment
find . \( -name "*.txt" -o -name "*.md" \) -mtime -7

# Fichiers > 1MB SAUF dans backup/
find . -type f -size +1M ! -path "*/backup/*"

# Logs récents OU de grande taille
find . -name "*.log" \( -mtime -7 -o -size +10M \)

# Fichiers .tmp OU plus de 30 jours
find . \( -name "*.tmp" -o -mtime +30 \)
```

---

### ÉTAPE 9 : Actions sur les résultats

**Actions disponibles :**

| Action | Description |
|--------|-------------|
| `-print` | Afficher (défaut) |
| `-print0` | Afficher avec null delimiter |
| `-ls` | Listing détaillé (comme ls -l) |
| `-delete` | Supprimer |
| `-exec CMD {} \;` | Exécuter commande |
| `-exec CMD {} +` | Exécuter commande (optimisé) |
| `-ok CMD {} \;` | Exécuter avec confirmation |
| `-printf FORMAT` | Format personnalisé |

---

**Test 1 : -print (défaut)**

```bash
# Affichage normal
find . -name "*.txt"

# Équivalent
find . -name "*.txt" -print
```

---

**Test 2 : -ls (listing détaillé)**

```bash
# Comme ls -l
find . -name "*.log" -ls

# Affiche : inode, permissions, liens, owner, group, size, date, nom
```

---

**Test 3 : -delete (supprimer)**

```bash
# [ATTENTION] ATTENTION : Pas de confirmation !

# Supprimer fichiers .tmp
find . -name "*.tmp" -delete

# Toujours tester SANS -delete d'abord !
find . -name "*.tmp"              # Vérifier
find . -name "*.tmp" -delete      # Puis supprimer
```

---

**Test 4 : -exec (exécuter commande)**

```bash
# Syntaxe 1 : {} \; (une commande par fichier)
find . -name "*.txt" -exec cat {} \;

# Syntaxe 2 : {} + (plusieurs fichiers en une commande)
find . -name "*.txt" -exec cat {} +

# Copier tous les .log vers backup/
find . -name "*.log" -exec cp {} backup/ \;

# Changer permissions
find . -name "*.sh" -exec chmod +x {} \;

# Afficher taille
find . -name "*.pdf" -exec du -h {} \;
```

**Différence `\;` vs `+` :**

```bash
# Avec \; : Exécute la commande pour CHAQUE fichier
find . -name "*.txt" -exec echo "Fichier: {}" \;
# Fichier: ./file1.txt
# Fichier: ./file2.txt

# Avec + : Exécute UNE FOIS avec tous les fichiers
find . -name "*.txt" -exec echo "Fichiers:" {} +
# Fichiers: ./file1.txt ./file2.txt
```

**`+` est généralement plus rapide !**

---

**Test 5 : -ok (confirmation interactive)**

```bash
# Demande confirmation pour chaque fichier
find . -name "*.tmp" -ok rm {} \;
# < rm ... ./temp/cache1.tmp > ? y
# < rm ... ./temp/session2.tmp > ? y
```

---

**Test 6 : -printf (format personnalisé)**

```bash
# Format personnalisé
find . -type f -printf "%p : %s bytes\n"

# Avec plusieurs champs
find . -type f -printf "Fichier: %p\nTaille: %s\nDate: %t\n\n"
```

**Codes printf :**

| Code | Description |
|------|-------------|
| `%p` | Nom du fichier |
| `%f` | Basename (nom sans chemin) |
| `%h` | Dirname (chemin sans nom) |
| `%s` | Taille en bytes |
| `%k` | Taille en KB |
| `%t` | Date de modification |
| `%u` | User name |
| `%g` | Group name |
| `%m` | Permissions (octal) |

---

### ÉTAPE 10 : Optimisation et performance

**Test 1 : Limiter la profondeur**

```bash
# Seulement le niveau actuel
find . -maxdepth 1 -name "*.txt"

# Maximum 2 niveaux
find . -maxdepth 2 -type f

# Minimum 2 niveaux (exclut niveau actuel)
find . -mindepth 2 -type f
```

---

**Test 2 : Exclure des dossiers (-prune)**

```bash
# Exclure node_modules
find . -path "*/node_modules" -prune -o -type f -print

# Exclure plusieurs dossiers
find . \( -path "*/node_modules" -o -path "*/\.git" \) -prune -o -type f -print

# Exclure .git et afficher seulement .js
find . -path "*/.git" -prune -o -name "*.js" -type f -print
```

---

**Test 3 : Redirection des erreurs**

```bash
# Supprimer erreurs "Permission denied"
find / -name "*.conf" 2>/dev/null

# Séparer stdout et stderr
find / -name "*.conf" 2>errors.log 1>results.txt

# Afficher seulement les erreurs
find / -name "*.conf" 1>/dev/null
```

---

**Test 4 : Parallélisation avec xargs**

```bash
# Sans parallélisation
find . -name "*.txt" -exec gzip {} \;

# Avec xargs (plus rapide)
find . -name "*.txt" -print0 | xargs -0 -P 4 gzip
# -print0 : null-separated
# -0 : xargs lit null-separated
# -P 4 : 4 processus parallèles
```

---

### ÉTAPE 11 : Commande `locate` - Recherche rapide

**locate = Base de données pré-indexée (beaucoup plus rapide que find)**

---

**Test 1 : locate basique**

```bash
# Installer si nécessaire
sudo apt install mlocate

# Mettre à jour la base de données
sudo updatedb

# Rechercher
locate rapport.txt

# Avec wildcard
locate "*.pdf"

# Insensible à la casse
locate -i readme
```

---

**Test 2 : Limiter les résultats**

```bash
# Limiter à 10 résultats
locate -l 10 "*.log"

# Compter les résultats
locate -c "*.conf"
```

---

**Test 3 : Recherche par basename**

```bash
# Dans tout le chemin (défaut)
locate config
# Trouve /etc/config, /home/user/.config, etc.

# Seulement le nom de fichier
locate -b "\config"
# Trouve seulement fichiers/dossiers nommés exactement "config"
```

---

**Test 4 : Mise à jour de la base**

```bash
# Mise à jour complète
sudo updatedb

# Mise à jour en excluant certains dossiers
sudo updatedb --prunepaths="/tmp /var/tmp"

# Configuration dans /etc/updatedb.conf
cat /etc/updatedb.conf
```

---

**Différence find vs locate :**

| Aspect | find | locate |
|--------|------|--------|
| **Vitesse** | * Lent | *** Très rapide |
| **Actualité** | *** Temps réel | * Peut être obsolète |
| **Flexibilité** | *** Très flexible | * Nom seulement |
| **Usage** | Recherche complexe | Recherche simple et rapide |

---

### ÉTAPE 12 : Autres commandes de recherche

**Test 1 : which - Chercher des exécutables**

```bash
# Où est la commande ls ?
which ls
# /usr/bin/ls

# Plusieurs commandes
which ls cat grep

# Afficher tous les chemins (si plusieurs)
which -a python
# /usr/bin/python
# /usr/local/bin/python
```

---

**Test 2 : whereis - Chercher binaires, sources, man pages**

```bash
# Chercher nginx
whereis nginx
# nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx /usr/share/man/man8/nginx.8.gz

# Seulement les binaires
whereis -b nginx
# nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx

# Seulement les man pages
whereis -m nginx
# nginx: /usr/share/man/man8/nginx.8.gz

# Seulement les sources
whereis -s nginx
```

---

**Test 3 : type - Type de commande**

```bash
# Type de commande
type ls
# ls is aliased to `ls --color=auto'

type cd
# cd is a shell builtin

type python
# python is /usr/bin/python

# Tous les emplacements
type -a echo
# echo is a shell builtin
# echo is /usr/bin/echo
```

---

**Test 4 : command -v - Vérifier existence**

```bash
# Vérifier si commande existe
if command -v docker &> /dev/null; then
    echo "Docker est installé"
else
    echo "Docker n'est pas installé"
fi

# Dans un script
command -v git || { echo "Git requis !"; exit 1; }
```

---

### ÉTAPE 13 : Scripts de maintenance avancés

**Script 1 : Nettoyage automatique des fichiers temporaires**

```bash
cat > cleanup-temp.sh << 'EOF'
#!/bin/bash

# Configuration
TEMP_DIRS=("/tmp" "/var/tmp" "$HOME/.cache")
AGE_DAYS=7
LOG_FILE="/var/log/cleanup-temp.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "[NETTOYAGE] Nettoyage des fichiers temporaires"

for dir in "${TEMP_DIRS[@]}"; do
    if [ -d "$dir" ]; then
        log "Nettoyage de $dir (> $AGE_DAYS jours)..."
        
        # Compter d'abord
        COUNT=$(find "$dir" -type f -mtime +$AGE_DAYS 2>/dev/null | wc -l)
        log "  Fichiers trouvés : $COUNT"
        
        # Supprimer
        find "$dir" -type f -mtime +$AGE_DAYS -delete 2>/dev/null
        
        log "  [OK] Nettoyé"
    else
        log "  [ATTENTION]  $dir n'existe pas"
    fi
done

# Dossiers vides
log "Suppression des dossiers vides..."
for dir in "${TEMP_DIRS[@]}"; do
    find "$dir" -type d -empty -delete 2>/dev/null
done

log "[OK] Nettoyage terminé"
EOF

chmod +x cleanup-temp.sh
```

---

**Script 2 : Trouver et compresser les vieux logs**

```bash
cat > compress-old-logs.sh << 'EOF'
#!/bin/bash

LOG_DIR="/var/log"
AGE_DAYS=7

echo "[COMPRESSION]  Compression des logs > $AGE_DAYS jours"

# Trouver logs non compressés
find "$LOG_DIR" -name "*.log" -type f -mtime +$AGE_DAYS | while read logfile; do
    if [ -f "$logfile" ]; then
        echo "Compression : $logfile"
        gzip -9 "$logfile"
    fi
done

echo "[OK] Compression terminée"

# Supprimer les logs compressés de plus de 30 jours
echo "[SUPPRIMER]  Suppression des logs compressés > 30 jours"
find "$LOG_DIR" -name "*.log.gz" -type f -mtime +30 -delete

echo "[OK] Nettoyage terminé"
EOF

chmod +x compress-old-logs.sh
```

---

**Script 3 : Audit des gros fichiers**

```bash
cat > find-large-files.sh << 'EOF'
#!/bin/bash

MIN_SIZE="${1:-100M}"  # Défaut 100MB
TARGET_DIR="${2:-.}"    # Défaut dossier courant

echo "[RECHERCHE] Recherche de fichiers > $MIN_SIZE dans $TARGET_DIR"
echo "=================================================="

find "$TARGET_DIR" -type f -size +$MIN_SIZE -exec du -h {} \; 2>/dev/null | \
    sort -rh | \
    head -20 | \
    nl

echo ""
echo "Top 20 des plus gros fichiers"
EOF

chmod +x find-large-files.sh

# Utilisation
./find-large-files.sh 10M /home
```

---

**Script 4 : Trouver les doublons**

```bash
cat > find-duplicates.sh << 'EOF'
#!/bin/bash

TARGET_DIR="${1:-.}"

echo "[RECHERCHE] Recherche de doublons dans $TARGET_DIR"

# Méthode 1 : Par taille (rapide mais faux positifs)
echo "=== Fichiers de même taille ==="
find "$TARGET_DIR" -type f -exec du -b {} \; | \
    sort -n | \
    uniq -d -w 10

echo ""

# Méthode 2 : Par checksum MD5 (précis mais lent)
echo "=== Doublons exacts (MD5) ==="
find "$TARGET_DIR" -type f -exec md5sum {} \; 2>/dev/null | \
    sort | \
    uniq -w 32 -D

echo ""
echo "[OK] Recherche terminée"
EOF

chmod +x find-duplicates.sh
```

---

**Script 5 : Backup sélectif**

```bash
cat > selective-backup.sh << 'EOF'
#!/bin/bash

SOURCE_DIR="$HOME/documents"
BACKUP_DIR="/backup/documents"
EXCLUDE_PATTERNS=("*.tmp" "*.cache" ".git")

DATE=$(date +%Y%m%d)
BACKUP_FILE="$BACKUP_DIR/backup-$DATE.tar.gz"

mkdir -p "$BACKUP_DIR"

echo "[PACKAGE] Backup de $SOURCE_DIR"

# Construire les exclusions
EXCLUDE_ARGS=""
for pattern in "${EXCLUDE_PATTERNS[@]}"; do
    EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude=$pattern"
done

# Créer liste des fichiers modifiés dans les 7 derniers jours
TEMP_LIST=$(mktemp)
find "$SOURCE_DIR" -type f -mtime -7 > "$TEMP_LIST"

# Créer archive
tar -czf "$BACKUP_FILE" -T "$TEMP_LIST" $EXCLUDE_ARGS

rm "$TEMP_LIST"

echo "[OK] Backup créé : $BACKUP_FILE"
du -h "$BACKUP_FILE"
EOF

chmod +x selective-backup.sh
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : find fonctionne**

```bash
touch test-find.txt
if find . -name "test-find.txt" | grep -q "test-find.txt"; then
    echo "[OK] Test 1 OK : find fonctionne"
else
    echo "[X] Test 1 FAIL"
fi
rm test-find.txt
```

---

**Test 2 : find avec conditions multiples**

```bash
# Créer fichiers test
mkdir -p test-multi
touch test-multi/file1.txt
touch test-multi/file2.log
chmod 755 test-multi/file2.log

# Chercher .log exécutables
COUNT=$(find test-multi -name "*.log" -perm -111 | wc -l)

if [ "$COUNT" -eq 1 ]; then
    echo "[OK] Test 2 OK : Conditions multiples fonctionnent"
else
    echo "[X] Test 2 FAIL"
fi

rm -rf test-multi
```

---

**Test 3 : locate fonctionne**

```bash
if command -v locate &> /dev/null; then
    echo "[OK] Test 3 OK : locate disponible"
else
    echo "[ATTENTION]  Test 3 : locate pas installé"
fi
```

---

**Test 4 : which trouve les commandes**

```bash
if which bash &> /dev/null; then
    echo "[OK] Test 4 OK : which fonctionne"
else
    echo "[X] Test 4 FAIL"
fi
```

---

**Test 5 : find -exec fonctionne**

```bash
# Créer fichiers test
mkdir -p test-exec
echo "test" > test-exec/file1.txt
echo "test" > test-exec/file2.txt

# Compter avec -exec
COUNT=$(find test-exec -name "*.txt" -exec wc -l {} \; | awk '{sum+=$1} END {print sum}')

if [ "$COUNT" -eq 2 ]; then
    echo "[OK] Test 5 OK : -exec fonctionne"
else
    echo "[X] Test 5 FAIL"
fi

rm -rf test-exec
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. find - Commande principale**
```bash
find [CHEMIN] [TESTS] [ACTIONS]
```

**2. Tests courants**

| Test | Description | Exemple |
|------|-------------|---------|
| `-name` | Par nom | `-name "*.txt"` |
| `-type` | Par type | `-type f` |
| `-size` | Par taille | `-size +1M` |
| `-mtime` | Par date | `-mtime -7` |
| `-perm` | Par permissions | `-perm 755` |
| `-user` | Par propriétaire | `-user alice` |

**3. Actions courantes**

| Action | Description |
|--------|-------------|
| `-print` | Afficher (défaut) |
| `-ls` | Listing détaillé |
| `-delete` | Supprimer |
| `-exec CMD {} \;` | Exécuter |

**4. Opérateurs logiques**
- ET : espace ou `-and`
- OU : `-o`
- NON : `!`

**5. Optimisation**
- `-maxdepth N` : Limiter profondeur
- `-prune` : Exclure dossiers
- `{} +` au lieu de `{} \;`
- Rediriger erreurs : `2>/dev/null`

**6. find vs locate**
- **find** : Temps réel, flexible
- **locate** : Rapide mais base pré-indexée

**7. Bonnes pratiques**
- [OK] Toujours tester sans `-delete`
- [OK] Utiliser `-print0` avec `xargs -0`
- [OK] Rediriger les erreurs
- [OK] Limiter la profondeur si possible
- [OK] Utiliser `+` au lieu de `\;` pour performance

---

## [COURS] CONCLUSION DE L'EXERCICE 8.1

**[OK] Félicitations ! Tu maîtrises maintenant la recherche de fichiers !**

**Ce que tu as appris :**
- Commande find complète (nom, type, taille, date, permissions)
- Opérateurs logiques
- Actions sur résultats (-exec, -delete, etc.)
- Commande locate
- which, whereis, type
- Scripts de maintenance
- Optimisation des recherches
- Audit de sécurité

**Compétences acquises :**
- [OK] Recherche de fichiers (niveau expert)
- [OK] Maintenance automatisée
- [OK] Audit système
- [OK] Optimisation
- [OK] Scripting avancé

**Temps moyen :** 2h

---

Tu veux continuer avec le prochain sujet de ta liste ?
**Prochains sujets disponibles :**
- 7. Disques & Filesystems (df, du, mount, LVM, RAID)
- 8. Utilisateurs & Groupes (useradd, usermod, sudo)
- 10. Gestion Paquets (apt, yum, snap)
- 12. Cron & Tâches planifiées

Lequel tu préfères ?

# [DOCS] SECTION 9 : DISQUES & SYSTÈMES DE FICHIERS

# [JAUNE] EXERCICE 9.1 : GESTION DES DISQUES ET PARTITIONS (INTERMÉDIAIRE)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Surveiller l'utilisation de l'espace disque
- Créer et gérer des partitions
- Monter et démonter des disques
- Configurer le montage automatique (/etc/fstab)
- Gérer les systèmes de fichiers
- Étendre des volumes (LVM)
- Diagnostiquer les problèmes d'espace
- Optimiser le stockage

**Problématique :** L'espace disque est une ressource critique. Il faut savoir surveiller l'utilisation, gérer les partitions, monter des disques supplémentaires, et résoudre les problèmes de manque d'espace.

### Cahier des charges

- Surveiller l'espace disque avec `df` et `du`
- Comprendre les systèmes de fichiers (ext4, xfs, btrfs)
- Créer et gérer des partitions
- Monter/démonter des disques
- Configurer /etc/fstab
- Gérer les UUID et labels
- Comprendre LVM (Logical Volume Management)
- Diagnostiquer les problèmes d'espace
- Nettoyer et optimiser

### Contraintes

- Commandes : `df`, `du`, `mount`, `fdisk`, `lsblk`, `blkid`
- [ATTENTION] Attention aux opérations destructives !
- Tests sur VM ou disque virtuel
- Temps estimé : 2-3h

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :
- [OK] Surveiller l'utilisation disque
- [OK] Comprendre les systèmes de fichiers
- [OK] Lister et identifier les disques
- [OK] Créer des partitions
- [OK] Formater des partitions
- [OK] Monter/démonter des disques
- [OK] Configurer le montage automatique
- [OK] Gérer les UUID et labels
- [OK] Diagnostiquer les problèmes d'espace
- [OK] Comprendre LVM

---

## [DOCS] PRÉREQUIS

- Sections 1-8 complètes
- Accès sudo
- [ATTENTION] VM recommandée pour les tests destructifs
- Disque additionnel pour tests (optionnel)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Surveiller l'espace disque avec `df`

**df = Disk Free (espace disque libre)**

---

**Test 1 : df basique**

```bash
# Voir l'utilisation des disques
df

# Format lisible (human-readable)
df -h

# Résultat typique :
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sda1        50G   20G   28G  42% /
# /dev/sda2       100G   45G   50G  48% /home
# tmpfs           2.0G  1.5M  2.0G   1% /run
```

**Colonnes :**

```
Filesystem      Size  Used Avail Use% Mounted on
    v            v     v     v     v      v
Partition    Taille Utilisé Libre % Point de montage
```

---

**Test 2 : Options utiles de df**

```bash
# Lisible pour humains
df -h

# Afficher le type de système de fichiers
df -T

# Afficher les inodes
df -i

# Exclure les filesystems temporaires
df -h -x tmpfs -x devtmpfs

# Seulement un filesystem spécifique
df -h /

# Format total
df -h --total
```

---

**Test 3 : Interpréter les résultats**

```bash
# Script d'alerte si disque > 80%
cat > check-disk-space.sh << 'EOF'
#!/bin/bash

THRESHOLD=80

echo "[RECHERCHE] Vérification espace disque (seuil: ${THRESHOLD}%)"
echo "================================================"

df -h | awk -v threshold=$THRESHOLD '
NR>1 {
    # Extraire le pourcentage (sans le %)
    gsub(/%/, "", $5)
    usage = $5
    
    if (usage > threshold) {
        print "[ATTENTION]  ALERTE:", $6, "->", usage"% utilisé"
    } else if (usage > threshold - 10) {
        print "[RAPIDE] Attention:", $6, "->", usage"% utilisé"
    } else {
        print "[OK] OK:", $6, "->", usage"% utilisé"
    }
}
'
EOF

chmod +x check-disk-space.sh
./check-disk-space.sh
```

---

### ÉTAPE 2 : Analyser l'utilisation avec `du`

**du = Disk Usage (utilisation disque par dossier/fichier)**

---

**Test 1 : du basique**

```bash
# Taille du dossier courant
du -sh .

# Taille de chaque sous-dossier (1 niveau)
du -h --max-depth=1

# Taille de chaque sous-dossier (triée)
du -h --max-depth=1 | sort -rh
```

---

**Test 2 : Trouver les gros dossiers**

```bash
# Top 10 des plus gros dossiers
du -h / 2>/dev/null | sort -rh | head -10

# Dans /home seulement
du -h --max-depth=2 /home 2>/dev/null | sort -rh | head -10

# Seuil minimum (ex: >100MB)
du -h / 2>/dev/null | awk '$1 ~ /[0-9]+G|[0-9]{3,}M/ {print}' | sort -rh | head -20
```

---

**Test 3 : Analyse par type de fichier**

```bash
# Script d'analyse
cat > disk-usage-analysis.sh << 'EOF'
#!/bin/bash

TARGET="${1:-.}"

echo "[GRAPHIQUE] Analyse de l'utilisation disque : $TARGET"
echo "============================================="
echo ""

# Taille totale
echo "Taille totale :"
du -sh "$TARGET"
echo ""

# Top 10 sous-dossiers
echo "Top 10 sous-dossiers :"
du -h --max-depth=1 "$TARGET" 2>/dev/null | sort -rh | head -11 | tail -10
echo ""

# Par extension
echo "Espace par type de fichier :"
find "$TARGET" -type f -name "*.*" 2>/dev/null | sed 's/.*\.//' | \
    sort | uniq -c | sort -rn | head -10 | \
    awk '{printf "%-10s: %d fichiers\n", $2, $1}'
echo ""

# Plus gros fichiers
echo "Top 10 plus gros fichiers :"
find "$TARGET" -type f -exec du -h {} + 2>/dev/null | \
    sort -rh | head -10 | \
    awk '{printf "%-8s %s\n", $1, $2}'

EOF

chmod +x disk-usage-analysis.sh
./disk-usage-analysis.sh ~
```

---

**Test 4 : Différence df vs du**

```bash
# df affiche l'espace utilisé au niveau filesystem
df -h /

# du affiche l'espace utilisé par les fichiers
du -sh /

# [ATTENTION] Peuvent être différents car :
# - Fichiers supprimés mais encore ouverts (non libérés)
# - Métadonnées du filesystem
# - Inodes
```

---

### ÉTAPE 3 : Lister et identifier les disques

**Test 1 : lsblk - Visualiser les disques et partitions**

```bash
# Affichage en arbre
lsblk

# Résultat typique :
# NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
# sda      8:0    0   100G  0 disk
# ├─sda1   8:1    0    50G  0 part /
# ├─sda2   8:2    0    45G  0 part /home
# └─sda3   8:3    0     5G  0 part [SWAP]
# sr0     11:0    1  1024M  0 rom

# Avec filesystem
lsblk -f

# Avec UUID et autres infos
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,UUID,FSTYPE
```

**Colonnes :**
- **NAME** : Nom du device
- **SIZE** : Taille
- **TYPE** : disk, part (partition), lvm, raid
- **MOUNTPOINT** : Où c'est monté
- **UUID** : Identifiant unique
- **FSTYPE** : Type de système de fichiers

---

**Test 2 : fdisk -l - Lister tous les disques**

```bash
# Lister tous les disques (nécessite sudo)
sudo fdisk -l

# Un disque spécifique
sudo fdisk -l /dev/sda
```

---

**Test 3 : blkid - Afficher UUID et infos**

```bash
# Tous les devices
sudo blkid

# Résultat :
# /dev/sda1: UUID="abc-123" TYPE="ext4" PARTUUID="xyz-789"
# /dev/sda2: UUID="def-456" TYPE="ext4" PARTUUID="uvw-012"

# Device spécifique
sudo blkid /dev/sda1

# Seulement UUID
sudo blkid -s UUID -o value /dev/sda1
```

---

**Test 4 : Informations système de fichiers**

```bash
# Type de filesystem
df -T

# Informations détaillées ext4
sudo tune2fs -l /dev/sda1

# Informations détaillées xfs
sudo xfs_info /dev/sda1  # Si xfs

# Statistiques d'utilisation
sudo dumpe2fs /dev/sda1 | grep -i "block count"
```

---

### ÉTAPE 4 : Comprendre /etc/fstab

**/etc/fstab = Table de montage automatique au boot**

---

**Test 1 : Lire /etc/fstab**

```bash
# Voir le fichier
cat /etc/fstab

# Résultat typique :
# # <device>  <mountpoint>  <type>  <options>  <dump>  <pass>
# UUID=abc-123  /  ext4  defaults  0  1
# UUID=def-456  /home  ext4  defaults  0  2
# UUID=ghi-789  none  swap  sw  0  0
```

**Format :**

```
UUID=abc-123  /home  ext4  defaults  0  2
     v         v      v       v      v  v
  Device   MountPt  Type  Options Dump Pass
```

**Colonnes :**

| Colonne | Description | Exemples |
|---------|-------------|----------|
| **Device** | UUID, LABEL, ou /dev/sdXN | `UUID=abc-123` |
| **Mountpoint** | Où monter | `/home`, `/mnt/data` |
| **Type** | Système de fichiers | `ext4`, `xfs`, `vfat` |
| **Options** | Options de montage | `defaults`, `noatime` |
| **Dump** | Backup (0=non, 1=oui) | `0` |
| **Pass** | Ordre vérification fsck | `0,1,2` |

---

**Test 2 : Options de montage courantes**

```bash
# Options dans /etc/fstab
```

| Option | Description |
|--------|-------------|
| `defaults` | Options par défaut (rw,suid,dev,exec,auto,nouser,async) |
| `rw` | Read-Write |
| `ro` | Read-Only |
| `noatime` | Ne pas mettre à jour access time (performance) |
| `nodiratime` | Ne pas mettre à jour access time des dossiers |
| `noexec` | Pas d'exécution de binaires |
| `nosuid` | Ignorer SUID/SGID |
| `nodev` | Pas de périphériques |
| `auto` | Monter automatiquement au boot |
| `noauto` | Ne pas monter automatiquement |
| `user` | Les utilisateurs peuvent monter |
| `nouser` | Seulement root peut monter (défaut) |
| `errors=remount-ro` | Remonter en RO si erreur |

---

**Test 3 : Vérifier /etc/fstab**

```bash
# Vérifier la syntaxe (ne monte pas)
sudo findmnt --verify

# Tester le montage de tout
sudo mount -a

# Si erreur, le système détaillera le problème

# Vérifier ce qui est actuellement monté
mount | column -t
```

---

### ÉTAPE 5 : Monter et démonter manuellement

**Test 1 : Monter un disque manuellement**

```bash
# Créer point de montage
sudo mkdir -p /mnt/data

# Monter
sudo mount /dev/sdb1 /mnt/data

# Vérifier
df -h /mnt/data
mount | grep /mnt/data

# Utiliser
cd /mnt/data
ls -la
```

---

**Test 2 : Monter avec options**

```bash
# Read-only
sudo mount -o ro /dev/sdb1 /mnt/data

# Avec noexec et nosuid (sécurité)
sudo mount -o noexec,nosuid /dev/sdb1 /mnt/data

# Plusieurs options
sudo mount -o rw,noatime,nodiratime /dev/sdb1 /mnt/data
```

---

**Test 3 : Monter par UUID**

```bash
# Trouver UUID
UUID=$(sudo blkid -s UUID -o value /dev/sdb1)

# Monter par UUID (recommandé)
sudo mount UUID=$UUID /mnt/data

# Ou avec -U
sudo mount -U $UUID /mnt/data
```

---

**Test 4 : Démonter**

```bash
# Démonter
sudo umount /mnt/data

# Si "device is busy"
# Trouver qui utilise
sudo lsof /mnt/data
sudo fuser -m /mnt/data

# Forcer (attention !)
sudo umount -f /mnt/data

# Démonter paresseusement (détache immédiatement, nettoie quand libre)
sudo umount -l /mnt/data
```

---

**Test 5 : Remonter avec nouvelles options**

```bash
# Remonter en read-only sans démonter
sudo mount -o remount,ro /mnt/data

# Remonter en read-write
sudo mount -o remount,rw /mnt/data

# Ajouter noatime
sudo mount -o remount,noatime /mnt/data
```

---

### ÉTAPE 6 : Créer et formater des partitions

**[ATTENTION] ATTENTION : Ces commandes sont DESTRUCTIVES ! Utiliser sur VM ou disque test !**

---

**Test 1 : Créer une partition avec fdisk (MBR)**

```bash
# [ATTENTION] Remplacer /dev/sdX par ton disque test !
DISK="/dev/sdX"  # ATTENTION : Vérifier avec lsblk !

# Lancer fdisk
sudo fdisk $DISK

# Commandes interactives :
# p    - Afficher partitions
# n    - Nouvelle partition
# d    - Supprimer partition
# t    - Changer type
# w    - Écrire et quitter
# q    - Quitter sans sauver
```

**Exemple de session fdisk :**

```
sudo fdisk /dev/sdb

Command (m for help): n        # Nouvelle partition
Partition type:
   p   primary
   e   extended
Select: p                       # Primary

Partition number (1-4): 1       # Numéro

First sector: [Enter]           # Défaut (début)
Last sector: +10G               # 10 GB

Command (m for help): w         # Écrire et quitter
```

---

**Test 2 : Créer une partition avec parted (GPT)**

```bash
# Parted est plus moderne et supporte GPT

# Créer table de partition GPT
sudo parted /dev/sdb mklabel gpt

# Créer partition
sudo parted /dev/sdb mkpart primary ext4 0% 50%
sudo parted /dev/sdb mkpart primary ext4 50% 100%

# Afficher
sudo parted /dev/sdb print

# Mode interactif
sudo parted /dev/sdb
```

---

**Test 3 : Formater une partition**

```bash
# Format ext4 (Linux standard)
sudo mkfs.ext4 /dev/sdb1

# Format xfs (performant pour gros fichiers)
sudo mkfs.xfs /dev/sdb1

# Format vfat/fat32 (USB, compatibilité Windows)
sudo mkfs.vfat /dev/sdb1

# Format ntfs (Windows)
sudo mkfs.ntfs /dev/sdb1

# Avec label
sudo mkfs.ext4 -L "DataDisk" /dev/sdb1

# Avec options
sudo mkfs.ext4 -L "Data" -m 1 /dev/sdb1  # -m 1 = 1% réservé (au lieu de 5%)
```

---

**Test 4 : Vérifier et réparer un filesystem**

```bash
# [ATTENTION] Le filesystem DOIT être démonté !

# Vérifier ext4
sudo fsck /dev/sdb1

# Vérifier et réparer automatiquement
sudo fsck -y /dev/sdb1

# Vérifier au prochain reboot
sudo touch /forcefsck

# Vérifier xfs
sudo xfs_repair /dev/sdb1

# Vérifier ntfs
sudo ntfsfix /dev/sdb1
```

---

### ÉTAPE 7 : UUID et Labels

**UUID = Universal Unique Identifier (recommandé pour /etc/fstab)**

---

**Test 1 : Gérer les UUID**

```bash
# Voir UUID
sudo blkid /dev/sdb1

# Extraire seulement UUID
UUID=$(sudo blkid -s UUID -o value /dev/sdb1)
echo $UUID

# Trouver device par UUID
sudo blkid -U $UUID

# Monter par UUID (meilleure pratique)
sudo mount UUID=$UUID /mnt/data
```

**Pourquoi UUID plutôt que /dev/sdXN ?**

```
/dev/sda peut devenir /dev/sdb si tu branches un autre disque !
UUID ne change jamais -> Plus fiable
```

---

**Test 2 : Gérer les Labels**

```bash
# Créer label lors du format
sudo mkfs.ext4 -L "MyData" /dev/sdb1

# Changer label existant (ext4)
sudo e2label /dev/sdb1 "NewLabel"

# Changer label (xfs)
sudo xfs_admin -L "NewLabel" /dev/sdb1

# Voir label
sudo blkid -s LABEL -o value /dev/sdb1

# Monter par label
sudo mount LABEL=MyData /mnt/data
```

---

**Test 3 : Exemple /etc/fstab avec UUID et Label**

```bash
# Ajouter à /etc/fstab

# Par UUID (recommandé)
UUID=abc-123-def-456  /mnt/data  ext4  defaults,noatime  0  2

# Par Label (alternatif)
LABEL=MyData  /mnt/data  ext4  defaults,noatime  0  2

# Par device (déconseillé)
/dev/sdb1  /mnt/data  ext4  defaults,noatime  0  2
```

---

### ÉTAPE 8 : LVM (Logical Volume Manager) - Introduction

**LVM = Flexibilité pour gérer l'espace disque**

**Concepts :**

```
Disques physiques (sda, sdb)
    v
Physical Volumes (PV) - Initialiser les disques pour LVM
    v
Volume Groups (VG) - Pool d'espace (combine plusieurs PV)
    v
Logical Volumes (LV) - Partitions "virtuelles" flexibles
    v
Systèmes de fichiers (ext4, xfs, etc.)
```

---

**Test 1 : Voir la configuration LVM existante**

```bash
# Voir Physical Volumes
sudo pvs
sudo pvdisplay

# Voir Volume Groups
sudo vgs
sudo vgdisplay

# Voir Logical Volumes
sudo lvs
sudo lvdisplay

# Vue d'ensemble
sudo lsblk
```

---

**Test 2 : Créer une configuration LVM simple**

```bash
# [ATTENTION] Exemple théorique - NE PAS exécuter sans disque test !

# 1. Créer Physical Volume
sudo pvcreate /dev/sdb1

# 2. Créer Volume Group
sudo vgcreate vg_data /dev/sdb1

# 3. Créer Logical Volume (10 GB)
sudo lvcreate -L 10G -n lv_data vg_data

# 4. Formater
sudo mkfs.ext4 /dev/vg_data/lv_data

# 5. Monter
sudo mkdir -p /mnt/lvm-data
sudo mount /dev/vg_data/lv_data /mnt/lvm-data
```

---

**Test 3 : Étendre un Logical Volume**

```bash
# [ATTENTION] Exemple théorique

# Étendre le LV de 5 GB
sudo lvextend -L +5G /dev/vg_data/lv_data

# Étendre le filesystem (ext4)
sudo resize2fs /dev/vg_data/lv_data

# Ou en une commande (avec -r)
sudo lvextend -L +5G -r /dev/vg_data/lv_data

# Pour xfs
sudo xfs_growfs /mnt/lvm-data
```

---

**Test 4 : Ajouter un disque au Volume Group**

```bash
# [ATTENTION] Exemple théorique

# 1. Créer PV sur nouveau disque
sudo pvcreate /dev/sdc1

# 2. Étendre le VG
sudo vgextend vg_data /dev/sdc1

# 3. Maintenant tu as plus d'espace pour créer/étendre des LV
sudo vgdisplay vg_data  # Voir l'espace libre
```

---

**Avantages de LVM :**

| Avantage | Description |
|----------|-------------|
| [OK] **Flexibilité** | Étendre/réduire facilement |
| [OK] **Snapshots** | Backup à un instant T |
| [OK] **Multi-disques** | Combiner plusieurs disques |
| [OK] **Migration** | Déplacer données entre disques |
| [X] **Complexité** | Plus complexe que partitions simples |

---

### ÉTAPE 9 : Diagnostiquer les problèmes d'espace

**Problème : "No space left on device"**

---

**Test 1 : Identifier le problème**

```bash
# Vérifier l'espace disque
df -h

# Vérifier les inodes
df -i

# Les deux peuvent causer "No space left"
# - Espace plein (0% Available)
# - Inodes pleins (IUse% = 100%)
```

---

**Test 2 : Trouver ce qui consomme l'espace**

```bash
# Dossiers les plus gros
sudo du -sh /* 2>/dev/null | sort -rh | head -10

# Descendre dans les sous-dossiers
sudo du -h --max-depth=2 /var 2>/dev/null | sort -rh | head -20

# Script complet
cat > find-disk-hogs.sh << 'EOF'
#!/bin/bash

echo "[RECHERCHE] Recherche de ce qui consomme l'espace disque"
echo "==============================================="
echo ""

# Top dossiers à la racine
echo "Top 10 dossiers (racine) :"
sudo du -sh /* 2>/dev/null | sort -rh | head -10
echo ""

# Fichiers logs volumineux
echo "Logs volumineux (> 100MB) :"
sudo find /var/log -type f -size +100M -exec du -h {} \; 2>/dev/null | sort -rh
echo ""

# Fichiers temporaires
echo "Fichiers temporaires (> 100MB) :"
sudo find /tmp /var/tmp -type f -size +100M -exec du -h {} \; 2>/dev/null | sort -rh
echo ""

# Cache apt (si Debian/Ubuntu)
if [ -d /var/cache/apt/archives ]; then
    echo "Cache APT :"
    du -sh /var/cache/apt/archives
    echo ""
fi

# Fichiers supprimés mais encore ouverts
echo "Fichiers supprimés mais encore ouverts :"
sudo lsof / | grep deleted | awk '{sum+=$7} END {print "Total:", sum/1024/1024, "MB"}'

EOF

chmod +x find-disk-hogs.sh
sudo ./find-disk-hogs.sh
```

---

**Test 3 : Fichiers supprimés mais encore ouverts**

```bash
# Lister les fichiers supprimés mais encore ouverts
sudo lsof | grep deleted

# Trier par taille
sudo lsof | grep deleted | awk '{print $7, $9}' | sort -rn

# Solution : Redémarrer le processus qui a le fichier ouvert
sudo systemctl restart <service>
```

---

**Test 4 : Problème d'inodes**

```bash
# Vérifier les inodes
df -i

# Si IUse% = 100% :
# Trop de petits fichiers !

# Trouver les dossiers avec beaucoup de fichiers
sudo find / -xdev -type d -exec sh -c 'echo $(find "{}" -maxdepth 1 | wc -l) {}' \; 2>/dev/null | sort -rn | head -20

# Ou plus simple
for dir in /*; do
    echo "$dir : $(find "$dir" -type f 2>/dev/null | wc -l) fichiers"
done | sort -t: -k2 -rn
```

---

**Test 5 : Nettoyer l'espace**

```bash
# Script de nettoyage
cat > cleanup-disk-space.sh << 'EOF'
#!/bin/bash

echo "[NETTOYAGE] Nettoyage de l'espace disque"
echo "==============================="
echo ""

# Espace avant
echo "Espace AVANT :"
df -h /
echo ""

# 1. Logs anciens
echo "1⃣  Nettoyage logs (> 30 jours)..."
sudo find /var/log -name "*.log" -type f -mtime +30 -delete
sudo find /var/log -name "*.gz" -type f -mtime +30 -delete
echo "   [OK] Fait"

# 2. Cache APT
if [ -d /var/cache/apt/archives ]; then
    echo "2⃣  Nettoyage cache APT..."
    sudo apt-get clean
    echo "   [OK] Fait"
fi

# 3. Journald logs
echo "3⃣  Nettoyage journald (> 30 jours)..."
sudo journalctl --vacuum-time=30d
echo "   [OK] Fait"

# 4. Fichiers temporaires
echo "4⃣  Nettoyage /tmp (> 7 jours)..."
sudo find /tmp -type f -mtime +7 -delete 2>/dev/null
sudo find /var/tmp -type f -mtime +7 -delete 2>/dev/null
echo "   [OK] Fait"

# 5. Thumbnails cache
if [ -d ~/.cache/thumbnails ]; then
    echo "5⃣  Nettoyage cache thumbnails..."
    rm -rf ~/.cache/thumbnails/*
    echo "   [OK] Fait"
fi

# 6. Cores dumps
echo "6⃣  Nettoyage core dumps..."
sudo find / -name "core" -type f -delete 2>/dev/null
echo "   [OK] Fait"

echo ""
echo "Espace APRÈS :"
df -h /
echo ""
echo "[OK] Nettoyage terminé"

EOF

chmod +x cleanup-disk-space.sh
```

---

### ÉTAPE 10 : Surveillance et monitoring

**Test 1 : Alertes automatiques**

```bash
# Script de monitoring
cat > monitor-disk-space.sh << 'EOF'
#!/bin/bash

THRESHOLD=80
EMAIL="admin@example.com"  # Configurer

df -h | awk -v threshold=$THRESHOLD '
NR>1 {
    gsub(/%/, "", $5)
    usage = $5
    filesystem = $1
    mountpoint = $6
    
    if (usage > threshold) {
        print "[ATTENTION]  ALERT: " mountpoint " (" filesystem ") is at " usage "%"
        # Envoyer email (si mail configuré)
        # system("echo \"Disk " mountpoint " at " usage "%\" | mail -s \"Disk Alert\" " EMAIL)
    }
}
'
EOF

chmod +x monitor-disk-space.sh

# Ajouter au cron (toutes les heures)
# 0 * * * * /path/to/monitor-disk-space.sh
```

---

**Test 2 : Dashboard simple**

```bash
cat > disk-dashboard.sh << 'EOF'
#!/bin/bash

clear
echo "═══════════════════════════════════════════════════"
echo "       [GRAPHIQUE] DASHBOARD ESPACE DISQUE"
echo "═══════════════════════════════════════════════════"
echo ""

# Utilisation par filesystem
echo "[ARCHIVE]  Utilisation par filesystem :"
df -h -x tmpfs -x devtmpfs | awk 'NR==1 || $5+0 > 0'
echo ""

# Top 5 plus gros dossiers
echo "[DOSSIER] Top 5 plus gros dossiers (/) :"
sudo du -sh /* 2>/dev/null | sort -rh | head -5
echo ""

# Inodes
echo "[NOMBRE] Utilisation inodes :"
df -i / | awk 'NR>1 {print $6 ": " $5}'
echo ""

# Alertes
echo "[ATTENTION]  Alertes :"
df -h | awk 'NR>1 {
    gsub(/%/, "", $5)
    if ($5 > 90) print "  CRITIQUE:", $6, "->", $5"%"
    else if ($5 > 80) print "  ATTENTION:", $6, "->", $5"%"
}'
echo ""

echo "═══════════════════════════════════════════════════"
echo "Dernière mise à jour : $(date)"
EOF

chmod +x disk-dashboard.sh
./disk-dashboard.sh
```

---

**Test 3 : Monitoring avec watch**

```bash
# Rafraîchir toutes les 5 secondes
watch -n 5 df -h

# Ou avec script personnalisé
watch -n 5 './disk-dashboard.sh'
```

---

### [OK] TESTS DE VALIDATION

**Test 1 : df et du fonctionnent**

```bash
if df -h / > /dev/null 2>&1; then
    echo "[OK] Test 1 OK : df fonctionne"
else
    echo "[X] Test 1 FAIL"
fi
```

---

**Test 2 : Montage/démontage**

```bash
# Créer fichier test
mkdir -p /tmp/test-mount-src
echo "test" > /tmp/test-mount-src/file.txt
mkdir -p /tmp/test-mount-dst

# Monter (bind mount)
sudo mount --bind /tmp/test-mount-src /tmp/test-mount-dst

# Vérifier
if [ -f /tmp/test-mount-dst/file.txt ]; then
    echo "[OK] Test 2 OK : Montage fonctionne"
else
    echo "[X] Test 2 FAIL"
fi

# Nettoyer
sudo umount /tmp/test-mount-dst
rm -rf /tmp/test-mount-src /tmp/test-mount-dst
```

---

**Test 3 : lsblk fonctionne**

```bash
if lsblk > /dev/null 2>&1; then
    echo "[OK] Test 3 OK : lsblk fonctionne"
else
    echo "[X] Test 3 FAIL"
fi
```

---

**Test 4 : blkid fonctionne**

```bash
if sudo blkid > /dev/null 2>&1; then
    echo "[OK] Test 4 OK : blkid fonctionne"
else
    echo "[X] Test 4 FAIL"
fi
```

---

**Test 5 : Espace disque surveillé**

```bash
USAGE=$(df / | awk 'NR==2 {gsub(/%/, "", $5); print $5}')

if [ "$USAGE" -lt 95 ]; then
    echo "[OK] Test 5 OK : Espace disque OK ($USAGE%)"
else
    echo "[ATTENTION]  Test 5 : Espace disque critique ($USAGE%)"
fi
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Surveillance de base**
```bash
df -h           # Espace par filesystem
du -sh DIR      # Espace utilisé par dossier
df -i           # Inodes
```

**2. Identification**
```bash
lsblk           # Visualiser disques/partitions
sudo blkid      # UUID et types
sudo fdisk -l   # Liste complète
```

**3. Montage manuel**
```bash
sudo mount /dev/sdX1 /mnt/point
sudo umount /mnt/point
```

**4. Montage automatique**
```bash
# /etc/fstab
UUID=xxx /mnt/data ext4 defaults,noatime 0 2
```

**5. Colonnes /etc/fstab**
```
Device  MountPoint  Type  Options  Dump  Pass
```

**6. Bonnes pratiques**
- [OK] Utiliser UUID plutôt que /dev/sdXN
- [OK] Surveiller régulièrement (cron)
- [OK] Nettoyer logs/cache automatiquement
- [OK] Tester /etc/fstab avec `mount -a`
- [OK] Toujours vérifier avant fdisk/mkfs
- [OK] Backup avant opérations destructives

**7. Ordres de magnitude**
- 80% = Attention
- 90% = Alerte
- 95% = Critique
- 100% = Urgence

**8. Causes "No space left"**
- Espace disque plein (df -h)
- Inodes pleins (df -i)
- Fichiers supprimés mais ouverts (lsof)

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. RAID logiciel (mdadm)**

```bash
# Créer RAID 1 (mirroring)
sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb1 /dev/sdc1

# Voir état
cat /proc/mdstat
```

---

**2. Quotas disque**

```bash
# Activer quotas
sudo apt install quota

# Éditer /etc/fstab
# UUID=xxx /home ext4 defaults,usrquota,grpquota 0 2

# Initialiser
sudo quotacheck -cum /home
sudo quotaon /home

# Définir quota pour utilisateur
sudo edquota -u alice

# Voir quotas
sudo repquota /home
```

---

**3. Systèmes de fichiers avancés**

```bash
# Btrfs - Snapshots natifs
sudo mkfs.btrfs /dev/sdb1
sudo mount /dev/sdb1 /mnt/btrfs
sudo btrfs subvolume snapshot /mnt/btrfs /mnt/btrfs/snapshot

# ZFS - Très avancé (nécessite installation)
sudo zpool create mypool /dev/sdb
sudo zfs create mypool/data
```

---

**4. Monitoring avancé**

```bash
# iotop - Monitoring I/O
sudo apt install iotop
sudo iotop

# ncdu - du interactif
sudo apt install ncdu
ncdu /

# Prometheus + Node Exporter
# Pour monitoring production
```

---

**5. Optimisation performance**

```bash
# Activer TRIM pour SSD
sudo fstrim -v /

# Ajouter au cron hebdomadaire
# 0 0 * * 0 /usr/sbin/fstrim -v / > /dev/null

# Options mount pour performance
# noatime,nodiratime dans /etc/fstab
```

---

## [COURS] CONCLUSION DE L'EXERCICE 9.1

**[OK] Félicitations ! Tu maîtrises maintenant la gestion des disques !**

**Ce que tu as appris :**
- Surveillance (df, du)
- Identification (lsblk, blkid, fdisk)
- Montage/démontage
- /etc/fstab
- UUID et labels
- Partitionnement
- Formatage
- Introduction LVM
- Diagnostic problèmes d'espace
- Scripts de monitoring/nettoyage

**Compétences acquises :**
- [OK] Gestion disques (niveau avancé)
- [OK] Surveillance et alertes
- [OK] Diagnostic et résolution
- [OK] Automatisation
- [OK] Optimisation

**Temps moyen :** 2-3h

---

**Prochaine étape :** Exercice 9.2 - LVM et RAID (optionnel, avancé) ou passer au prochain sujet ?

Tu veux :
1. Continuer avec LVM/RAID avancé ?
2. Passer à **Utilisateurs & Groupes** ?
3. Passer à **Gestion de paquets** ?

Lequel tu préfères ?


# [DOCS] SECTION 10 : UTILISATEURS & GROUPES

# [JAUNE] GESTION COMPLÈTE DES UTILISATEURS ET GROUPES (INTERMÉDIAIRE/AVANCÉ)

## [LISTE] INTRODUCTION

### Contexte professionnel

Tu es administrateur système dans une entreprise et tu dois gérer :
- L'onboarding des nouveaux employés (création comptes)
- Les permissions d'accès aux ressources
- Les groupes de travail (développeurs, ops, finance, etc.)
- Les privilèges sudo (qui peut faire quoi)
- La sécurité des comptes (passwords, expiration, verrouillage)
- L'offboarding (départ d'employés)
- L'audit de sécurité des comptes
- La conformité avec les politiques d'entreprise

### Méthodologie : POURQUOI / COMMENT / QUAND

Chaque concept sera expliqué selon :
- **POURQUOI** : Raison d'être, problème résolu
- **COMMENT** : Mise en œuvre technique
- **QUAND** : Cas d'usage, timing

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cette section, tu sauras :
- [OK] Comprendre l'architecture des utilisateurs Linux
- [OK] Créer, modifier, supprimer des utilisateurs
- [OK] Gérer les groupes primaires et secondaires
- [OK] Configurer sudo avec précision
- [OK] Gérer les politiques de mot de passe
- [OK] Auditer la sécurité des comptes
- [OK] Implémenter 5 cas réels d'entreprise
- [OK] Automatiser la gestion des utilisateurs
- [OK] Résoudre les problèmes courants
- [OK] Appliquer les bonnes pratiques de sécurité

---

## [DOCS] TABLE DES MATIÈRES

1. **Concepts fondamentaux**
2. **Fichiers système**
3. **Gestion des utilisateurs**
4. **Gestion des groupes**
5. **Sudo et privilèges**
6. **Sécurité et politiques**
7. **[ENTREPRISE] 5 CAS RÉELS ENTREPRISE**
8. **Automatisation**
9. **Troubleshooting**
10. **Bonnes pratiques**

---

# PARTIE 1 : CONCEPTS FONDAMENTAUX

## [REFLEXION] POURQUOI la gestion des utilisateurs est cruciale ?

**Problèmes sans gestion appropriée :**
- [ROUGE] Risques de sécurité (accès non autorisés)
- [ROUGE] Confusion sur les permissions
- [ROUGE] Difficulté à auditer qui fait quoi
- [ROUGE] Partage de comptes (mauvaise pratique)
- [ROUGE] Impossibilité de tracer les actions
- [ROUGE] Non-conformité réglementaire

**Solutions apportées :**
- [OK] Chaque personne a son compte unique
- [OK] Principe du moindre privilège
- [OK] Traçabilité complète
- [OK] Gestion centralisée
- [OK] Automatisation possible
- [OK] Audit et conformité

---

## [GRAPHIQUE] Architecture des utilisateurs Linux

### Types d'utilisateurs

```bash
# Créer l'environnement de démonstration
mkdir -p ~/users-exercise
cd ~/users-exercise
```

**1. Root (UID 0)**

```
POURQUOI : Contrôle total du système
COMMENT : UID = 0, accès illimité
QUAND : Tâches administratives critiques seulement
```

```bash
# Voir les infos root
id root
# uid=0(root) gid=0(root) groups=0(root)

# [ATTENTION] Ne JAMAIS travailler en root au quotidien !
```

---

**2. Utilisateurs système (UID 1-999)**

```
POURQUOI : Exécuter des services (nginx, mysql, etc.)
COMMENT : Créés par les paquets, UID < 1000, pas de shell interactif
QUAND : Installation de services
```

```bash
# Exemples d'utilisateurs système
grep -E "^(www-data|mysql|postgres|redis)" /etc/passwd

# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
#    v     v  v  v      v        v            v
#  nom    pwd UID GID comment  home        shell (pas de login)
```

**Caractéristiques :**
- Pas de mot de passe (généralement)
- Shell = `/usr/sbin/nologin` ou `/bin/false`
- Home dans `/var` ou `/usr`
- Utilisés pour isolation de sécurité

---

**3. Utilisateurs normaux (UID ≥ 1000)**

```
POURQUOI : Personnes réelles qui utilisent le système
COMMENT : UID ≥ 1000 (configurable), home dans /home
QUAND : Création de comptes pour employés
```

```bash
# Voir les utilisateurs normaux
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, "UID:", $3}' /etc/passwd
```

---

### Groupes : Primaire vs Secondaires

```
POURQUOI : Organiser les permissions par rôle/équipe
COMMENT : Chaque user a 1 groupe primaire + N groupes secondaires
QUAND : Partager l'accès à des ressources entre équipes
```

**Groupe primaire :**
- Attribué à la création du user
- Utilisé pour les nouveaux fichiers créés
- Défini dans `/etc/passwd` (4ème champ)

**Groupes secondaires :**
- Ajoutés avec `usermod -aG` ou `gpasswd -a`
- Donnent des permissions additionnelles
- Listés dans `/etc/group`

```bash
# Voir les groupes d'un utilisateur
id alice
# uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo),999(docker)
#                      ^                    ^                ^        ^
#                  primaire              secondaires
```

---

## [DOSSIER] Fichiers système essentiels

### /etc/passwd

```
POURQUOI : Base de données des utilisateurs
COMMENT : Format texte, 7 champs par ligne
QUAND : Consulté à chaque login
```

```bash
# Voir le contenu
cat /etc/passwd

# Format :
# username:x:UID:GID:GECOS:home:shell
```

**Exemple détaillé :**

```
alice:x:1001:1001:Alice Dupont,Dev Team,555-1234:/home/alice:/bin/bash
  v   v   v    v         v                    v            v
  1   2   3    4         5                    6            7

1. Username (login name)
2. Password (x = dans /etc/shadow)
3. UID (User ID)
4. GID (Primary Group ID)
5. GECOS (nom complet, infos optionnelles)
6. Home directory
7. Shell par défaut
```

```bash
# Extraire des infos spécifiques
cat > parse-passwd.sh << 'EOF'
#!/bin/bash

echo "[LISTE] Analyse /etc/passwd"
echo "====================="
echo ""

# Utilisateurs normaux
echo "[UTILISATEURS] Utilisateurs normaux (UID ≥ 1000) :"
awk -F: '$3 >= 1000 && $3 < 65534 {
    printf "%-15s UID: %-5s Shell: %s\n", $1, $3, $7
}' /etc/passwd
echo ""

# Utilisateurs sans shell interactif
echo "[VERROUILLE] Utilisateurs système (no login) :"
awk -F: '$7 ~ /nologin|false/ {
    printf "%-15s UID: %-5s Purpose: %s\n", $1, $3, $5
}' /etc/passwd | head -10

EOF

chmod +x parse-passwd.sh
./parse-passwd.sh
```

---

### /etc/shadow

```
POURQUOI : Stockage sécurisé des mots de passe
COMMENT : Accessible uniquement par root, mots de passe hachés
QUAND : Consultation lors de l'authentification
```

```bash
# Voir (nécessite sudo)
sudo cat /etc/shadow | head -5

# Format :
# username:password:lastchange:min:max:warn:inactive:expire:reserved
```

**Exemple détaillé :**

```
alice:$6$rounds=5000$xyz...:19000:0:90:7:14:19500:

  v          v                 v    v  v  v  v    v
  1          2                 3    4  5  6  7    8

1. Username
2. Password hash ($6$ = SHA-512)
3. Last password change (jours depuis epoch)
4. Minimum days before change allowed
5. Maximum days password is valid
6. Warning days before expiration
7. Inactive days after expiration
8. Account expiration date
9. Reserved
```

**Déchiffrer les valeurs :**

```bash
# Script pour analyser /etc/shadow
cat > analyze-shadow.sh << 'EOF'
#!/bin/bash

USERNAME="${1:-$(whoami)}"

echo "[SECURISE] Analyse du compte : $USERNAME"
echo "================================"

# Extraire la ligne
LINE=$(sudo grep "^${USERNAME}:" /etc/shadow)

if [ -z "$LINE" ]; then
    echo "[X] Utilisateur non trouvé"
    exit 1
fi

# Parser
IFS=: read -r user pass last min max warn inactive expire reserved <<< "$LINE"

echo ""
echo "Mot de passe :"
if [[ "$pass" == "!" || "$pass" == "*" ]]; then
    echo "  [X] Compte verrouillé"
elif [[ "$pass" == "" ]]; then
    echo "  [ATTENTION]  Pas de mot de passe (connexion sans mdp possible)"
else
    echo "  [OK] Mot de passe défini (hash: ${pass:0:20}...)"
fi

echo ""
echo "Politique de mot de passe :"
echo "  Dernière modification : $last jours depuis epoch"
[ "$min" -gt 0 ] && echo "  Min days avant changement : $min"
[ "$max" != 99999 ] && echo "  Max days validité : $max"
[ "$warn" -gt 0 ] && echo "  Avertissement : $warn jours avant expiration"
[ "$inactive" != "" ] && echo "  Inactivité max : $inactive jours"

echo ""
if [ "$expire" != "" ]; then
    EXPIRE_DATE=$(date -d "@$((expire * 86400))" +%Y-%m-%d 2>/dev/null)
    echo "  [ALARM_CLOCK] Expiration compte : $EXPIRE_DATE"
else
    echo "  ∞ Pas d'expiration"
fi

EOF

chmod +x analyze-shadow.sh
./analyze-shadow.sh $USER
```

---

### /etc/group

```
POURQUOI : Base de données des groupes
COMMENT : Format texte, 4 champs par ligne
QUAND : Gérer les groupes et leurs membres
```

```bash
# Voir le contenu
cat /etc/group

# Format :
# groupname:x:GID:user1,user2,user3
```

**Exemple détaillé :**

```
developers:x:1100:alice,bob,charlie
    v      v  v         v
    1      2  3         4

1. Group name
2. Password (x = rarement utilisé)
3. GID (Group ID)
4. Member list (utilisateurs secondaires)
```

**[ATTENTION] Important :** Les groupes primaires n'apparaissent PAS dans la liste des membres !

```bash
# Voir tous les groupes d'un utilisateur
groups alice
# alice : alice sudo developers docker

# Voir le détail
id alice
# uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo),1100(developers),999(docker)
```

---

### /etc/gshadow

```
POURQUOI : Mots de passe de groupes (rarement utilisé)
COMMENT : Permet de "se connecter" à un groupe avec newgrp
QUAND : Environnements très spécifiques
```

```bash
sudo cat /etc/gshadow | head -5

# Format :
# groupname:password:admins:members
```

---

### /etc/sudoers

```
POURQUOI : Définir qui peut exécuter quoi avec sudo
COMMENT : Fichier de configuration avec syntaxe spéciale
QUAND : Donner des privilèges administratifs limités
```

```bash
# JAMAIS éditer directement ! Utiliser visudo
sudo visudo

# Emplacement : /etc/sudoers
# Includes : /etc/sudoers.d/*
```

**Nous verrons cela en détail dans la partie Sudo.**

---

# PARTIE 2 : GESTION DES UTILISATEURS

## 🆕 Créer un utilisateur - useradd

### POURQUOI utiliser useradd ?

```
PROBLÈME : Besoin d'ajouter un nouvel employé au système
SOLUTION : useradd crée l'utilisateur avec toutes les structures nécessaires
QUAND : Onboarding, création de comptes de service
```

### COMMENT utiliser useradd ?

**Syntaxe de base :**

```bash
useradd [OPTIONS] username
```

---

**Test 1 : Création basique**

```bash
# Créer utilisateur simple
sudo useradd testuser

# Vérifier
grep testuser /etc/passwd
# testuser:x:1001:1001::/home/testuser:/bin/sh

# Problèmes :
# - Pas de mot de passe
# - Pas de home directory créé
# - Shell = /bin/sh (pas bash)
```

---

**Test 2 : Création complète (recommandé)**

```bash
# Créer avec toutes les options
sudo useradd -m -s /bin/bash -c "Alice Dupont" -G sudo alice

# Options :
# -m : Créer home directory
# -s : Shell par défaut
# -c : Commentaire (GECOS)
# -G : Groupes secondaires
```

**Vérifier :**

```bash
# Dans /etc/passwd
grep alice /etc/passwd
# alice:x:1001:1001:Alice Dupont:/home/alice:/bin/bash

# Home créé ?
ls -ld /home/alice
# drwxr-xr-x 2 alice alice 4096 Dec 31 10:00 /home/alice

# Groupes
groups alice
# alice : alice sudo
```

---

**Test 3 : Options avancées**

```bash
# Créer avec UID et GID spécifiques
sudo useradd -m -u 1500 -g 1500 -s /bin/bash bob

# Créer utilisateur système
sudo useradd -r -s /usr/sbin/nologin myapp
# -r : system account (UID < 1000)

# Créer avec home personnalisé
sudo useradd -m -d /opt/appuser -s /bin/bash appuser

# Créer avec date d'expiration
sudo useradd -m -e 2025-12-31 tempuser
# -e : expire date

# Créer avec skeleton personnalisé
sudo useradd -m -k /etc/skel.custom charlie
# -k : skeleton directory
```

---

**Options principales de useradd :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-m` | Créer home directory | `useradd -m alice` |
| `-d DIR` | Home directory personnalisé | `useradd -d /opt/alice alice` |
| `-s SHELL` | Shell par défaut | `useradd -s /bin/bash alice` |
| `-c "TEXT"` | Commentaire (GECOS) | `useradd -c "Alice D" alice` |
| `-u UID` | UID spécifique | `useradd -u 1500 alice` |
| `-g GID` | Groupe primaire | `useradd -g developers alice` |
| `-G GROUPS` | Groupes secondaires | `useradd -G sudo,docker alice` |
| `-r` | Compte système | `useradd -r myapp` |
| `-e DATE` | Date d'expiration | `useradd -e 2025-12-31 alice` |
| `-f DAYS` | Inactivité avant désactivation | `useradd -f 30 alice` |
| `-k SKEL` | Skeleton directory | `useradd -k /etc/skel.dev alice` |
| `-p HASH` | Password hash | `useradd -p '$6$...' alice` |

---

**Test 4 : Skeleton directory (/etc/skel)**

```
POURQUOI : Fichiers par défaut pour nouveaux utilisateurs
COMMENT : Contenu copié dans home à la création
QUAND : Personnaliser l'environnement des nouveaux users
```

```bash
# Voir le contenu
ls -la /etc/skel

# Personnaliser
sudo cp ~/.bashrc /etc/skel/.bashrc.company
sudo tee -a /etc/skel/.bashrc << 'EOF'

# Configuration entreprise
export COMPANY="ACME Corp"
export EDITOR=vim
alias ll='ls -lah'

EOF

# Créer utilisateur (recevra ces fichiers)
sudo useradd -m testskel

# Vérifier
ls -la /home/testskel
cat /home/testskel/.bashrc
```

---

**Test 5 : Définir le mot de passe**

```bash
# Méthode 1 : Interactive
sudo passwd alice
# Enter new UNIX password:
# Retype new UNIX password:

# Méthode 2 : Non-interactive (scripts)
echo "alice:MonMotDePasse123" | sudo chpasswd

# Méthode 3 : Avec hash
PASSWORD_HASH=$(openssl passwd -6 "MonMotDePasse123")
sudo useradd -p "$PASSWORD_HASH" alice

# Méthode 4 : Forcer changement au premier login
sudo passwd -e alice
# User must change password on next login
```

---

### QUAND utiliser chaque méthode ?

| Méthode | Quand utiliser | Sécurité |
|---------|----------------|----------|
| `passwd` interactif | Création manuelle | *** Sécurisé |
| `chpasswd` | Scripts automatisés | ** OK si bien codé |
| `useradd -p` | Rarement (hash pré-calculé) | ** OK |
| `passwd -e` | Forcer changement initial | *** Très bon |

---

## [EDIT] Modifier un utilisateur - usermod

### POURQUOI usermod ?

```
PROBLÈME : Besoin de modifier un compte existant
SOLUTION : usermod permet de changer tous les paramètres
QUAND : Changement de rôle, d'équipe, de shell, etc.
```

### COMMENT utiliser usermod ?

```bash
usermod [OPTIONS] username
```

---

**Test 1 : Modifier le shell**

```bash
# Changer le shell
sudo usermod -s /bin/zsh alice

# Vérifier
grep alice /etc/passwd | cut -d: -f7
# /bin/zsh
```

---

**Test 2 : Ajouter à des groupes**

```bash
# [ATTENTION] ATTENTION : -G seul REMPLACE tous les groupes !
sudo usermod -G developers alice
# alice est maintenant SEULEMENT dans developers (perd sudo, etc.)

# [OK] CORRECT : -aG pour AJOUTER
sudo usermod -aG docker alice
# -a : append (ajouter)
# -G : groups

# Vérifier
groups alice
# alice : alice sudo docker
```

**[ATTENTION] ERREUR COURANTE :**

```bash
# FAUX (remplace tous les groupes)
sudo usermod -G docker alice

# CORRECT (ajoute aux groupes existants)
sudo usermod -aG docker alice
```

---

**Test 3 : Modifier l'UID**

```bash
# Changer UID
sudo usermod -u 1600 alice

# [ATTENTION] Fichiers existants gardent l'ancien UID !
# Il faut les mettre à jour
sudo find /home/alice -user 1001 -exec chown alice {} +
```

---

**Test 4 : Verrouiller/déverrouiller un compte**

```bash
# Verrouiller (disable login)
sudo usermod -L alice
# Ajoute ! devant le hash dans /etc/shadow

# Vérifier
sudo grep alice /etc/shadow
# alice:!$6$xyz...:...

# Déverrouiller
sudo usermod -U alice
```

---

**Test 5 : Changer le home directory**

```bash
# Changer home ET déplacer les fichiers
sudo usermod -m -d /opt/alice alice
# -d : nouveau home
# -m : déplacer les fichiers

# Sans -m (ne déplace PAS les fichiers)
sudo usermod -d /opt/alice2 alice
# Fichiers restent dans ancien home !
```

---

**Test 6 : Modifier la date d'expiration**

```bash
# Définir expiration
sudo usermod -e 2025-12-31 alice

# Voir expiration
sudo chage -l alice
# Account expires : Dec 31, 2025

# Supprimer expiration
sudo usermod -e "" alice
```

---

**Options principales de usermod :**

| Option | Description | Exemple |
|--------|-------------|---------|
| `-s SHELL` | Changer shell | `usermod -s /bin/zsh alice` |
| `-d HOME` | Changer home | `usermod -d /opt/alice alice` |
| `-m` | Déplacer home (avec -d) | `usermod -m -d /new alice` |
| `-G GROUPS` | Remplacer groupes | `usermod -G dev,ops alice` |
| `-aG GROUPS` | Ajouter groupes | `usermod -aG docker alice` |
| `-u UID` | Changer UID | `usermod -u 1600 alice` |
| `-g GID` | Changer groupe primaire | `usermod -g developers alice` |
| `-l NAME` | Renommer | `usermod -l alice2 alice` |
| `-L` | Verrouiller | `usermod -L alice` |
| `-U` | Déverrouiller | `usermod -U alice` |
| `-e DATE` | Date expiration | `usermod -e 2025-12-31 alice` |
| `-f DAYS` | Inactivité max | `usermod -f 30 alice` |

---

## [SUPPRIMER] Supprimer un utilisateur - userdel

### POURQUOI userdel ?

```
PROBLÈME : Employé quitte l'entreprise
SOLUTION : Supprimer le compte de manière sécurisée
QUAND : Offboarding, comptes de test obsolètes
```

### COMMENT utiliser userdel ?

```bash
userdel [OPTIONS] username
```

---

**Test 1 : Suppression simple (garde home)**

```bash
# Supprimer utilisateur (garde /home)
sudo userdel alice

# Le home reste
ls -ld /home/alice
# drwxr-xr-x 2 1001 1001 4096 Dec 31 10:00 /home/alice
#              ^    ^
#            UID  GID (orphelins)
```

---

**Test 2 : Suppression complète**

```bash
# Supprimer utilisateur ET home
sudo userdel -r alice
# -r : remove home and mail spool

# Vérifier
ls -ld /home/alice
# ls: cannot access '/home/alice': No such file or directory
```

---

**Test 3 : Forcer la suppression**

```bash
# Si l'utilisateur est connecté
sudo userdel -f alice
# -f : force (même si connecté)

# [ATTENTION] Dangereux ! Peut causer des problèmes
```

---

### QUAND supprimer vs désactiver ?

| Action | Quand | Commande |
|--------|-------|----------|
| **Désactiver** | Départ temporaire, suspension | `usermod -L` |
| **Supprimer** | Départ définitif, compte test | `userdel -r` |
| **Archiver** | Départ avec conservation données | Manuel (voir cas réel) |

---

## [RECHERCHE] Consulter les informations - id, finger, chage

**Test 1 : id - Informations d'identité**

```bash
# Informations du user courant
id

# User spécifique
id alice
# uid=1001(alice) gid=1001(alice) groups=1001(alice),27(sudo),999(docker)

# Seulement UID
id -u alice
# 1001

# Seulement GID
id -g alice
# 1001

# Tous les groupes (IDs)
id -G alice
# 1001 27 999

# Tous les groupes (noms)
id -Gn alice
# alice sudo docker
```

---

**Test 2 : chage - Informations de vieillissement du mot de passe**

```bash
# Voir toutes les infos
sudo chage -l alice

# Résultat typique :
# Last password change                    : Dec 31, 2024
# Password expires                        : Mar 31, 2025
# Password inactive                       : never
# Account expires                         : never
# Minimum number of days between password change : 0
# Maximum number of days between password change : 90
# Number of days of warning before password expires : 7

# Modifier la politique
sudo chage -M 90 -m 1 -W 7 alice
# -M : max days
# -m : min days
# -W : warning days

# Forcer changement au prochain login
sudo chage -d 0 alice
```

---

**Test 3 : getent - Interroger les bases de données système**

```bash
# Voir un utilisateur
getent passwd alice

# Voir un groupe
getent group sudo

# Voir tous les utilisateurs
getent passwd

# Utile car fonctionne aussi avec LDAP, NIS, etc.
```

---

# PARTIE 3 : GESTION DES GROUPES

## 🆕 Créer un groupe - groupadd

### POURQUOI les groupes ?

```
PROBLÈME : Besoin de donner les mêmes permissions à plusieurs users
SOLUTION : Créer un groupe et y ajouter les users
QUAND : Organiser par équipe, projet, rôle
```

### COMMENT créer un groupe ?

```bash
groupadd [OPTIONS] groupname
```

---

**Test 1 : Création basique**

```bash
# Créer groupe
sudo groupadd developers

# Vérifier
grep developers /etc/group
# developers:x:1100:

# GID automatiquement attribué
```

---

**Test 2 : Avec GID spécifique**

```bash
# Créer avec GID précis
sudo groupadd -g 2000 finance

# Vérifier
getent group finance
# finance:x:2000:
```

---

**Test 3 : Groupe système**

```bash
# Créer groupe système (GID < 1000)
sudo groupadd -r appgroup

# Vérifier
getent group appgroup
# appgroup:x:999:
```

---

## + Ajouter des membres - gpasswd, usermod

### POURQUOI plusieurs méthodes ?

```
PROBLÈME : Ajouter/retirer users des groupes
SOLUTIONS : gpasswd (recommandé) ou usermod
QUAND : Gérer les appartenances aux équipes
```

---

**Test 1 : Ajouter avec gpasswd (recommandé)**

```bash
# Ajouter alice au groupe developers
sudo gpasswd -a alice developers
# Adding user alice to group developers

# Vérifier
groups alice
# alice : alice developers

# Voir dans /etc/group
grep developers /etc/group
# developers:x:1100:alice
```

---

**Test 2 : Retirer avec gpasswd**

```bash
# Retirer alice du groupe developers
sudo gpasswd -d alice developers
# Removing user alice from group developers

# Vérifier
groups alice
# alice : alice
```

---

**Test 3 : Définir les administrateurs du groupe**

```bash
# Alice peut gérer le groupe developers
sudo gpasswd -A alice developers

# Maintenant alice peut ajouter/retirer des membres
# (sans être root)
```

---

**Test 4 : Ajouter avec usermod**

```bash
# Ajouter alice à plusieurs groupes
sudo usermod -aG developers,docker,sudo alice

# [ATTENTION] Toujours utiliser -aG (append) !
```

---

**Test 5 : Remplacer tous les membres**

```bash
# Définir les membres du groupe
sudo gpasswd -M alice,bob,charlie developers

# Remplace TOUS les membres existants
```

---

## [SUPPRIMER] Supprimer un groupe - groupdel

```bash
# Supprimer un groupe
sudo groupdel developers

# [ATTENTION] Ne peut pas supprimer si c'est le groupe primaire d'un user !

# Erreur typique :
# groupdel: cannot remove the primary group of user 'alice'

# Solution : Changer le groupe primaire d'abord
sudo usermod -g alice alice  # Groupe primaire = alice
sudo groupdel developers      # Maintenant OK
```

---

## [SYNC] Modifier un groupe - groupmod

```bash
# Renommer un groupe
sudo groupmod -n devteam developers
# developers -> devteam

# Changer le GID
sudo groupmod -g 2100 devteam
```

---

# PARTIE 4 : SUDO ET PRIVILÈGES

## [REFLEXION] POURQUOI sudo ?

```
PROBLÈME : Root est trop puissant, logs insuffisants
SOLUTION : sudo donne des privilèges limités et tracés
QUAND : Toute tâche administrative
```

**Avantages de sudo :**
- [OK] Pas besoin de partager le mot de passe root
- [OK] Traçabilité complète (qui a fait quoi)
- [OK] Permissions granulaires
- [OK] Timeout configurable
- [OK] Peut restreindre les commandes

---

## [NOTE] Fichier /etc/sudoers

### COMMENT configurer sudo ?

```
ATTENTION : TOUJOURS utiliser visudo (vérifie la syntaxe)
```

```bash
# Éditer le fichier sudoers
sudo visudo

# Utilise l'éditeur défini dans $EDITOR
# Vérifie la syntaxe avant de sauvegarder
```

---

### Format du fichier sudoers

**Syntaxe de base :**

```
user/group  host=(runas)  commands
```

**Exemples :**

```bash
# User alice peut tout faire
alice ALL=(ALL:ALL) ALL

# Groupe sudo peut tout faire (avec mot de passe)
%sudo ALL=(ALL:ALL) ALL

# User bob peut redémarrer le serveur (sans mot de passe)
bob ALL=(ALL) NOPASSWD: /usr/sbin/reboot

# Groupe developers peut utiliser apt
%developers ALL=(ALL) /usr/bin/apt, /usr/bin/apt-get
```

---

**Décomposition :**

```
alice ALL=(ALL:ALL) ALL
  v    v    v   v    v
  1    2    3   4    5

1. User ou %group
2. Host (ALL = n'importe quel host)
3. User à devenir (ALL = n'importe quel user)
4. Group à devenir (ALL = n'importe quel group)
5. Commandes autorisées (ALL = toutes)
```

---

### QUAND utiliser NOPASSWD ?

| Situation | NOPASSWD ? | Raison |
|-----------|------------|--------|
| Tâches critiques | [X] Non | Sécurité |
| Scripts automatisés | [OK] Oui (avec précaution) | Automatisation |
| Commandes spécifiques limitées | [OK] Possible | Ergonomie |
| Accès complet | [X] Non | Dangereux |

---

### Exemples pratiques de configuration

**Test 1 : Utilisateur avec accès complet**

```bash
# Ajouter à /etc/sudoers.d/alice
sudo tee /etc/sudoers.d/alice << 'EOF'
# Alice a tous les droits sudo
alice ALL=(ALL:ALL) ALL
EOF

# Tester
sudo -u alice sudo whoami
# root
```

---

**Test 2 : Groupe avec commandes spécifiques**

```bash
# Créer groupe
sudo groupadd webadmins

# Configuration sudo
sudo tee /etc/sudoers.d/webadmins << 'EOF'
# Web admins peuvent gérer nginx et apache
%webadmins ALL=(ALL) /usr/sbin/nginx, /usr/bin/systemctl * nginx, /usr/bin/systemctl * apache2
EOF

# Ajouter alice au groupe
sudo gpasswd -a alice webadmins

# Tester
sudo -u alice sudo systemctl restart nginx
# OK

sudo -u alice sudo apt update
# Erreur : commande non autorisée
```

---

**Test 3 : Alias de commandes**

```bash
sudo tee /etc/sudoers.d/aliases << 'EOF'
# Définir des alias
Cmnd_Alias NETWORKING = /usr/sbin/route, /usr/sbin/ifconfig, /usr/bin/ping, /usr/sbin/dhclient, /usr/bin/ip
Cmnd_Alias SOFTWARE = /usr/bin/apt, /usr/bin/dpkg, /usr/bin/snap
Cmnd_Alias SERVICES = /usr/bin/systemctl start, /usr/bin/systemctl stop, /usr/bin/systemctl restart, /usr/bin/systemctl status
Cmnd_Alias STORAGE = /usr/sbin/fdisk, /usr/bin/mount, /usr/bin/umount

# Utiliser les alias
%netadmins ALL=(ALL) NETWORKING
%devops ALL=(ALL) SOFTWARE, SERVICES
EOF
```

---

**Test 4 : Restrictions avec wildcards**

```bash
sudo tee /etc/sudoers.d/restricted << 'EOF'
# Alice peut seulement lire les logs
alice ALL=(ALL) NOPASSWD: /usr/bin/tail /var/log/*, /usr/bin/less /var/log/*, /usr/bin/cat /var/log/*

# Bob peut gérer seulement les services spécifiques
bob ALL=(ALL) /usr/bin/systemctl * nginx, /usr/bin/systemctl * mysql

# [ATTENTION] Attention aux wildcards : peuvent être contournés !
EOF
```

---

**Test 5 : Restrictions par host**

```bash
# Si tu gères plusieurs serveurs
sudo tee /etc/sudoers.d/multihosts << 'EOF'
# Alice a sudo seulement sur web-server-01
alice web-server-01=(ALL) ALL

# Bob a sudo sur les DB servers
bob db-server-01,db-server-02=(ALL) ALL
EOF
```

---

### Bonnes pratiques sudoers

```bash
# [OK] RECOMMANDÉ : Utiliser /etc/sudoers.d/
# Plutôt que de modifier /etc/sudoers directement

# Créer un fichier par user/groupe
sudo visudo -f /etc/sudoers.d/alice

# Nommer les fichiers sans extension ni caractères spéciaux
# [OK] BON : alice, developers, webadmins
# [X] MAUVAIS : alice.conf, developers~, web-admins.bak

# Permissions correctes
sudo chmod 440 /etc/sudoers.d/alice
sudo chown root:root /etc/sudoers.d/alice
```

---

## [RECHERCHE] Vérifier et tester sudo

```bash
# Lister les privilèges sudo de l'user courant
sudo -l

# Résultat typique :
# User alice may run the following commands:
#     (ALL : ALL) ALL

# Lister pour un autre user
sudo -U bob -l

# Tester une commande sans l'exécuter
sudo -v
# Valide le mot de passe

# Exécuter comme un autre user
sudo -u bob whoami
# bob

# Voir les logs sudo
sudo journalctl -u sudo
# Ou
sudo cat /var/log/auth.log | grep sudo
```

---

# PARTIE 5 : SÉCURITÉ ET POLITIQUES

## [SECURISE] Politiques de mot de passe

### POURQUOI des politiques strictes ?

```
PROBLÈME : Mots de passe faibles = risque de compromission
SOLUTION : Imposer complexité, expiration, historique
QUAND : Toujours en production
```

---

### COMMENT configurer avec PAM

**Fichier : /etc/pam.d/common-password**

```bash
# Voir la configuration actuelle
cat /etc/pam.d/common-password

# Installer libpam-pwquality
sudo apt install libpam-pwquality

# Configurer /etc/security/pwquality.conf
sudo tee /etc/security/pwquality.conf << 'EOF'
# Longueur minimum
minlen = 12

# Nombre minimum de classes de caractères (maj, min, chiffres, spéciaux)
minclass = 3

# Au moins 1 majuscule
ucredit = -1

# Au moins 1 minuscule
lcredit = -1

# Au moins 1 chiffre
dcredit = -1

# Au moins 1 caractère spécial
ocredit = -1

# Nombre de caractères différents du vieux mot de passe
difok = 5

# Refuser si contient le username
usercheck = 1

# Vérifier dictionnaire
dictcheck = 1
EOF

# Tester
sudo passwd testuser
# Le système va vérifier ces règles
```

---

### Expiration et vieillissement des mots de passe

```bash
# Définir politique par défaut (/etc/login.defs)
sudo tee -a /etc/login.defs << 'EOF'

# Password aging controls
PASS_MAX_DAYS   90      # Expiration après 90 jours
PASS_MIN_DAYS   1       # Minimum 1 jour entre changements
PASS_WARN_AGE   7       # Avertissement 7 jours avant
EOF

# Pour utilisateur existant
sudo chage -M 90 -m 1 -W 7 alice

# Voir la politique
sudo chage -l alice
```

---

### Verrouillage après échecs de login

```bash
# Configurer PAM pour verrouiller après 5 tentatives
sudo tee -a /etc/pam.d/common-auth << 'EOF'
# Verrouillage après 5 échecs
auth required pam_tally2.so deny=5 unlock_time=900 onerr=fail
EOF

# Voir les tentatives échouées
sudo pam_tally2 -u alice

# Réinitialiser le compteur
sudo pam_tally2 -u alice --reset
```

---

## [RECHERCHE] Audit de sécurité

**Script d'audit complet :**

```bash
cat > security-audit.sh << 'EOF'
#!/bin/bash

echo "[VERROUILLE] AUDIT DE SÉCURITÉ DES COMPTES"
echo "================================="
echo ""

# 1. Comptes sans mot de passe
echo "[ATTENTION]  Comptes sans mot de passe :"
sudo awk -F: '($2 == "") {print $1}' /etc/shadow
echo ""

# 2. Comptes avec UID 0 (en plus de root)
echo "[ATTENTION]  Comptes avec UID 0 (super-users) :"
awk -F: '($3 == 0) {print $1}' /etc/passwd
echo ""

# 3. Comptes sans home directory
echo "[ATTENTION]  Comptes sans home directory :"
awk -F: '{print $1, $6}' /etc/passwd | while read user home; do
    if [ ! -d "$home" ] && [ "$home" != "/" ]; then
        echo "  $user -> $home (missing)"
    fi
done
echo ""

# 4. Utilisateurs avec sudo
echo "[UTILISATEURS] Utilisateurs avec sudo :"
getent group sudo | cut -d: -f4 | tr ',' '\n'
echo ""

# 5. Dernières connexions
echo "[CALENDRIER] Dernières connexions :"
last -10
echo ""

# 6. Comptes dormants (pas de login depuis 90 jours)
echo "[ATTENTE] Comptes dormants (>90 jours) :"
last -t 90 | awk '{print $1}' | sort | uniq -c | sort -rn
echo ""

# 7. Mots de passe expirés
echo "[ALARM_CLOCK] Mots de passe expirés :"
sudo awk -F: '{
    if ($2 != "!" && $2 != "*") {
        system("sudo chage -l " $1 " | grep \"Password expires\" | grep -v \"never\"")
    }
}' /etc/shadow
echo ""

echo "[OK] Audit terminé"
EOF

chmod +x security-audit.sh
sudo ./security-audit.sh
```

---

# PARTIE 6 : [ENTREPRISE] 5 CAS RÉELS ENTREPRISE

## CAS 1 : [PERSONNE][CODE] ONBOARDING NOUVEAU DÉVELOPPEUR

### POURQUOI ce cas ?

```
CONTEXTE : Alice rejoint l'équipe dev backend
BESOIN :
- Compte avec accès SSH
- Accès aux repos Git
- Sudo limité pour redémarrer services dev
- Accès Docker
- Groupe developers
CONTRAINTES :
- Mot de passe fort requis
- Changement au premier login
- Expiration après 90 jours
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# onboard-developer.sh

# Variables
NEW_USER="alice"
FULL_NAME="Alice Dupont"
EMAIL="alice@company.com"
GROUPS="developers,docker"

echo "[OBJECTIF] Onboarding développeur : $NEW_USER"
echo "======================================"

# 1. Créer l'utilisateur
echo "1⃣  Création utilisateur..."
sudo useradd -m \
    -s /bin/bash \
    -c "$FULL_NAME <$EMAIL>" \
    -G $GROUPS \
    $NEW_USER

# 2. Définir politique de mot de passe
echo "2⃣  Configuration politique mot de passe..."
sudo chage -M 90 -m 1 -W 7 -I 14 $NEW_USER

# 3. Générer mot de passe temporaire
TEMP_PASS=$(openssl rand -base64 12)
echo "$NEW_USER:$TEMP_PASS" | sudo chpasswd

# 4. Forcer changement au premier login
sudo chage -d 0 $NEW_USER

# 5. Configurer sudo (redémarrage services dev)
sudo tee /etc/sudoers.d/$NEW_USER << EOF
# $NEW_USER peut redémarrer les services de dev
$NEW_USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp-dev
$NEW_USER ALL=(ALL) NOPASSWD: /usr/bin/systemctl status myapp-dev
$NEW_USER ALL=(ALL) /usr/bin/systemctl restart nginx
EOF

sudo chmod 440 /etc/sudoers.d/$NEW_USER

# 6. Créer structure de répertoires
echo "3⃣  Création structure home..."
sudo -u $NEW_USER mkdir -p /home/$NEW_USER/{projects,bin,.ssh}
sudo chmod 700 /home/$NEW_USER/.ssh

# 7. Configurer Git
echo "4⃣  Configuration Git..."
sudo -u $NEW_USER git config --global user.name "$FULL_NAME"
sudo -u $NEW_USER git config --global user.email "$EMAIL"

# 8. Ajouter clé SSH (si fournie)
if [ -f "${NEW_USER}_id_rsa.pub" ]; then
    sudo cp "${NEW_USER}_id_rsa.pub" /home/$NEW_USER/.ssh/authorized_keys
    sudo chown $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh/authorized_keys
    sudo chmod 600 /home/$NEW_USER/.ssh/authorized_keys
fi

# 9. Personnaliser .bashrc
sudo -u $NEW_USER tee -a /home/$NEW_USER/.bashrc << 'BASHRC'

# Company aliases
alias ll='ls -lah'
alias grep='grep --color=auto'

# Dev environment
export EDITOR=vim
export COMPANY_ENV=development

# Project shortcuts
alias cdproj='cd ~/projects'

BASHRC

# 10. Welcome message
sudo -u $NEW_USER tee /home/$NEW_USER/README.txt << WELCOME
Bienvenue chez ACME Corp, $FULL_NAME !

[CLE] Mot de passe temporaire : $TEMP_PASS
   [ATTENTION]  À changer à la première connexion

[DOSSIER] Structure :
   ~/projects  - Vos projets
   ~/bin       - Vos scripts
   ~/.ssh      - Clés SSH

[OUTIL] Outils configurés :
   - Git (user: $FULL_NAME, email: $EMAIL)
   - Docker (groupe docker)
   - Sudo limité (services dev)

[DOCS] Documentation :
   https://wiki.company.com/onboarding

Support : support@company.com
WELCOME

# Récapitulatif
echo ""
echo "[OK] Onboarding terminé !"
echo "======================"
echo "User       : $NEW_USER"
echo "UID        : $(id -u $NEW_USER)"
echo "Groups     : $(groups $NEW_USER)"
echo "Home       : /home/$NEW_USER"
echo "Shell      : /bin/bash"
echo "Temp Pass  : $TEMP_PASS"
echo ""
echo "[EMAIL] Envoyer les infos à : $EMAIL"
echo "[FICHIER] README : /home/$NEW_USER/README.txt"

EOF

chmod +x onboard-developer.sh
```

---

### QUAND utiliser ce script ?

```
[OK] Nouveau développeur
[OK] Contractor temporaire (avec expiration)
[OK] Stagiaire
[X] Utilisateurs non-techniques (adapter)
```

---

## CAS 2 : [WEB] SERVEUR WEB MULTI-CLIENTS

### POURQUOI ce cas ?

```
CONTEXTE : Hébergement web partagé pour 3 clients
BESOIN :
- Isolation complète entre clients
- Chaque client a son dossier /var/www/clientX
- Accès FTP/SFTP uniquement à leur dossier
- Pas d'accès shell
- Quotas disque
CONTRAINTES :
- Sécurité maximale
- Pas d'interférence entre clients
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-web-hosting-client.sh

# Variables
CLIENT_NAME="${1:-client1}"
CLIENT_DOMAIN="${2:-example.com}"
DISK_QUOTA="10G"

echo "[WEB] Configuration hébergement web : $CLIENT_NAME"
echo "==============================================="

# 1. Créer groupe client
echo "1⃣  Création groupe..."
sudo groupadd ${CLIENT_NAME}_group

# 2. Créer utilisateur (pas de shell)
echo "2⃣  Création utilisateur..."
sudo useradd -m \
    -d /home/$CLIENT_NAME \
    -s /usr/sbin/nologin \
    -g ${CLIENT_NAME}_group \
    -c "Web Hosting - $CLIENT_DOMAIN" \
    $CLIENT_NAME

# 3. Définir mot de passe
PASS=$(openssl rand -base64 16)
echo "$CLIENT_NAME:$PASS" | sudo chpasswd

# 4. Créer structure web
echo "3⃣  Structure web..."
sudo mkdir -p /var/www/$CLIENT_NAME/{public_html,logs,tmp}
sudo chown -R $CLIENT_NAME:${CLIENT_NAME}_group /var/www/$CLIENT_NAME
sudo chmod 750 /var/www/$CLIENT_NAME
sudo chmod 755 /var/www/$CLIENT_NAME/public_html
sudo chmod 770 /var/www/$CLIENT_NAME/logs
sudo chmod 770 /var/www/$CLIENT_NAME/tmp

# 5. Configurer chroot pour SFTP
echo "4⃣  Configuration SFTP chroot..."
sudo tee -a /etc/ssh/sshd_config << EOF

# Client $CLIENT_NAME - SFTP only, chrooted
Match User $CLIENT_NAME
    ChrootDirectory /var/www/$CLIENT_NAME
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no
    PasswordAuthentication yes
EOF

# 6. Ajuster permissions pour chroot
sudo chown root:root /var/www/$CLIENT_NAME
sudo chmod 755 /var/www/$CLIENT_NAME

# Créer les dossiers accessibles
sudo mkdir -p /var/www/$CLIENT_NAME/{uploads,files}
sudo chown $CLIENT_NAME:${CLIENT_NAME}_group /var/www/$CLIENT_NAME/{uploads,files}

# 7. Quotas disque (si quota activé)
if command -v setquota &> /dev/null; then
    echo "5⃣  Configuration quota ($DISK_QUOTA)..."
    # Convertir 10G en blocks (1K)
    BLOCKS=$((${DISK_QUOTA%G} * 1024 * 1024))
    sudo setquota -u $CLIENT_NAME $BLOCKS $BLOCKS 0 0 /
fi

# 8. Configurer Nginx vhost
echo "6⃣  Configuration Nginx..."
sudo tee /etc/nginx/sites-available/$CLIENT_NAME << NGINX
server {
    listen 80;
    server_name $CLIENT_DOMAIN www.$CLIENT_DOMAIN;
    
    root /var/www/$CLIENT_NAME/public_html;
    index index.html index.php;
    
    access_log /var/www/$CLIENT_NAME/logs/access.log;
    error_log /var/www/$CLIENT_NAME/logs/error.log;
    
    # PHP-FPM (pool dédié)
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php-fpm-$CLIENT_NAME.sock;
    }
    
    # Sécurité
    location ~ /\. {
        deny all;
    }
}
NGINX

sudo ln -sf /etc/nginx/sites-available/$CLIENT_NAME /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# 9. Configurer PHP-FPM pool dédié
echo "7⃣  Configuration PHP-FPM pool..."
sudo tee /etc/php/8.1/fpm/pool.d/$CLIENT_NAME.conf << PHPFPM
[$CLIENT_NAME]
user = $CLIENT_NAME
group = ${CLIENT_NAME}_group
listen = /run/php/php-fpm-$CLIENT_NAME.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

; Isolation
php_admin_value[open_basedir] = /var/www/$CLIENT_NAME:/tmp
php_admin_value[upload_tmp_dir] = /var/www/$CLIENT_NAME/tmp
PHPFPM

sudo systemctl reload php8.1-fpm

# 10. Page d'accueil par défaut
sudo -u $CLIENT_NAME tee /var/www/$CLIENT_NAME/public_html/index.html << HTML
<!DOCTYPE html>
<html>
<head>
    <title>$CLIENT_DOMAIN</title>
</head>
<body>
    <h1>Bienvenue sur $CLIENT_DOMAIN</h1>
    <p>Site en construction...</p>
</body>
</html>
HTML

# 11. Documentation client
sudo tee /var/www/$CLIENT_NAME/README.txt << README
Client : $CLIENT_NAME
Domain : $CLIENT_DOMAIN

[DOSSIER] Accès SFTP :
   Host     : $(hostname)
   Username : $CLIENT_NAME
   Password : $PASS
   Port     : 22

[DOSSIER] Structure :
   /uploads     - Téléchargements
   /files       - Fichiers
   /public_html - Site web (visible publiquement)
   /logs        - Logs (lecture seule)

[ATTENTION]  Limites :
   Quota disque : $DISK_QUOTA
   Pas d'accès shell
   Isolé dans votre dossier

[DOCS] Support : support@company.com
README

# Récapitulatif
echo ""
echo "[OK] Configuration terminée !"
echo "=========================="
echo "Client      : $CLIENT_NAME"
echo "Domain      : $CLIENT_DOMAIN"
echo "Web Root    : /var/www/$CLIENT_NAME/public_html"
echo "SFTP User   : $CLIENT_NAME"
echo "SFTP Pass   : $PASS"
echo "Quota       : $DISK_QUOTA"
echo ""
echo "[OUTIL] Services à redémarrer :"
echo "   sudo systemctl restart sshd"
echo ""
echo "[EMAIL] Envoyer /var/www/$CLIENT_NAME/README.txt au client"

```

---

### QUAND utiliser ce cas ?

```
[OK] Hébergement web partagé
[OK] Environnement mutualisé
[OK] Clients qui uploadent via FTP/SFTP
[X] Serveurs dédiés (overkill)
```

---

## CAS 3 : [ARCHIVE] SERVEUR DE BASE DE DONNÉES SÉCURISÉ

### POURQUOI ce cas ?

```
CONTEXTE : Serveur MySQL partagé par plusieurs apps
BESOIN :
- Utilisateur système dédié pour chaque application
- Accès sudo limité à mysql commands
- Pas d'accès direct à /var/lib/mysql
- Rotation automatique des logs
CONTRAINTES :
- Principe du moindre privilège
- Audit complet
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-database-app-user.sh

APP_NAME="${1:-myapp}"
DB_NAME="${APP_NAME}_db"

echo "[ARCHIVE]  Configuration utilisateur DB : $APP_NAME"
echo "==========================================="

# 1. Créer groupe database
if ! getent group dbusers > /dev/null; then
    sudo groupadd dbusers
fi

# 2. Créer utilisateur système
echo "1⃣  Création utilisateur système..."
sudo useradd -r \
    -d /opt/$APP_NAME \
    -s /bin/bash \
    -G dbusers \
    -c "Database User - $APP_NAME" \
    ${APP_NAME}_db

# 3. Créer home directory avec structure
sudo mkdir -p /opt/$APP_NAME/{bin,logs,backups,conf}
sudo chown -R ${APP_NAME}_db:dbusers /opt/$APP_NAME
sudo chmod 750 /opt/$APP_NAME

# 4. Configuration sudo (seulement mysql client + backup)
sudo tee /etc/sudoers.d/${APP_NAME}_db << EOF
# ${APP_NAME}_db peut utiliser mysql client et backup
${APP_NAME}_db ALL=(ALL) NOPASSWD: /usr/bin/mysql
${APP_NAME}_db ALL=(ALL) NOPASSWD: /usr/bin/mysqldump
${APP_NAME}_db ALL=(ALL) NOPASSWD: /usr/bin/systemctl status mysql
EOF

sudo chmod 440 /etc/sudoers.d/${APP_NAME}_db

# 5. Créer config MySQL
MYSQL_PASS=$(openssl rand -base64 16)

sudo -u ${APP_NAME}_db tee /opt/$APP_NAME/conf/my.cnf << MYCNF
[client]
user=${APP_NAME}_user
password=$MYSQL_PASS
database=$DB_NAME
socket=/var/run/mysqld/mysqld.sock
MYCNF

sudo chmod 600 /opt/$APP_NAME/conf/my.cnf

# 6. Créer utilisateur MySQL et DB
sudo mysql << MYSQL
CREATE DATABASE IF NOT EXISTS $DB_NAME;
CREATE USER IF NOT EXISTS '${APP_NAME}_user'@'localhost' IDENTIFIED BY '$MYSQL_PASS';
GRANT ALL PRIVILEGES ON $DB_NAME.* TO '${APP_NAME}_user'@'localhost';
FLUSH PRIVILEGES;
MYSQL

# 7. Script de backup
sudo -u ${APP_NAME}_db tee /opt/$APP_NAME/bin/backup.sh << 'BACKUP'
#!/bin/bash
BACKUP_DIR="/opt/APP_NAME/backups"
DATE=$(date +%Y%m%d_%H%M%S)
CONFIG="/opt/APP_NAME/conf/my.cnf"

mysqldump --defaults-file=$CONFIG --single-transaction --quick \
    > $BACKUP_DIR/backup_${DATE}.sql

# Compression
gzip $BACKUP_DIR/backup_${DATE}.sql

# Rotation (garder 7 jours)
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +7 -delete

echo "Backup terminé : backup_${DATE}.sql.gz"
BACKUP

# Remplacer APP_NAME
sudo sed -i "s/APP_NAME/$APP_NAME/g" /opt/$APP_NAME/bin/backup.sh
sudo chmod 750 /opt/$APP_NAME/bin/backup.sh

# 8. Cron pour backup quotidien
echo "2⃣  Configuration backup automatique..."
sudo -u ${APP_NAME}_db crontab -l 2>/dev/null | { 
    cat
    echo "0 2 * * * /opt/$APP_NAME/bin/backup.sh >> /opt/$APP_NAME/logs/backup.log 2>&1"
} | sudo -u ${APP_NAME}_db crontab -

# 9. Script de connexion rapide
sudo -u ${APP_NAME}_db tee /opt/$APP_NAME/bin/connect.sh << 'CONNECT'
#!/bin/bash
mysql --defaults-file=/opt/APP_NAME/conf/my.cnf "$@"
CONNECT

sudo sed -i "s/APP_NAME/$APP_NAME/g" /opt/$APP_NAME/bin/connect.sh
sudo chmod 750 /opt/$APP_NAME/bin/connect.sh

# 10. Documentation
sudo -u ${APP_NAME}_db tee /opt/$APP_NAME/README.md << README
# Application Database: $APP_NAME

## Informations
- Database: $DB_NAME
- System User: ${APP_NAME}_db
- MySQL User: ${APP_NAME}_user

## Scripts disponibles
\`\`\`bash
# Se connecter à la DB
/opt/$APP_NAME/bin/connect.sh

# Backup manuel
/opt/$APP_NAME/bin/backup.sh

# Voir les backups
ls -lh /opt/$APP_NAME/backups/
\`\`\`

## Commandes utiles
\`\`\`bash
# Devenir l'utilisateur
sudo su - ${APP_NAME}_db

# Voir les tables
mysql --defaults-file=~/conf/my.cnf -e "SHOW TABLES;"

# Restaurer backup
gunzip < backups/backup_YYYYMMDD_HHMMSS.sql.gz | \\
    mysql --defaults-file=~/conf/my.cnf
\`\`\`

## Sécurité
- Config MySQL dans ~/conf/my.cnf (600)
- Backups quotidiens à 2h du matin
- Retention: 7 jours
- Pas d'accès direct à /var/lib/mysql
README

# Récapitulatif
echo ""
echo "[OK] Configuration terminée !"
echo "=========================="
echo "App          : $APP_NAME"
echo "System User  : ${APP_NAME}_db"
echo "MySQL User   : ${APP_NAME}_user"
echo "Database     : $DB_NAME"
echo "Home         : /opt/$APP_NAME"
echo ""
echo "[NOTE] Pour utiliser :"
echo "   sudo su - ${APP_NAME}_db"
echo "   ./bin/connect.sh"
echo ""
echo "[DOCS] Documentation : /opt/$APP_NAME/README.md"

```

---

### QUAND utiliser ce cas ?

```
[OK] Applications avec DB dédiée
[OK] Environnement multi-tenant
[OK] Sécurisation maximale
[X] Base de données personnelles
```

---

## CAS 4 : [SYNC] ROTATION D'EMPLOYÉ (OFFBOARDING)

### POURQUOI ce cas ?

```
CONTEXTE : Bob quitte l'entreprise
BESOIN :
- Désactiver accès immédiatement
- Archiver home directory
- Transférer ownership de fichiers
- Conserver pour audit (90 jours)
- Documentation complète
CONTRAINTES :
- Conformité légale
- Traçabilité
- Récupération possible
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# offboard-employee.sh

OLD_USER="${1}"
NEW_OWNER="${2:-admin}"
ARCHIVE_DIR="/archives/users"

if [ -z "$OLD_USER" ]; then
    echo "Usage: $0 <username> [new_owner]"
    exit 1
fi

echo "[VERROUILLE] Offboarding utilisateur : $OLD_USER"
echo "======================================"

# Vérifier que l'utilisateur existe
if ! id "$OLD_USER" &>/dev/null; then
    echo "[X] Utilisateur $OLD_USER n'existe pas"
    exit 1
fi

# Date de l'offboarding
OFFBOARD_DATE=$(date +%Y%m%d)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# 1. Vérouiller le compte immédiatement
echo "1⃣  Verrouillage du compte..."
sudo usermod -L $OLD_USER
sudo usermod -s /usr/sbin/nologin $OLD_USER
sudo chage -E 0 $OLD_USER

echo "   [OK] Compte verrouillé"

# 2. Tuer toutes les sessions actives
echo "2⃣  Fermeture sessions actives..."
sudo pkill -KILL -u $OLD_USER
echo "   [OK] Sessions fermées"

# 3. Supprimer des groupes sensibles
echo "3⃣  Retrait des groupes privilégiés..."
for group in sudo docker wheel admin; do
    if groups $OLD_USER | grep -q $group; then
        sudo gpasswd -d $OLD_USER $group 2>/dev/null
        echo "   - Retiré de $group"
    fi
done

# 4. Désactiver clés SSH
echo "4⃣  Désactivation clés SSH..."
if [ -d "/home/$OLD_USER/.ssh" ]; then
    sudo mv /home/$OLD_USER/.ssh /home/$OLD_USER/.ssh.disabled_$TIMESTAMP
    echo "   [OK] Clés SSH désactivées"
fi

# 5. Désactiver sudo
echo "5⃣  Suppression privilèges sudo..."
if [ -f "/etc/sudoers.d/$OLD_USER" ]; then
    sudo mv /etc/sudoers.d/$OLD_USER /etc/sudoers.d/${OLD_USER}.disabled_$TIMESTAMP
    echo "   [OK] Sudo désactivé"
fi

# 6. Lister les fichiers appartenant à l'utilisateur
echo "6⃣  Recherche fichiers propriétaire..."
FILELIST="/tmp/${OLD_USER}_files_${TIMESTAMP}.txt"
sudo find / -user $OLD_USER -not -path "/proc/*" -not -path "/sys/*" \
    2>/dev/null > $FILELIST
FILECOUNT=$(wc -l < $FILELIST)
echo "   [FICHIER] $FILECOUNT fichiers trouvés"

# 7. Archiver le home directory
echo "7⃣  Archivage home directory..."
sudo mkdir -p $ARCHIVE_DIR
ARCHIVE_FILE="$ARCHIVE_DIR/${OLD_USER}_${OFFBOARD_DATE}.tar.gz"

sudo tar -czf "$ARCHIVE_FILE" \
    -C /home \
    --exclude=".cache" \
    --exclude=".local/share/Trash" \
    $OLD_USER

ARCHIVE_SIZE=$(du -h "$ARCHIVE_FILE" | cut -f1)
echo "   [OK] Archive créée : $ARCHIVE_FILE ($ARCHIVE_SIZE)"

# 8. Générer checksum
sudo sha256sum "$ARCHIVE_FILE" > "${ARCHIVE_FILE}.sha256"

# 9. Transférer ownership des fichiers critiques
echo "8⃣  Transfert ownership fichiers partagés..."
# Fichiers dans /var/www, /opt, etc.
for dir in /var/www /opt /srv; do
    if [ -d "$dir" ]; then
        sudo find "$dir" -user $OLD_USER -exec chown $NEW_OWNER:$NEW_OWNER {} + 2>/dev/null
    fi
done

# 10. Créer rapport d'offboarding
REPORT_FILE="$ARCHIVE_DIR/${OLD_USER}_offboarding_report_${TIMESTAMP}.txt"
sudo tee "$REPORT_FILE" << REPORT
╔══════════════════════════════════════════════════════════╗
║         RAPPORT D'OFFBOARDING                             ║
╚══════════════════════════════════════════════════════════╝

Utilisateur     : $OLD_USER
Date offboarding: $(date)
Exécuté par     : $(whoami)

═══════════════════════════════════════════════════════════

[LISTE] INFORMATIONS COMPTE

UID/GID         : $(id -u $OLD_USER)/$(id -g $OLD_USER)
Groupes (avant) : $(id -Gn $OLD_USER 2>/dev/null || echo "N/A")
Home directory  : /home/$OLD_USER
Shell           : $(getent passwd $OLD_USER | cut -d: -f7)

═══════════════════════════════════════════════════════════

[VERROUILLE] ACTIONS EFFECTUÉES

[OK] Compte verrouillé (usermod -L)
[OK] Shell changé vers /usr/sbin/nologin
[OK] Expiration compte définie à EPOCH 0
[OK] Sessions actives terminées (pkill)
[OK] Retiré des groupes sudo, docker, wheel, admin
[OK] Clés SSH désactivées
[OK] Privilèges sudo supprimés
[OK] Home directory archivé

═══════════════════════════════════════════════════════════

[PACKAGE] ARCHIVE

Fichier : $ARCHIVE_FILE
Taille  : $ARCHIVE_SIZE
SHA256  : $(cat ${ARCHIVE_FILE}.sha256)

═══════════════════════════════════════════════════════════

[GRAPHIQUE] FICHIERS

Total fichiers propriétaire : $FILECOUNT
Liste complète              : $FILELIST

Top 10 plus gros fichiers :
$(sudo find /home/$OLD_USER -type f -exec du -h {} + 2>/dev/null | sort -rh | head -10)

═══════════════════════════════════════════════════════════

[SYNC] OWNERSHIP TRANSFÉRÉ

Fichiers dans /var/www, /opt, /srv transférés à : $NEW_OWNER

═══════════════════════════════════════════════════════════

[ALARM_CLOCK] RÉTENTION

Archive conservée pour : 90 jours
Suppression prévue le   : $(date -d "+90 days" +%Y-%m-%d)

═══════════════════════════════════════════════════════════

[NOTE] NOTES

Cron jobs de l'utilisateur :
$(sudo crontab -u $OLD_USER -l 2>/dev/null || echo "Aucun")

Dernières connexions :
$(last $OLD_USER | head -5)

═══════════════════════════════════════════════════════════

[ATTENTION]  ACTIONS RECOMMANDÉES

[ ] Révoquer accès applications tierces (GitHub, GitLab, etc.)
[ ] Révoquer clés API
[ ] Changer mots de passe comptes partagés si nécessaires
[ ] Informer équipe du départ
[ ] Réassigner tickets/tâches en cours

═══════════════════════════════════════════════════════════

Fin du rapport
REPORT

echo "   [OK] Rapport généré : $REPORT_FILE"

# 11. Créer tâche de suppression automatique (90 jours)
echo "9⃣  Planification suppression automatique..."
sudo tee "/etc/cron.d/${OLD_USER}_cleanup" << CRON
# Suppression automatique compte $OLD_USER après 90 jours
# Créé le $(date)

0 2 $(date -d "+90 days" +"%d %m") * root userdel -r $OLD_USER 2>/dev/null && rm /etc/cron.d/${OLD_USER}_cleanup
CRON

echo "   [ALARM_CLOCK] Suppression programmée : $(date -d "+90 days" +%Y-%m-%d)"

# 12. Notification (optionnel - configurer mail)
# echo "Offboarding $OLD_USER terminé" | mail -s "Offboarding $OLD_USER" admin@company.com

# Récapitulatif final
echo ""
echo "[OK] OFFBOARDING TERMINÉ"
echo "======================"
echo ""
echo "[DOSSIER] Archive : $ARCHIVE_FILE ($ARCHIVE_SIZE)"
echo "[FICHIER] Rapport : $REPORT_FILE"
echo "[LISTE] Fichiers: $FILELIST"
echo ""
echo "[VERROUILLE] Le compte est VERROUILLÉ"
echo "[ALARM_CLOCK] Suppression automatique dans 90 jours"
echo ""
echo "[ATTENTION]  Actions manuelles restantes :"
echo "   - Révoquer accès externes (GitHub, etc.)"
echo "   - Changer MDP comptes partagés si nécessaires"
echo "   - Réassigner tâches en cours"
echo ""
echo "[EMAIL] Rapport envoyé à admin@company.com"

```

---

### QUAND utiliser ce cas ?

```
[OK] Départ employé (démission, licenciement)
[OK] Fin contrat contractor
[OK] Compte compromis (incident sécurité)
[X] Congé temporaire (utiliser usermod -L seulement)
```

---

## CAS 5 : [RECHERCHE] AUDIT DE SÉCURITÉ COMPLET

### POURQUOI ce cas ?

```
CONTEXTE : Audit sécurité annuel
BESOIN :
- Identifier comptes dormants
- Trouver privilèges excessifs
- Vérifier conformité politiques
- Détecter anomalies
- Rapport exécutif
CONTRAINTES :
- Sans impact sur production
- Rapport détaillé + résumé
- Actions recommandées
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# security-audit-complete.sh

REPORT_DIR="/var/log/security-audit"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="$REPORT_DIR/audit_$TIMESTAMP.html"

sudo mkdir -p $REPORT_DIR

echo "[RECHERCHE] AUDIT DE SÉCURITÉ COMPLET"
echo "============================"
echo "Génération du rapport..."

# Fonctions utilitaires
count_issue() {
    echo "$1" | grep -v "^$" | wc -l
}

# Début du rapport HTML
cat > $REPORT_FILE << 'HTML_START'
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Audit de Sécurité</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
        h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
        h2 { color: #34495e; margin-top: 30px; border-left: 4px solid #3498db; padding-left: 10px; }
        .summary { background: #ecf0f1; padding: 15px; border-radius: 5px; margin: 20px 0; }
        .critical { color: #e74c3c; font-weight: bold; }
        .warning { color: #f39c12; font-weight: bold; }
        .ok { color: #27ae60; font-weight: bold; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th { background: #34495e; color: white; padding: 10px; text-align: left; }
        td { padding: 10px; border-bottom: 1px solid #ddd; }
        tr:hover { background: #f8f9fa; }
        .metric { display: inline-block; margin: 10px; padding: 15px; background: #3498db; color: white; border-radius: 5px; min-width: 150px; text-align: center; }
        .metric-value { font-size: 2em; font-weight: bold; }
        .metric-label { font-size: 0.9em; }
        pre { background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 5px; overflow-x: auto; }
        .recommendation { background: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; margin: 10px 0; }
    </style>
</head>
<body>
<div class="container">
<h1>[VERROUILLE] Audit de Sécurité des Utilisateurs et Groupes</h1>
<p><strong>Date :</strong> $(date)<br>
<strong>Serveur :</strong> $(hostname)<br>
<strong>Exécuté par :</strong> $(whoami)</p>
HTML_START

# 1. Résumé exécutif
echo "<h2>[GRAPHIQUE] Résumé Exécutif</h2>" >> $REPORT_FILE
echo "<div class='summary'>" >> $REPORT_FILE

TOTAL_USERS=$(awk -F: '$3 >= 1000 && $3 < 65534' /etc/passwd | wc -l)
TOTAL_SUDO=$(getent group sudo | cut -d: -f4 | tr ',' '\n' | wc -l)
LOCKED_ACCOUNTS=$(sudo awk -F: '$2 ~ /^!/ {print $1}' /etc/shadow | wc -l)

echo "<div class='metric'><div class='metric-value'>$TOTAL_USERS</div><div class='metric-label'>Utilisateurs</div></div>" >> $REPORT_FILE
echo "<div class='metric'><div class='metric-value'>$TOTAL_SUDO</div><div class='metric-label'>Accès Sudo</div></div>" >> $REPORT_FILE
echo "<div class='metric'><div class='metric-value'>$LOCKED_ACCOUNTS</div><div class='metric-label'>Comptes Verrouillés</div></div>" >> $REPORT_FILE

echo "</div>" >> $REPORT_FILE

# 2. Comptes critiques
echo "<h2>[ALERTE] Comptes Critiques</h2>" >> $REPORT_FILE

echo "<h3>Comptes avec UID 0 (Super-utilisateurs)</h3>" >> $REPORT_FILE
UID_ZERO=$(awk -F: '$3 == 0' /etc/passwd)
if [ $(echo "$UID_ZERO" | wc -l) -gt 1 ]; then
    echo "<p class='critical'>[ATTENTION] CRITIQUE : Plusieurs comptes avec UID 0 détectés !</p>" >> $REPORT_FILE
else
    echo "<p class='ok'>[OK] OK : Seulement root a UID 0</p>" >> $REPORT_FILE
fi
echo "<pre>$UID_ZERO</pre>" >> $REPORT_FILE

# 3. Comptes sans mot de passe
echo "<h3>Comptes sans Mot de Passe</h3>" >> $REPORT_FILE
NO_PASS=$(sudo awk -F: '($2 == "" || $2 == "*") && $1 != "root" {print $1}' /etc/shadow)
if [ -n "$NO_PASS" ]; then
    echo "<p class='critical'>[ATTENTION] $(count_issue "$NO_PASS") comptes sans mot de passe :</p>" >> $REPORT_FILE
    echo "<pre>$NO_PASS</pre>" >> $REPORT_FILE
else
    echo "<p class='ok'>[OK] Aucun compte sans mot de passe</p>" >> $REPORT_FILE
fi

# 4. Privilèges sudo
echo "<h2>[CLE] Privilèges Sudo</h2>" >> $REPORT_FILE
echo "<table>" >> $REPORT_FILE
echo "<tr><th>Utilisateur</th><th>Groupes</th><th>Sudo Direct</th></tr>" >> $REPORT_FILE

SUDO_USERS=$(getent group sudo | cut -d: -f4 | tr ',' '\n')
for user in $SUDO_USERS; do
    GROUPS=$(groups $user | cut -d: -f2)
    SUDO_FILE=""
    [ -f "/etc/sudoers.d/$user" ] && SUDO_FILE="[OK]"
    echo "<tr><td>$user</td><td>$GROUPS</td><td>$SUDO_FILE</td></tr>" >> $REPORT_FILE
done

echo "</table>" >> $REPORT_FILE

# 5. Comptes dormants
echo "<h2>[ATTENTE] Comptes Dormants (>90 jours sans connexion)</h2>" >> $REPORT_FILE

DORMANT=""
for user in $(awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd); do
    LAST_LOGIN=$(last -n 1 $user 2>/dev/null | head -1)
    if echo "$LAST_LOGIN" | grep -q "Never logged in\|wtmp begins"; then
        DORMANT="$DORMANT\n$user: Jamais connecté"
    fi
done

if [ -n "$DORMANT" ]; then
    echo "<p class='warning'>[ATTENTION] $(echo -e "$DORMANT" | grep -v "^$" | wc -l) comptes dormants détectés</p>" >> $REPORT_FILE
    echo "<pre>$(echo -e "$DORMANT")</pre>" >> $REPORT_FILE
    echo "<div class='recommendation'><strong>Recommandation :</strong> Désactiver ou supprimer ces comptes</div>" >> $REPORT_FILE
else
    echo "<p class='ok'>[OK] Aucun compte dormant</p>" >> $REPORT_FILE
fi

# 6. Politiques de mot de passe
echo "<h2>[SECURISE] Politiques de Mot de Passe</h2>" >> $REPORT_FILE
echo "<table>" >> $REPORT_FILE
echo "<tr><th>Utilisateur</th><th>Dernier Changement</th><th>Max Days</th><th>Expiration</th></tr>" >> $REPORT_FILE

for user in $(awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd); do
    PASS_INFO=$(sudo chage -l $user 2>/dev/null)
    LAST_CHANGE=$(echo "$PASS_INFO" | grep "Last password change" | cut -d: -f2 | xargs)
    MAX_DAYS=$(echo "$PASS_INFO" | grep "Maximum number" | cut -d: -f2 | xargs)
    EXPIRE=$(echo "$PASS_INFO" | grep "Account expires" | cut -d: -f2 | xargs)
    
    echo "<tr><td>$user</td><td>$LAST_CHANGE</td><td>$MAX_DAYS</td><td>$EXPIRE</td></tr>" >> $REPORT_FILE
done

echo "</table>" >> $REPORT_FILE

# 7. Groupes critiques
echo "<h2>[UTILISATEURS] Groupes et Membres</h2>" >> $REPORT_FILE
echo "<table>" >> $REPORT_FILE
echo "<tr><th>Groupe</th><th>GID</th><th>Membres</th></tr>" >> $REPORT_FILE

for group in sudo docker wheel admin; do
    if getent group $group > /dev/null; then
        GID=$(getent group $group | cut -d: -f3)
        MEMBERS=$(getent group $group | cut -d: -f4)
        echo "<tr><td>$group</td><td>$GID</td><td>${MEMBERS:-Aucun}</td></tr>" >> $REPORT_FILE
    fi
done

echo "</table>" >> $REPORT_FILE

# 8. Fichiers SUID/SGID
echo "<h2>[CONFIG] Fichiers SUID/SGID</h2>" >> $REPORT_FILE

SUID_FILES=$(sudo find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | wc -l)
echo "<p>Total fichiers SUID/SGID : <strong>$SUID_FILES</strong></p>" >> $REPORT_FILE

echo "<h3>Top 10 fichiers SUID</h3>" >> $REPORT_FILE
echo "<pre>" >> $REPORT_FILE
sudo find / -type f -perm -4000 2>/dev/null | head -10 >> $REPORT_FILE
echo "</pre>" >> $REPORT_FILE

# 9. Clés SSH
echo "<h2>[CLE] Clés SSH</h2>" >> $REPORT_FILE
echo "<table>" >> $REPORT_FILE
echo "<tr><th>Utilisateur</th><th>Clés Autorisées</th><th>Clés Privées</th></tr>" >> $REPORT_FILE

for user in $(awk -F: '$3 >= 1000 && $3 < 65534 {print $1}' /etc/passwd); do
    HOME_DIR=$(getent passwd $user | cut -d: -f6)
    AUTH_KEYS=0
    PRIV_KEYS=0
    
    [ -f "$HOME_DIR/.ssh/authorized_keys" ] && AUTH_KEYS=$(wc -l < "$HOME_DIR/.ssh/authorized_keys")
    PRIV_KEYS=$(find "$HOME_DIR/.ssh" -name "id_*" -not -name "*.pub" 2>/dev/null | wc -l)
    
    echo "<tr><td>$user</td><td>$AUTH_KEYS</td><td>$PRIV_KEYS</td></tr>" >> $REPORT_FILE
done

echo "</table>" >> $REPORT_FILE

# 10. Recommandations
echo "<h2>[LISTE] Recommandations</h2>" >> $REPORT_FILE

cat >> $REPORT_FILE << 'RECOMMENDATIONS'
<div class="recommendation">
<h3>Actions Prioritaires</h3>
<ol>
<li>Désactiver ou supprimer les comptes dormants (>90 jours)</li>
<li>Vérifier les comptes avec UID 0 (ne doit être que root)</li>
<li>Auditer les fichiers SUID/SGID inhabituels</li>
<li>Implémenter politique de rotation des mots de passe (90 jours)</li>
<li>Restreindre les privilèges sudo au strict nécessaire</li>
</ol>
</div>

<div class="recommendation">
<h3>Améliorations Long Terme</h3>
<ul>
<li>Implémenter authentification à deux facteurs (2FA)</li>
<li>Centraliser gestion utilisateurs (LDAP/Active Directory)</li>
<li>Automatiser revue trimestrielle des comptes</li>
<li>Mettre en place alertes pour création comptes UID 0</li>
<li>Logger toutes les commandes sudo (auditd)</li>
</ul>
</div>
RECOMMENDATIONS

# Conclusion
cat >> $REPORT_FILE << 'HTML_END'
<h2>[OK] Fin du Rapport</h2>
<p>Ce rapport a été généré automatiquement. Vérifier manuellement les points critiques.</p>
<p><em>Prochaine révision recommandée : 90 jours</em></p>
</div>
</body>
</html>
HTML_END

# Générer aussi version texte
TEXT_REPORT="${REPORT_FILE%.html}.txt"
lynx -dump -nolist $REPORT_FILE > $TEXT_REPORT 2>/dev/null || \
    w3m -dump $REPORT_FILE > $TEXT_REPORT 2>/dev/null || \
    echo "Version texte non générée (lynx ou w3m requis)" > $TEXT_REPORT

# Récapitulatif
echo ""
echo "[OK] AUDIT TERMINÉ"
echo "================"
echo "Rapport HTML : $REPORT_FILE"
echo "Rapport TXT  : $TEXT_REPORT"
echo ""
echo "[WEB] Ouvrir avec : firefox $REPORT_FILE"
echo ""
echo "Points d'attention :"
grep -i "critique\|warning" $TEXT_REPORT | head -5

```

---

### QUAND utiliser ce cas ?

```
[OK] Audit annuel/trimestriel
[OK] Avant certification (ISO 27001, SOC 2)
[OK] Après incident de sécurité
[OK] Changement d'équipe sysadmin
[X] Monitoring quotidien (trop lourd)
```

---

# PARTIE 7 : AUTOMATISATION

## Scripts de gestion de masse

```bash
#!/bin/bash
# bulk-user-management.sh

CSV_FILE="${1:-users.csv}"

# Format CSV : username,full_name,email,groups,expiration
# Exemple : alice,Alice Dupont,alice@company.com,developers:docker,2025-12-31

while IFS=, read -r username fullname email groups expiration; do
    echo "Traitement : $username"
    
    # Créer utilisateur
    sudo useradd -m -s /bin/bash -c "$fullname <$email>" $username
    
    # Ajouter aux groupes
    IFS=':' read -ra GROUP_ARRAY <<< "$groups"
    for group in "${GROUP_ARRAY[@]}"; do
        sudo usermod -aG "$group" $username
    done
    
    # Expiration
    [ -n "$expiration" ] && sudo usermod -e "$expiration" $username
    
    # Mot de passe temporaire
    TEMP_PASS=$(openssl rand -base64 12)
    echo "$username:$TEMP_PASS" | sudo chpasswd
    sudo chage -d 0 $username
    
    echo "$username créé. Mot de passe : $TEMP_PASS"
    
done < "$CSV_FILE"
```

---

## [COURS] CONCLUSION

**[OK] Félicitations ! Tu maîtrises maintenant COMPLÈTEMENT la gestion des utilisateurs et groupes !**

**Ce que tu as appris :**
- Architecture complète (UID, GID, types d'utilisateurs)
- Tous les fichiers (/etc/passwd, shadow, group, sudoers)
- Toutes les commandes (useradd, usermod, userdel, etc.)
- Gestion sudo avancée
- Politiques de sécurité
- **5 cas réels d'entreprise complets**
- Automatisation
- Audit et conformité

**Compétences acquises :**
- [OK] Gestion utilisateurs (niveau expert)
- [OK] Configuration sudo granulaire
- [OK] Sécurisation des comptes
- [OK] Onboarding/Offboarding
- [OK] Audit de sécurité

**Temps moyen :** 3-4h

---

Tu veux continuer avec la **Gestion de paquets (apt, yum, snap)** ?

# [DOCS] SECTION 11 : GESTION DE PAQUETS

# [JAUNE] GESTION COMPLÈTE DES PAQUETS LINUX (INTERMÉDIAIRE/AVANCÉ)

## [LISTE] INTRODUCTION

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Installer et mettre à jour des logiciels
- Gérer les dépendances complexes
- Maintenir la cohérence du système
- Appliquer les correctifs de sécurité
- Gérer des dépôts personnalisés
- Automatiser les déploiements
- Résoudre les conflits de versions
- Gérer plusieurs distributions (Debian, RHEL, Arch)

### Méthodologie : POURQUOI / COMMENT / QUAND

Comme précédemment, chaque concept sera expliqué selon :
- **POURQUOI** : Raison d'être, problème résolu
- **COMMENT** : Mise en œuvre technique détaillée
- **QUAND** : Cas d'usage, timing optimal

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cette section, tu sauras :
- [OK] Comprendre les gestionnaires de paquets
- [OK] Maîtriser APT (Debian/Ubuntu)
- [OK] Maîtriser YUM/DNF (RHEL/CentOS/Fedora)
- [OK] Utiliser pacman (Arch Linux)
- [OK] Gérer Snap, Flatpak, AppImage
- [OK] Créer et gérer des dépôts
- [OK] Compiler depuis les sources
- [OK] Implémenter 5 cas réels d'entreprise
- [OK] Automatiser la gestion de paquets
- [OK] Résoudre les problèmes courants
- [OK] Appliquer les bonnes pratiques

---

## [DOCS] TABLE DES MATIÈRES

1. **Concepts fondamentaux**
2. **APT - Debian/Ubuntu**
3. **YUM/DNF - RHEL/CentOS/Fedora**
4. **Pacman - Arch Linux**
5. **Formats universels (Snap, Flatpak, AppImage)**
6. **Gestion des dépôts**
7. **Compilation depuis sources**
8. **[ENTREPRISE] 5 CAS RÉELS ENTREPRISE**
9. **Automatisation**
10. **Troubleshooting**
11. **Bonnes pratiques**

---

# PARTIE 1 : CONCEPTS FONDAMENTAUX

## [REFLEXION] POURQUOI les gestionnaires de paquets ?

### Problèmes SANS gestionnaire de paquets

```
SCÉNARIO : Années 1990, installation manuelle

1. Télécharger le tarball (.tar.gz)
2. Extraire : tar -xzf logiciel.tar.gz
3. Lire README pour dépendances
4. Installer dépendances (répéter processus)
5. ./configure
6. make
7. make install
8. Espérer que ça marche...

PROBLÈMES :
[ROUGE] Dependency hell (enfer des dépendances)
[ROUGE] Pas de désinstallation propre
[ROUGE] Conflits de versions
[ROUGE] Pas de mises à jour automatiques
[ROUGE] Fichiers éparpillés partout
[ROUGE] Impossible de savoir ce qui est installé
[ROUGE] Sécurité : pas de vérification d'intégrité
```

---

### Solutions apportées par les gestionnaires

```
AVEC gestionnaire de paquets :

[OK] Installation en une commande
[OK] Résolution automatique des dépendances
[OK] Mises à jour centralisées
[OK] Désinstallation propre
[OK] Gestion des versions
[OK] Signatures cryptographiques
[OK] Rollback possible
[OK] Base de données centralisée
```

---

## [PACKAGE] Qu'est-ce qu'un paquet ?

### POURQUOI empaqueter ?

```
PROBLÈME : Distribution et installation complexes
SOLUTION : Paquet = Archive + Métadonnées
QUAND : Toujours pour distribution logicielle
```

**Anatomie d'un paquet :**

```bash
# Créer environnement de démonstration
mkdir -p ~/packages-exercise
cd ~/packages-exercise
```

**Un paquet contient :**

```
monapp_1.0.deb
│
├── Données (fichiers du logiciel)
│   ├── /usr/bin/monapp
│   ├── /etc/monapp/config.conf
│   └── /usr/share/doc/monapp/README
│
└── Métadonnées
    ├── Nom et version
    ├── Description
    ├── Dépendances
    ├── Scripts (pre/post install/remove)
    ├── Checksums
    └── Signature GPG
```

---

### Types de paquets par distribution

| Distribution | Format | Gestionnaire Bas-niveau | Gestionnaire Haut-niveau |
|--------------|--------|-------------------------|--------------------------|
| **Debian/Ubuntu** | `.deb` | `dpkg` | `apt`, `apt-get` |
| **RHEL/CentOS/Fedora** | `.rpm` | `rpm` | `yum`, `dnf` |
| **Arch Linux** | `.pkg.tar.zst` | `pacman` | `pacman` |
| **openSUSE** | `.rpm` | `rpm` | `zypper` |
| **Alpine Linux** | `.apk` | `apk` | `apk` |

---

### Bas-niveau vs Haut-niveau

```
POURQUOI deux niveaux ?

BAS-NIVEAU (dpkg, rpm) :
- Installe/supprime paquets locaux
- Ne gère PAS les dépendances
- Opérations basiques

HAUT-NIVEAU (apt, yum) :
- Télécharge depuis dépôts
- Résout les dépendances automatiquement
- Interface utilisateur conviviale
- Gestion de cache

ANALOGIE :
Bas-niveau = Tournevis (outil précis, manuel)
Haut-niveau = Perceuse électrique (automatisé, puissant)
```

---

## [SYNC] Cycle de vie d'un paquet

```
CRÉATION -> DÉPÔT -> INSTALLATION -> UTILISATION -> MISE À JOUR -> SUPPRESSION
```

**Exemple visuel :**

```bash
# Script de démonstration du cycle
cat > package-lifecycle-demo.sh << 'EOF'
#!/bin/bash

echo "[PACKAGE] CYCLE DE VIE D'UN PAQUET"
echo "==========================="
echo ""

# 1. CRÉATION
echo "1⃣  CRÉATION (mainteneur de paquet)"
echo "   - Développeur crée le logiciel"
echo "   - Empaquetage (.deb, .rpm, etc.)"
echo "   - Signature GPG"
echo ""

# 2. PUBLICATION
echo "2⃣  PUBLICATION (dépôt)"
echo "   - Upload vers dépôt officiel/PPA"
echo "   - Indexation"
echo "   - Distribution CDN"
echo ""

# 3. DÉCOUVERTE
echo "3⃣  DÉCOUVERTE (utilisateur)"
echo "   $ apt update"
echo "   - Télécharge listes de paquets"
echo "   - Met à jour le cache local"
echo ""

# 4. INSTALLATION
echo "4⃣  INSTALLATION"
echo "   $ apt install nginx"
echo "   - Vérification dépendances"
echo "   - Téléchargement paquet + dépendances"
echo "   - Vérification signature"
echo "   - Extraction fichiers"
echo "   - Exécution scripts post-install"
echo "   - Configuration"
echo ""

# 5. UTILISATION
echo "5⃣  UTILISATION"
echo "   $ systemctl start nginx"
echo "   - Le logiciel tourne"
echo ""

# 6. MISE À JOUR
echo "6⃣  MISE À JOUR"
echo "   $ apt update && apt upgrade"
echo "   - Nouvelle version disponible"
echo "   - Arrêt service (si nécessaire)"
echo "   - Remplacement fichiers"
echo "   - Migration config"
echo "   - Redémarrage service"
echo ""

# 7. SUPPRESSION
echo "7⃣  SUPPRESSION"
echo "   $ apt remove nginx"
echo "   - Arrêt service"
echo "   - Suppression fichiers"
echo "   - Conservation config (remove)"
echo "   OU"
echo "   $ apt purge nginx"
echo "   - Suppression complète (+ config)"

EOF

chmod +x package-lifecycle-demo.sh
./package-lifecycle-demo.sh
```

---

# PARTIE 2 : APT - DEBIAN/UBUNTU

## [OBJECTIF] POURQUOI APT ?

```
CONTEXTE : Debian (1993) puis Ubuntu (2004)
PROBLÈME : dpkg ne gère pas les dépendances
SOLUTION : APT (Advanced Package Tool)
QUAND : Toute distribution basée Debian
```

**Évolution :**
```
apt-get (1998) -> Outil historique, script-friendly
apt-cache       -> Recherche de paquets
apt (2014)      -> Interface moderne unifiée
```

---

## [ENTREE] Installation de paquets

### COMMENT installer avec APT ?

**Test 1 : Mise à jour des index**

```
POURQUOI : Synchroniser avec les dépôts
COMMENT : apt update
QUAND : Avant toute installation, quotidien
```

```bash
# Mise à jour des listes de paquets
sudo apt update

# Résultat typique :
# Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease
# Get:2 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
# ...
# Fetched 1,234 kB in 2s (617 kB/s)
# Reading package lists... Done
# Building dependency tree... Done
```

**Que se passe-t-il ?**

```
1. Lecture /etc/apt/sources.list
2. Connexion aux dépôts
3. Téléchargement des index (Packages.gz)
4. Mise à jour cache local (/var/lib/apt/lists/)
5. Vérification signatures GPG
```

---

**Test 2 : Installation basique**

```bash
# Installer un paquet
sudo apt install nginx

# Résultat :
# Reading package lists... Done
# Building dependency tree... Done
# The following additional packages will be installed:
#   nginx-common nginx-core
# The following NEW packages will be installed:
#   nginx nginx-common nginx-core
# 0 upgraded, 3 newly installed, 0 to remove
# Do you want to continue? [Y/n]
```

**Processus complet :**

```bash
# Script de démonstration détaillé
cat > apt-install-demo.sh << 'EOF'
#!/bin/bash

PACKAGE="htop"

echo "[PACKAGE] Installation détaillée : $PACKAGE"
echo "===================================="
echo ""

# 1. Vérifier si déjà installé
echo "1⃣  Vérification état actuel..."
if dpkg -l | grep -q "^ii.*$PACKAGE"; then
    echo "   [OK] $PACKAGE déjà installé"
    echo "   Version : $(dpkg -l | grep "^ii.*$PACKAGE" | awk '{print $3}')"
else
    echo "   [X] $PACKAGE non installé"
fi
echo ""

# 2. Simuler installation
echo "2⃣  Simulation (dry-run)..."
sudo apt install -s $PACKAGE | head -15
echo ""

# 3. Voir les dépendances
echo "3⃣  Arbre de dépendances..."
apt-cache depends $PACKAGE
echo ""

# 4. Taille à télécharger
echo "4⃣  Informations de téléchargement..."
apt-cache show $PACKAGE | grep -E "^(Package|Version|Size|Installed-Size)"
echo ""

# 5. Installation réelle
echo "5⃣  Installation..."
echo "   Commande : sudo apt install -y $PACKAGE"
echo "   (Exécuter manuellement pour installer)"

EOF

chmod +x apt-install-demo.sh
./apt-install-demo.sh
```

---

**Test 3 : Options d'installation**

```bash
# Installation sans confirmation
sudo apt install -y nginx

# Simuler sans installer (dry-run)
sudo apt install -s nginx

# Réinstaller
sudo apt install --reinstall nginx

# Installer version spécifique
sudo apt install nginx=1.18.0-6ubuntu14

# Télécharger sans installer
sudo apt download nginx

# Installer depuis fichier .deb local
sudo apt install ./nginx_1.18.0-6ubuntu14_amd64.deb
```

---

**Test 4 : Installation multiple**

```bash
# Installer plusieurs paquets
sudo apt install nginx mysql-server php-fpm

# Avec wildcard
sudo apt install php-*

# Depuis un fichier
cat > packages.txt << 'EOF'
nginx
mysql-server
php-fpm
redis-server
git
EOF

# Installer tous
sudo apt install $(cat packages.txt)
```

---

## [RECHERCHE] Recherche de paquets

**Test 1 : Recherche basique**

```bash
# Rechercher un paquet
apt search nginx

# Rechercher avec regex
apt search '^nginx'

# Recherche exacte
apt search --names-only nginx

# Avec description courte
apt-cache search nginx --names-only
```

---

**Test 2 : Informations détaillées**

```bash
# Afficher infos complètes
apt show nginx

# Résultat typique :
# Package: nginx
# Version: 1.18.0-6ubuntu14
# Priority: optional
# Section: web
# Maintainer: Ubuntu Developers
# Installed-Size: 46.1 kB
# Depends: nginx-core | nginx-full | nginx-light | nginx-extras
# Homepage: https://nginx.org
# Description: small, powerful, scalable web/proxy server

# Voir toutes les versions disponibles
apt-cache policy nginx

# Voir les dépendances
apt-cache depends nginx

# Voir les reverse dependencies (qui dépend de ce paquet)
apt-cache rdepends nginx
```

---

**Test 3 : Lister les paquets**

```bash
# Tous les paquets installés
apt list --installed

# Paquets disponibles
apt list --all-versions

# Paquets upgradables
apt list --upgradable

# Avec filtre
apt list --installed | grep nginx
```

---

## ^ Mise à jour de paquets

### POURQUOI mettre à jour ?

```
RAISONS :
[OK] Correctifs de sécurité
[OK] Nouvelles fonctionnalités
[OK] Corrections de bugs
[OK] Performance améliorée

QUAND :
- Sécurité : Immédiatement
- Système : Hebdomadaire
- Production : Fenêtre de maintenance
```

---

**Test 1 : Mise à jour complète**

```bash
# 1. Mettre à jour les index
sudo apt update

# 2. Voir ce qui sera mis à jour
apt list --upgradable

# 3. Mettre à jour tous les paquets
sudo apt upgrade

# Alternative : upgrade + gestion dépendances
sudo apt full-upgrade

# OU (obsolète mais équivalent)
sudo apt-get dist-upgrade
```

---

**Test 2 : Différence upgrade vs full-upgrade**

```bash
# Script de comparaison
cat > apt-upgrade-comparison.sh << 'EOF'
#!/bin/bash

echo "[SYNC] DIFFÉRENCES UPGRADE vs FULL-UPGRADE"
echo "======================================"
echo ""

echo "[GRAPHIQUE] apt upgrade :"
echo "   - Met à jour les paquets existants"
echo "   - N'installe PAS de nouveaux paquets"
echo "   - Ne supprime PAS de paquets"
echo "   - Conservateur, sûr"
echo ""
echo "   Exemple : nginx 1.18 -> 1.19"
echo "   Si nginx 1.19 nécessite nouvelle lib -> PAS installé"
echo ""

echo "[GRAPHIQUE] apt full-upgrade :"
echo "   - Met à jour les paquets existants"
echo "   - PEUT installer de nouveaux paquets"
echo "   - PEUT supprimer des paquets"
echo "   - Agressif, gère mieux les dépendances"
echo ""
echo "   Exemple : nginx 1.18 -> 1.19"
echo "   Si nginx 1.19 nécessite nouvelle lib -> INSTALLÉE"
echo "   Si vieille lib obsolète -> SUPPRIMÉE"
echo ""

echo "[SCALES]  QUAND utiliser quoi ?"
echo ""
echo "   upgrade       -> Mises à jour quotidiennes sûres"
echo "   full-upgrade  -> Upgrades majeurs, changements système"
echo ""

# Simuler les deux
echo "[ANALYSE] Simulation upgrade :"
sudo apt upgrade -s | head -20
echo ""

echo "[ANALYSE] Simulation full-upgrade :"
sudo apt full-upgrade -s | head -20

EOF

chmod +x apt-upgrade-comparison.sh
./apt-upgrade-comparison.sh
```

---

**Test 3 : Mise à jour sélective**

```bash
# Mettre à jour seulement un paquet
sudo apt install --only-upgrade nginx

# Exclure un paquet des mises à jour (hold)
sudo apt-mark hold nginx

# Voir les paquets en hold
apt-mark showhold

# Libérer
sudo apt-mark unhold nginx
```

---

## [SUPPRIMER] Suppression de paquets

### COMMENT supprimer ?

**Test 1 : remove vs purge**

```
DIFFÉRENCES :

remove :
- Supprime les binaires
- GARDE les fichiers de configuration
- QUAND : Réinstallation prévue

purge :
- Supprime TOUT (binaires + config)
- Nettoyage complet
- QUAND : Suppression définitive
```

```bash
# Supprimer (garder config)
sudo apt remove nginx

# Vérifier - config reste
ls /etc/nginx/
# nginx.conf encore présent

# Supprimer complètement (+ config)
sudo apt purge nginx

# Vérifier - plus rien
ls /etc/nginx/
# ls: cannot access '/etc/nginx/': No such file or directory

# Supprimer + dépendances non utilisées
sudo apt autoremove nginx

# Purge + autoremove
sudo apt purge --autoremove nginx
```

---

**Test 2 : Nettoyage système**

```bash
# Supprimer paquets orphelins (dépendances non utilisées)
sudo apt autoremove

# Nettoyer le cache (.deb téléchargés)
sudo apt clean

# Nettoyer cache partiel (garder versions actuelles)
sudo apt autoclean

# Voir l'espace utilisé par le cache
du -sh /var/cache/apt/archives/
```

---

**Script de nettoyage complet :**

```bash
cat > apt-cleanup.sh << 'EOF'
#!/bin/bash

echo "[NETTOYAGE] NETTOYAGE APT COMPLET"
echo "========================"
echo ""

# Espace avant
BEFORE=$(df -h / | awk 'NR==2 {print $4}')
echo "[SAUVEGARDE] Espace libre avant : $BEFORE"
echo ""

# 1. Paquets orphelins
echo "1⃣  Suppression paquets orphelins..."
sudo apt autoremove -y
echo ""

# 2. Cache
echo "2⃣  Nettoyage cache APT..."
CACHE_SIZE=$(du -sh /var/cache/apt/archives/ | cut -f1)
echo "   Taille cache : $CACHE_SIZE"
sudo apt clean
echo "   [OK] Cache nettoyé"
echo ""

# 3. Listes obsolètes
echo "3⃣  Suppression listes obsolètes..."
sudo apt autoclean
echo ""

# 4. Fichiers de config orphelins
echo "4⃣  Recherche configs orphelines..."
dpkg -l | grep '^rc' | awk '{print $2}' | while read pkg; do
    echo "   Purge : $pkg"
    sudo dpkg --purge $pkg 2>/dev/null
done
echo ""

# 5. Logs anciens
echo "5⃣  Nettoyage logs APT (>30 jours)..."
sudo find /var/log/apt/ -name "*.log" -mtime +30 -delete
echo ""

# Espace après
AFTER=$(df -h / | awk 'NR==2 {print $4}')
echo "[SAUVEGARDE] Espace libre après : $AFTER"
echo ""
echo "[OK] Nettoyage terminé"

EOF

chmod +x apt-cleanup.sh
sudo ./apt-cleanup.sh
```

---

## [OUTIL] Gestion avancée APT

**Test 1 : Verrouiller des versions**

```bash
# Empêcher mise à jour
sudo apt-mark hold nginx

# Vérifier
apt-mark showhold

# Libérer
sudo apt-mark unhold nginx

# Installer version précise et verrouiller
sudo apt install nginx=1.18.0-6ubuntu14
sudo apt-mark hold nginx
```

---

**Test 2 : Priorités (pinning)**

```
POURQUOI : Contrôler les versions depuis différents dépôts
COMMENT : /etc/apt/preferences.d/
QUAND : Mix de dépôts (stable + testing)
```

```bash
# Créer règle de priorité
sudo tee /etc/apt/preferences.d/nginx-pinning << 'EOF'
Package: nginx
Pin: version 1.18.*
Pin-Priority: 1001
EOF

# Priorités :
# < 0   : Jamais installer
# 0-100 : Installé seulement si aucune autre version
# 100-500 : Installer si pas déjà installé
# 500-990 : Installer sauf si version plus récente
# > 1000 : Installer même si downgrade

# Voir les priorités
apt-cache policy nginx
```

---

**Test 3 : Configuration APT**

```bash
# Configuration principale
cat /etc/apt/apt.conf.d/50unattended-upgrades

# Créer config personnalisée
sudo tee /etc/apt/apt.conf.d/99custom << 'EOF'
// Conserver anciennes versions dans le cache
APT::Clean-Installed "false";

// Timeout téléchargement
Acquire::http::Timeout "30";

// Compression
Acquire::CompressionTypes::Order:: "gz";

// Recommandations (installer par défaut)
APT::Install-Recommends "true";
APT::Install-Suggests "false";

// Conserver fichiers .deb téléchargés
APT::Keep-Downloaded-Packages "true";
EOF

# Vérifier la config
apt-config dump
```

---

**Test 4 : Corrections de problèmes**

```bash
# Réparer dépendances cassées
sudo apt install -f

# Reconfigurer un paquet
sudo dpkg-reconfigure nginx

# Forcer réinstallation
sudo apt install --reinstall nginx

# Résoudre problèmes de verrouillage
sudo rm /var/lib/apt/lists/lock
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock*
sudo dpkg --configure -a

# Réparer base de données dpkg
sudo dpkg --configure -a
sudo apt update
sudo apt install -f
```

---

## [GRAPHIQUE] Bas-niveau : dpkg

### POURQUOI dpkg ?

```
CONTEXTE : Gestionnaire bas-niveau Debian
PROBLÈME : Opérations précises sur paquets locaux
SOLUTION : dpkg (Debian Package)
QUAND : Debug, opérations manuelles, scripts
```

---

**Test 1 : Lister les paquets**

```bash
# Tous les paquets installés
dpkg -l

# Format de sortie :
# ii  nginx  1.18.0-6ubuntu14  web server
# v     v         v                v
# État Nom     Version      Description

# États possibles :
# ii = Installed OK
# rc = Removed, Config remains
# un = Unknown
# iU = Installed, Unpacked

# Filtrer
dpkg -l | grep nginx
dpkg -l 'nginx*'

# Compter paquets installés
dpkg -l | grep '^ii' | wc -l
```

---

**Test 2 : Informations sur un paquet**

```bash
# Status d'un paquet
dpkg -s nginx

# Lister les fichiers d'un paquet
dpkg -L nginx

# Trouver à quel paquet appartient un fichier
dpkg -S /usr/sbin/nginx
# nginx-core: /usr/sbin/nginx

# Vérifier intégrité
sudo dpkg -V nginx
```

---

**Test 3 : Installer/Supprimer avec dpkg**

```bash
# Installer fichier .deb local
sudo dpkg -i package.deb

# Supprimer
sudo dpkg -r nginx

# Purge
sudo dpkg -P nginx

# [ATTENTION] dpkg ne gère PAS les dépendances !
# Si dépendances manquantes :
sudo dpkg -i package.deb  # Échoue
sudo apt install -f       # Résout les dépendances
```

---

**Test 4 : Extraire et analyser un .deb**

```bash
# Télécharger un .deb
apt download nginx

# Voir le contenu sans installer
dpkg -c nginx_*.deb

# Extraire le contenu
dpkg -x nginx_*.deb extracted/

# Extraire les métadonnées
dpkg -e nginx_*.deb extracted/DEBIAN/

# Voir les scripts
cat extracted/DEBIAN/postinst
cat extracted/DEBIAN/prerm
```

---

**Script d'analyse de paquet :**

```bash
cat > analyze-deb.sh << 'EOF'
#!/bin/bash

DEB_FILE="${1}"

if [ ! -f "$DEB_FILE" ]; then
    echo "Usage: $0 <fichier.deb>"
    exit 1
fi

echo "[PACKAGE] ANALYSE DU PAQUET : $DEB_FILE"
echo "================================"
echo ""

# 1. Informations générales
echo "1⃣  Informations :"
dpkg-deb -I "$DEB_FILE" | head -20
echo ""

# 2. Contenu
echo "2⃣  Fichiers contenus (top 20) :"
dpkg-deb -c "$DEB_FILE" | head -20
echo ""

# 3. Scripts
echo "3⃣  Scripts de maintenance :"
TEMP_DIR=$(mktemp -d)
dpkg -e "$DEB_FILE" "$TEMP_DIR"

for script in preinst postinst prerm postrm; do
    if [ -f "$TEMP_DIR/$script" ]; then
        echo "   [FICHIER] $script :"
        echo "   ---"
        cat "$TEMP_DIR/$script" | head -10
        echo "   ..."
        echo ""
    fi
done

rm -rf "$TEMP_DIR"

# 4. Dépendances
echo "4⃣  Dépendances :"
dpkg-deb -f "$DEB_FILE" Depends
echo ""

# 5. Taille
echo "5⃣  Taille :"
echo "   Fichier .deb : $(du -h "$DEB_FILE" | cut -f1)"
echo "   Installé     : $(dpkg-deb -f "$DEB_FILE" Installed-Size) KB"

EOF

chmod +x analyze-deb.sh

# Utilisation
apt download nginx
./analyze-deb.sh nginx_*.deb
```

---

# PARTIE 3 : YUM/DNF - RHEL/CENTOS/FEDORA

## [OBJECTIF] POURQUOI YUM/DNF ?

```
CONTEXTE : Red Hat, CentOS, Fedora utilisent .rpm
PROBLÈME : rpm (bas-niveau) ne gère pas dépendances
SOLUTION : YUM (2003), puis DNF (2015)
QUAND : Distributions Red Hat-based
```

**Évolution :**
```
rpm (1995)     -> Gestionnaire bas-niveau
YUM (2003)     -> Yellowdog Updater Modified
DNF (2015)     -> Dandified YUM (plus rapide, meilleur)
```

**DNF = YUM amélioré**
- Résolution dépendances plus rapide
- Meilleure gestion mémoire
- API Python plus propre
- Compatible avec commandes YUM

---

## [ENTREE] Installation avec DNF

**Test 1 : Installation basique**

```bash
# Mettre à jour les métadonnées
sudo dnf check-update

# Installer un paquet
sudo dnf install nginx

# Installer sans confirmation
sudo dnf install -y nginx

# Installer version spécifique
sudo dnf install nginx-1.20.0

# Installer depuis fichier local
sudo dnf install ./nginx-1.20.0.rpm

# Installer groupe de paquets
sudo dnf groupinstall "Development Tools"
```

---

**Test 2 : Recherche**

```bash
# Rechercher paquet
dnf search nginx

# Informations détaillées
dnf info nginx

# Lister tous les paquets
dnf list

# Paquets installés
dnf list installed

# Paquets disponibles
dnf list available

# Voir les dépendances
dnf deplist nginx

# Qui fournit un fichier ?
dnf provides /usr/sbin/nginx
```

---

**Test 3 : Groupes**

```
POURQUOI : Installer ensembles cohérents
COMMENT : dnf group
QUAND : Dev tools, serveurs web, bureautique
```

```bash
# Lister les groupes
dnf group list

# Infos sur un groupe
dnf group info "Development Tools"

# Installer un groupe
sudo dnf groupinstall "Development Tools"

# Supprimer un groupe
sudo dnf groupremove "Development Tools"
```

---

## ^ Mise à jour avec DNF

```bash
# Vérifier mises à jour
dnf check-update

# Mettre à jour tout
sudo dnf update

# Mettre à jour un paquet
sudo dnf update nginx

# Upgrade complet (peut changer release)
sudo dnf upgrade

# Voir l'historique
dnf history

# Annuler la dernière transaction
sudo dnf history undo last

# Répéter une transaction
sudo dnf history redo 5
```

---

## [SUPPRIMER] Suppression avec DNF

```bash
# Supprimer paquet
sudo dnf remove nginx

# Supprimer + dépendances non utilisées
sudo dnf autoremove nginx

# Nettoyer cache
sudo dnf clean all

# Nettoyer seulement métadonnées
sudo dnf clean metadata
```

---

## [OUTIL] Configuration DNF

```bash
# Fichier de config
cat /etc/dnf/dnf.conf

# Exemple de configuration
sudo tee -a /etc/dnf/dnf.conf << 'EOF'

[main]
# Conserver cache téléchargé
keepcache=1

# Nombre de transactions dans l'historique
history_record_packages=100

# Vérifier signatures GPG
gpgcheck=1

# Téléchargements parallèles
max_parallel_downloads=10

# Timeout
timeout=30

# Installer recommandations
install_weak_deps=True
EOF
```

---

## [GRAPHIQUE] Bas-niveau : RPM

**Test 1 : Opérations RPM**

```bash
# Lister paquets installés
rpm -qa

# Filtrer
rpm -qa | grep nginx

# Informations sur paquet
rpm -qi nginx

# Lister fichiers
rpm -ql nginx

# Quel paquet fournit un fichier ?
rpm -qf /usr/sbin/nginx

# Installer rpm local
sudo rpm -ivh package.rpm
# -i : install
# -v : verbose
# -h : hash (barre de progression)

# Mettre à jour
sudo rpm -Uvh package.rpm

# Supprimer
sudo rpm -e nginx

# Vérifier intégrité
rpm -V nginx
```

---

**Test 2 : Créer un RPM simple**

```bash
cat > create-simple-rpm.sh << 'EOF'
#!/bin/bash

# Installer outils
sudo dnf install -y rpm-build rpmdevtools

# Créer structure
rpmdev-setuptree

# Créer spec file
cat > ~/rpmbuild/SPECS/myapp.spec << 'SPEC'
Name:           myapp
Version:        1.0
Release:        1%{?dist}
Summary:        My Application

License:        MIT
URL:            https://example.com
Source0:        %{name}-%{version}.tar.gz

%description
This is my application

%prep
%setup -q

%build
# Compilation ici

%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/bin
cp myapp $RPM_BUILD_ROOT/usr/bin/

%files
/usr/bin/myapp

%changelog
* Wed Dec 31 2024 Author <author@example.com> - 1.0-1
- Initial package
SPEC

# Créer source
mkdir -p ~/rpmbuild/SOURCES/myapp-1.0
echo '#!/bin/bash' > ~/rpmbuild/SOURCES/myapp-1.0/myapp
echo 'echo "Hello from myapp"' >> ~/rpmbuild/SOURCES/myapp-1.0/myapp
chmod +x ~/rpmbuild/SOURCES/myapp-1.0/myapp
cd ~/rpmbuild/SOURCES
tar czf myapp-1.0.tar.gz myapp-1.0/

# Build
cd ~/rpmbuild/SPECS
rpmbuild -ba myapp.spec

echo "[OK] RPM créé dans ~/rpmbuild/RPMS/"
ls -lh ~/rpmbuild/RPMS/*/myapp-*

EOF

chmod +x create-simple-rpm.sh
```

---

# PARTIE 4 : PACMAN - ARCH LINUX

## [OBJECTIF] POURQUOI Pacman ?

```
CONTEXTE : Arch Linux (rolling release)
PHILOSOPHIE : Simplicité, minimalisme, contrôle
SOLUTION : Pacman (Package Manager)
QUAND : Arch, Manjaro, EndeavourOS
```

---

## [ENTREE] Installation avec Pacman

```bash
# Synchroniser bases de données
sudo pacman -Sy

# Mettre à jour le système
sudo pacman -Syu

# Installer paquet
sudo pacman -S nginx

# Installer sans confirmation
sudo pacman -S --noconfirm nginx

# Installer plusieurs
sudo pacman -S nginx mysql php
```

---

## [RECHERCHE] Recherche

```bash
# Rechercher
pacman -Ss nginx

# Informations
pacman -Si nginx

# Paquets installés
pacman -Q

# Rechercher dans installés
pacman -Qs nginx

# Infos paquet installé
pacman -Qi nginx

# Fichiers d'un paquet
pacman -Ql nginx

# Quel paquet fournit un fichier ?
pacman -Qo /usr/sbin/nginx
```

---

## [SUPPRIMER] Suppression

```bash
# Supprimer paquet
sudo pacman -R nginx

# Supprimer + dépendances non utilisées
sudo pacman -Rs nginx

# Supprimer + config
sudo pacman -Rn nginx

# Supprimer + tout (dépendances + config)
sudo pacman -Rns nginx

# Nettoyer cache
sudo pacman -Sc

# Nettoyer complètement
sudo pacman -Scc
```

---

## [DESIGN] AUR (Arch User Repository)

```
POURQUOI : Paquets communautaires
COMMENT : yay, paru (helpers AUR)
QUAND : Logiciels pas dans dépôts officiels
```

```bash
# Installer yay (AUR helper)
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si

# Utiliser yay (syntaxe identique à pacman)
yay -S package-from-aur

# Mise à jour (officiel + AUR)
yay -Syu

# Recherche AUR
yay -Ss package
```

---

# PARTIE 5 : FORMATS UNIVERSELS

## [OBJECTIF] POURQUOI les formats universels ?

```
PROBLÈME :
- Incompatibilité entre distributions
- Dépendances complexes
- Versions système figées

SOLUTIONS :
- Snap (Canonical)
- Flatpak (Freedesktop)
- AppImage (portable)

AVANTAGES :
[OK] Fonctionne sur toutes les distributions
[OK] Isolation (conteneurs)
[OK] Mises à jour indépendantes
[OK] Plusieurs versions possibles

INCONVÉNIENTS :
[X] Taille plus importante
[X] Performance légèrement inférieure
[X] Duplication bibliothèques
```

---

## [PACKAGE] Snap

### COMMENT utiliser Snap ?

```bash
# Installer snapd
sudo apt install snapd  # Debian/Ubuntu
sudo dnf install snapd  # Fedora

# Rechercher
snap find vlc

# Installer
sudo snap install vlc

# Lister installés
snap list

# Infos
snap info vlc

# Mettre à jour
sudo snap refresh vlc

# Mettre à jour tous
sudo snap refresh

# Supprimer
sudo snap remove vlc

# Voir les révisions
snap list --all vlc

# Revenir à version précédente
sudo snap revert vlc
```

---

### Canaux Snap

```
POURQUOI : Différents niveaux de stabilité
COMMENT : stable, candidate, beta, edge
QUAND : Test nouvelles fonctionnalités
```

```bash
# Installer depuis canal spécifique
sudo snap install --channel=edge firefox

# Changer de canal
sudo snap refresh --channel=beta firefox

# Voir les canaux disponibles
snap info firefox
```

---

## [PACKAGE] Flatpak

```bash
# Installer Flatpak
sudo apt install flatpak

# Ajouter Flathub (dépôt principal)
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

# Rechercher
flatpak search gimp

# Installer
flatpak install flathub org.gimp.GIMP

# Lister
flatpak list

# Exécuter
flatpak run org.gimp.GIMP

# Mettre à jour
flatpak update

# Supprimer
flatpak uninstall org.gimp.GIMP

# Nettoyer
flatpak uninstall --unused
```

---

## [PACKAGE] AppImage

```
POURQUOI : Portable, pas d'installation
COMMENT : Fichier unique exécutable
QUAND : Tester logiciels, portabilité
```

```bash
# Télécharger AppImage
wget https://example.com/app.AppImage

# Rendre exécutable
chmod +x app.AppImage

# Exécuter
./app.AppImage

# Intégrer au système (optionnel)
./app.AppImage --appimage-extract
sudo mv squashfs-root /opt/myapp
sudo ln -s /opt/myapp/AppRun /usr/local/bin/myapp
```

---

## [GRAPHIQUE] Comparaison

```bash
cat > compare-package-formats.sh << 'EOF'
#!/bin/bash

echo "[PACKAGE] COMPARAISON FORMATS DE PAQUETS"
echo "=================================="
echo ""

cat << 'TABLE'
┌──────────────┬──────────┬──────────┬───────────┬──────────┐
│ Critère      │ APT/DNF  │ Snap     │ Flatpak   │ AppImage │
├──────────────┼──────────┼──────────┼───────────┼──────────┤
│ Universel    │    [X]    │    [OK]    │    [OK]     │    [OK]    │
│ Taille       │    *** │    *    │    *     │    **  │
│ Performance  │    *** │    **  │    **   │    ***│
│ Isolation    │    [X]    │    [OK]    │    [OK]     │    [X]    │
│ Auto-update  │    [OK]    │    [OK]    │    [OK]     │    [X]    │
│ Installation │    [OK]    │    [OK]    │    [OK]     │    [X]    │
│ Portable     │    [X]    │    [X]    │    [X]     │    [OK]    │
└──────────────┴──────────┴──────────┴───────────┴──────────┘

QUAND UTILISER :

APT/DNF (Natif)
  [OK] Applications système
  [OK] Serveurs
  [OK] Performance critique
  [OK] Intégration profonde

Snap
  [OK] Applications desktop Ubuntu
  [OK] Déploiement rapide
  [OK] Mises à jour automatiques

Flatpak
  [OK] Applications desktop multi-distro
  [OK] Isolation importante
  [OK] Versions multiples

AppImage
  [OK] Test rapide
  [OK] Portabilité USB
  [OK] Pas de privilèges admin
TABLE

EOF

chmod +x compare-package-formats.sh
./compare-package-formats.sh
```

---

# PARTIE 6 : GESTION DES DÉPÔTS

## [REFLEXION] POURQUOI gérer les dépôts ?

```
PROBLÈME : Dépôts officiels limités/obsolètes
SOLUTION : Ajouter dépôts tiers (PPA, EPEL, etc.)
RISQUES : Sécurité, stabilité
QUAND : Logiciels récents, paquets spécialisés
```

---

## [NOTE] Dépôts APT (Debian/Ubuntu)

### Fichier sources.list

```bash
# Fichier principal
cat /etc/apt/sources.list

# Format :
# deb [options] http://url distribution composants
```

**Exemple :**

```
deb http://archive.ubuntu.com/ubuntu jammy main restricted
deb http://archive.ubuntu.com/ubuntu jammy-updates main restricted
deb http://security.ubuntu.com/ubuntu jammy-security main restricted

Décomposition :
deb             -> Type (deb = binaires, deb-src = sources)
http://...      -> URL du dépôt
jammy           -> Distribution (code release)
main restricted -> Composants (sections)
```

---

### Composants Ubuntu

```
main       -> Logiciels open source supportés
restricted -> Logiciels propriétaires supportés
universe   -> Communauté, pas de support officiel
multiverse -> Logiciels restreints (licences)
```

---

### Ajouter un dépôt

**Méthode 1 : add-apt-repository**

```bash
# Ajouter PPA (Personal Package Archive)
sudo add-apt-repository ppa:nginx/stable

# Mettre à jour
sudo apt update

# Supprimer PPA
sudo add-apt-repository --remove ppa:nginx/stable
```

---

**Méthode 2 : Fichier manuel**

```bash
# Créer fichier dans sources.list.d
sudo tee /etc/apt/sources.list.d/nginx.list << 'EOF'
deb [arch=amd64 signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu jammy nginx
EOF

# Importer clé GPG
wget -O- https://nginx.org/keys/nginx_signing.key | \
    gpg --dearmor | \
    sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg > /dev/null

# Mettre à jour
sudo apt update
```

---

**Méthode 3 : Script complet**

```bash
cat > add-repo-safe.sh << 'EOF'
#!/bin/bash

REPO_URL="$1"
REPO_NAME="$2"
GPG_KEY_URL="$3"

if [ -z "$REPO_URL" ] || [ -z "$REPO_NAME" ]; then
    echo "Usage: $0 <repo_url> <repo_name> [gpg_key_url]"
    exit 1
fi

echo "[PACKAGE] Ajout dépôt : $REPO_NAME"
echo "=========================="

# 1. Créer backup
sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup-$(date +%Y%m%d)

# 2. Importer clé GPG si fournie
if [ -n "$GPG_KEY_URL" ]; then
    echo "[CLE] Import clé GPG..."
    wget -O- "$GPG_KEY_URL" | \
        gpg --dearmor | \
        sudo tee "/usr/share/keyrings/${REPO_NAME}-keyring.gpg" > /dev/null
    
    SIGNED_BY="signed-by=/usr/share/keyrings/${REPO_NAME}-keyring.gpg"
fi

# 3. Ajouter dépôt
echo "[NOTE] Ajout dépôt..."
echo "deb [${SIGNED_BY}] $REPO_URL" | \
    sudo tee "/etc/apt/sources.list.d/${REPO_NAME}.list"

# 4. Mettre à jour
echo "[SYNC] Mise à jour index..."
sudo apt update

echo "[OK] Dépôt $REPO_NAME ajouté"

EOF

chmod +x add-repo-safe.sh

# Utilisation
# ./add-repo-safe.sh "http://nginx.org/packages/ubuntu jammy nginx" nginx "https://nginx.org/keys/nginx_signing.key"
```

---

## [NOTE] Dépôts YUM/DNF (RHEL/CentOS/Fedora)

```bash
# Fichiers de configuration
ls /etc/yum.repos.d/

# Format .repo
cat /etc/yum.repos.d/nginx.repo

# Exemple :
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
```

---

**Ajouter dépôt DNF**

```bash
# Méthode 1 : dnf config-manager
sudo dnf config-manager --add-repo https://example.com/repo

# Méthode 2 : Fichier manuel
sudo tee /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF

# Importer clé GPG
sudo rpm --import https://nginx.org/keys/nginx_signing.key

# Lister dépôts
dnf repolist

# Activer/désactiver
sudo dnf config-manager --set-disabled nginx-stable
sudo dnf config-manager --set-enabled nginx-stable
```

---

**EPEL (Extra Packages for Enterprise Linux)**

```bash
# Installer EPEL
sudo dnf install epel-release

# Ou sur RHEL
sudo dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm

# Vérifier
dnf repolist | grep epel
```

---

## [SECURISE] Sécurité des dépôts

### POURQUOI vérifier les signatures ?

```
PROBLÈME : Dépôts malveillants, man-in-the-middle
SOLUTION : Signatures GPG
QUAND : TOUJOURS !
```

---

**Vérifier signatures APT**

```bash
# Lister clés GPG
apt-key list

# Vérifier paquet
apt-key adv --verify package.deb

# Voir les warnings
sudo apt update 2>&1 | grep -i warning

# Ajouter clé manuellement
wget -O- https://example.com/key.gpg | sudo apt-key add -

# [ATTENTION] apt-key est déprécié ! Utiliser /usr/share/keyrings/
```

---

**Méthode moderne (recommandée)**

```bash
# Télécharger clé dans keyring
wget -O- https://example.com/key.gpg | \
    gpg --dearmor | \
    sudo tee /usr/share/keyrings/example-keyring.gpg > /dev/null

# Référencer dans sources.list
echo "deb [signed-by=/usr/share/keyrings/example-keyring.gpg] https://example.com/repo stable main" | \
    sudo tee /etc/apt/sources.list.d/example.list
```

---

# PARTIE 7 : COMPILATION DEPUIS LES SOURCES

## [REFLEXION] POURQUOI compiler ?

```
RAISONS :
[OK] Version spécifique non packagée
[OK] Optimisations personnalisées
[OK] Fonctionnalités custom
[OK] Dernière version (bleeding edge)
[OK] Pas de paquet disponible

INCONVÉNIENTS :
[X] Complexe
[X] Dépendances manuelles
[X] Pas de mises à jour auto
[X] Désinstallation difficile
```

---

## [OUTIL] Processus classique : configure, make, make install

```bash
# Étape 1 : Télécharger sources
wget https://nginx.org/download/nginx-1.24.0.tar.gz

# Étape 2 : Extraire
tar -xzf nginx-1.24.0.tar.gz
cd nginx-1.24.0

# Étape 3 : Installer dépendances build
sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev

# Étape 4 : Configurer
./configure \
    --prefix=/usr/local/nginx \
    --with-http_ssl_module \
    --with-http_v2_module

# Étape 5 : Compiler
make -j$(nproc)

# Étape 6 : Installer
sudo make install

# Étape 7 : Vérifier
/usr/local/nginx/sbin/nginx -v
```

---

**Script complet de compilation**

```bash
cat > compile-from-source.sh << 'EOF'
#!/bin/bash

APP_NAME="nginx"
VERSION="1.24.0"
URL="https://nginx.org/download/${APP_NAME}-${VERSION}.tar.gz"
PREFIX="/opt/${APP_NAME}-${VERSION}"

echo "[OUTIL] Compilation de $APP_NAME $VERSION"
echo "===================================="

# 1. Dépendances
echo "1⃣  Installation dépendances..."
sudo apt update
sudo apt install -y build-essential libpcre3-dev libssl-dev zlib1g-dev

# 2. Téléchargement
echo "2⃣  Téléchargement sources..."
cd /tmp
wget -q "$URL"
tar -xzf "${APP_NAME}-${VERSION}.tar.gz"
cd "${APP_NAME}-${VERSION}"

# 3. Configuration
echo "3⃣  Configuration..."
./configure \
    --prefix="$PREFIX" \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_gzip_static_module \
    2>&1 | tee configure.log

if [ ${PIPESTATUS[0]} -ne 0 ]; then
    echo "[X] Configuration échouée"
    tail -20 configure.log
    exit 1
fi

# 4. Compilation
echo "4⃣  Compilation ($(nproc) cores)..."
make -j$(nproc) 2>&1 | tee make.log

if [ ${PIPESTATUS[0]} -ne 0 ]; then
    echo "[X] Compilation échouée"
    tail -20 make.log
    exit 1
fi

# 5. Installation
echo "5⃣  Installation..."
sudo make install

# 6. Créer symlink
sudo ln -sf "$PREFIX" "/opt/$APP_NAME"
sudo ln -sf "/opt/$APP_NAME/sbin/nginx" "/usr/local/bin/nginx"

# 7. Créer service systemd
echo "6⃣  Création service systemd..."
sudo tee /etc/systemd/system/nginx-custom.service << SERVICE
[Unit]
Description=Nginx Custom Build
After=network.target

[Service]
Type=forking
PIDFile=/opt/nginx/logs/nginx.pid
ExecStartPre=/opt/nginx/sbin/nginx -t
ExecStart=/opt/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP \$MAINPID
ExecStop=/bin/kill -s QUIT \$MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
SERVICE

sudo systemctl daemon-reload

# 8. Documentation
echo "7⃣  Création documentation..."
sudo tee "$PREFIX/INSTALL_INFO.txt" << INFO
Installation personnalisée : $APP_NAME $VERSION
Date : $(date)
Prefix : $PREFIX
Configure options : voir configure.log
Build logs : voir make.log

Utilisation :
  Démarrer : sudo systemctl start nginx-custom
  Arrêter  : sudo systemctl stop nginx-custom
  Status   : sudo systemctl status nginx-custom

Désinstaller :
  sudo systemctl stop nginx-custom
  sudo systemctl disable nginx-custom
  sudo rm -rf $PREFIX
  sudo rm /usr/local/bin/nginx
  sudo rm /etc/systemd/system/nginx-custom.service
INFO

echo ""
echo "[OK] Installation terminée"
echo "======================="
echo "Binaire  : $PREFIX/sbin/nginx"
echo "Config   : $PREFIX/conf/nginx.conf"
echo "Logs     : $PREFIX/logs/"
echo "Service  : nginx-custom"
echo ""
echo "Démarrer : sudo systemctl start nginx-custom"

EOF

chmod +x compile-from-source.sh
```

---

## [OBJECTIF] Gestionnaire d'alternatives (update-alternatives)

```
POURQUOI : Gérer plusieurs versions
COMMENT : update-alternatives
QUAND : Python 2 vs 3, Java 8 vs 11, etc.
```

```bash
# Voir les alternatives
update-alternatives --list python

# Configurer alternative
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.10 1
sudo update-alternatives --install /usr/bin/python python /usr/bin/python3.11 2

# Choisir interactivement
sudo update-alternatives --config python

# Définir par défaut
sudo update-alternatives --set python /usr/bin/python3.11

# Supprimer alternative
sudo update-alternatives --remove python /usr/bin/python3.10
```

---

# PARTIE 8 : [ENTREPRISE] 5 CAS RÉELS ENTREPRISE

## CAS 1 : [ECRAN] SERVEUR LAMP PRODUCTION

### POURQUOI ce cas ?

```
CONTEXTE : Serveur web PHP + MySQL
BESOIN :
- Stack LAMP (Linux, Apache, MySQL, PHP)
- Versions spécifiques
- Configuration optimisée
- Monitoring
- Backups automatiques
CONTRAINTES :
- Haute disponibilité
- Performance optimale
- Sécurité renforcée
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-lamp-production.sh

echo "[ECRAN]  INSTALLATION SERVEUR LAMP PRODUCTION"
echo "========================================"

# Variables
PHP_VERSION="8.1"
MYSQL_VERSION="8.0"

# 1. Mise à jour système
echo "1⃣  Mise à jour système..."
sudo apt update
sudo apt upgrade -y

# 2. Installation Apache
echo "2⃣  Installation Apache..."
sudo apt install -y apache2 apache2-utils

# Modules Apache
sudo a2enmod rewrite ssl headers expires deflate http2

# Configuration Apache
sudo tee /etc/apache2/conf-available/security-hardening.conf << 'APACHE'
# Sécurité
ServerTokens Prod
ServerSignature Off
TraceEnable Off

# Performance
Timeout 60
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

# Compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
    AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>

# Cache
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
</IfModule>
APACHE

sudo a2enconf security-hardening
sudo systemctl restart apache2

# 3. Installation MySQL
echo "3⃣  Installation MySQL..."
sudo apt install -y mysql-server mysql-client

# Sécurisation MySQL
sudo mysql << MYSQL
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'CHANGE_ME';
DELETE FROM mysql.user WHERE User='';
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
FLUSH PRIVILEGES;
MYSQL

# Configuration MySQL optimisée
sudo tee -a /etc/mysql/mysql.conf.d/production.cnf << 'MYSQL_CONF'
[mysqld]
# Performance
max_connections = 200
innodb_buffer_pool_size = 2G
innodb_log_file_size = 256M
innodb_flush_log_at_trx_commit = 2

# Sécurité
local-infile = 0
skip-name-resolve
bind-address = 127.0.0.1

# Logs
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
MYSQL_CONF

sudo systemctl restart mysql

# 4. Installation PHP
echo "4⃣  Installation PHP $PHP_VERSION..."
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update

sudo apt install -y \
    php${PHP_VERSION} \
    php${PHP_VERSION}-fpm \
    php${PHP_VERSION}-mysql \
    php${PHP_VERSION}-curl \
    php${PHP_VERSION}-gd \
    php${PHP_VERSION}-mbstring \
    php${PHP_VERSION}-xml \
    php${PHP_VERSION}-zip \
    php${PHP_VERSION}-intl \
    php${PHP_VERSION}-bcmath \
    php${PHP_VERSION}-opcache \
    libapache2-mod-php${PHP_VERSION}

# Configuration PHP optimisée
sudo tee /etc/php/${PHP_VERSION}/apache2/conf.d/99-production.ini << 'PHP'
; Performance
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2

; Sécurité
expose_php = Off
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log

; Limites
max_execution_time = 60
max_input_time = 60
memory_limit = 256M
post_max_size = 64M
upload_max_filesize = 64M
PHP

sudo mkdir -p /var/log/php
sudo chown www-data:www-data /var/log/php

# 5. Composer
echo "5⃣  Installation Composer..."
curl -sS https://getcomposer.org/installer | sudo php -- \
    --install-dir=/usr/local/bin --filename=composer

# 6. Firewall
echo "6⃣  Configuration firewall..."
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

# 7. Fail2ban
echo "7⃣  Installation Fail2ban..."
sudo apt install -y fail2ban

sudo tee /etc/fail2ban/jail.local << 'FAIL2BAN'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

[sshd]
enabled = true

[apache-auth]
enabled = true

[apache-badbots]
enabled = true

[apache-noscript]
enabled = true

[apache-overflows]
enabled = true
FAIL2BAN

sudo systemctl enable fail2ban
sudo systemctl restart fail2ban

# 8. Monitoring avec Apache mod_status
sudo tee /etc/apache2/conf-available/server-status.conf << 'STATUS'
<Location "/server-status">
    SetHandler server-status
    Require local
</Location>

ExtendedStatus On
STATUS

sudo a2enconf server-status
sudo systemctl reload apache2

# 9. Backups automatiques
echo "8⃣  Configuration backups..."
sudo mkdir -p /backups/{mysql,www}

sudo tee /usr/local/bin/lamp-backup.sh << 'BACKUP'
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"

# MySQL
mysqldump --all-databases --single-transaction --quick | \
    gzip > $BACKUP_DIR/mysql/all_databases_${DATE}.sql.gz

# WWW
tar -czf $BACKUP_DIR/www/www_${DATE}.tar.gz /var/www/html

# Rotation (7 jours)
find $BACKUP_DIR/mysql -name "*.sql.gz" -mtime +7 -delete
find $BACKUP_DIR/www -name "*.tar.gz" -mtime +7 -delete
BACKUP

sudo chmod +x /usr/local/bin/lamp-backup.sh

# Cron quotidien
echo "0 2 * * * /usr/local/bin/lamp-backup.sh > /var/log/lamp-backup.log 2>&1" | sudo crontab -

# 10. Création site de test
echo "9⃣  Création site de test..."
sudo mkdir -p /var/www/html/phpinfo
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/phpinfo/index.php

# 11. Health check script
sudo tee /usr/local/bin/lamp-health.sh << 'HEALTH'
#!/bin/bash

echo "[HOPITAL] LAMP Health Check"
echo "==================="

# Apache
if systemctl is-active --quiet apache2; then
    echo "[OK] Apache running"
else
    echo "[X] Apache DOWN"
fi

# MySQL
if systemctl is-active --quiet mysql; then
    echo "[OK] MySQL running"
else
    echo "[X] MySQL DOWN"
fi

# PHP
PHP_VERSION=$(php -v | head -1 | cut -d' ' -f2)
echo "[OK] PHP $PHP_VERSION"

# Disk
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -lt 80 ]; then
    echo "[OK] Disk: $DISK_USAGE%"
else
    echo "[ATTENTION]  Disk: $DISK_USAGE%"
fi

# Connections
MYSQL_CONN=$(mysql -e "SHOW PROCESSLIST;" | wc -l)
echo "[GRAPHIQUE] MySQL connections: $MYSQL_CONN"

APACHE_CONN=$(netstat -an | grep :80 | grep ESTABLISHED | wc -l)
echo "[GRAPHIQUE] Apache connections: $APACHE_CONN"
HEALTH

sudo chmod +x /usr/local/bin/lamp-health.sh

# Récapitulatif
echo ""
echo "[OK] INSTALLATION LAMP TERMINÉE"
echo "=============================="
echo ""
echo "[LISTE] Composants installés :"
echo "   - Apache $(apache2 -v | head -1 | awk '{print $3}')"
echo "   - MySQL $(mysql --version | awk '{print $5}' | sed 's/,//')"
echo "   - PHP $(php -v | head -1 | awk '{print $2}')"
echo "   - Composer $(composer --version | awk '{print $3}')"
echo ""
echo "[SECURISE] Sécurité :"
echo "   - UFW activé (ports 22, 80, 443)"
echo "   - Fail2ban actif"
echo "   - Apache hardening"
echo "   - MySQL sécurisé"
echo ""
echo "[PACKAGE] Backups :"
echo "   - Script : /usr/local/bin/lamp-backup.sh"
echo "   - Cron : Quotidien 2h"
echo "   - Dossier : /backups"
echo ""
echo "[HOPITAL] Health check :"
echo "   /usr/local/bin/lamp-health.sh"
echo ""
echo "[WEB] Tests :"
echo "   http://$(hostname -I | awk '{print $1}')/phpinfo/"
echo ""
echo "[ATTENTION]  TODO :"
echo "   1. Changer mot de passe MySQL root"
echo "   2. Configurer SSL (certbot)"
echo "   3. Créer vhosts Apache"
echo "   4. Configurer monitoring externe"

```

---

### QUAND utiliser ce cas ?

```
[OK] Serveur web production
[OK] Hébergement applications PHP
[OK] CMS (WordPress, Drupal, etc.)
[OK] Applications Laravel, Symfony
[X] Applications Node.js, Python (autres stacks)
```

---

## CAS 2 : [DOCKER] ENVIRONNEMENT DE DÉVELOPPEMENT CONTAINERISÉ

### POURQUOI ce cas ?

```
CONTEXTE : Équipe dev multi-projets
BESOIN :
- Isolation projets
- Versions multiples (Python 3.8, 3.9, 3.10, 3.11)
- Pas de conflits
- Reproductible
- Déploiement rapide
CONTRAINTES :
- Performance
- Facilité d'utilisation
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-dev-containerized.sh

echo "[DOCKER] ENVIRONNEMENT DEV CONTAINERISÉ"
echo "================================="

# 1. Installation Docker
echo "1⃣  Installation Docker..."
sudo apt update
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# Ajouter clé GPG Docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
    sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Ajouter dépôt
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Installer
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Ajouter user au groupe docker
sudo usermod -aG docker $USER

# 2. Docker Compose
echo "2⃣  Installation Docker Compose..."
COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
sudo curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
    -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 3. Créer structure projets
echo "3⃣  Création structure projets..."
mkdir -p ~/dev/{python,nodejs,php,databases}

# 4. Templates Docker Compose

# Python multi-versions
cat > ~/dev/python/docker-compose.yml << 'PYTHON'
version: '3.8'

services:
  python38:
    image: python:3.8
    container_name: dev-python38
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
    
  python39:
    image: python:3.9
    container_name: dev-python39
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
    
  python310:
    image: python:3.10
    container_name: dev-python310
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
    
  python311:
    image: python:3.11
    container_name: dev-python311
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
PYTHON

# Node.js multi-versions
cat > ~/dev/nodejs/docker-compose.yml << 'NODEJS'
version: '3.8'

services:
  node16:
    image: node:16
    container_name: dev-node16
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
    
  node18:
    image: node:18
    container_name: dev-node18
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
    
  node20:
    image: node:20
    container_name: dev-node20
    volumes:
      - ./app:/app
    working_dir: /app
    command: sleep infinity
NODEJS

# Databases
cat > ~/dev/databases/docker-compose.yml << 'DATABASES'
version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: dev-mysql
    environment:
      MYSQL_ROOT_PASSWORD: devpassword
      MYSQL_DATABASE: devdb
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
      
  postgres:
    image: postgres:15
    container_name: dev-postgres
    environment:
      POSTGRES_PASSWORD: devpassword
      POSTGRES_DB: devdb
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
      
  redis:
    image: redis:7
    container_name: dev-redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
      
  mongodb:
    image: mongo:6
    container_name: dev-mongo
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: devpassword
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db

volumes:
  mysql-data:
  postgres-data:
  redis-data:
  mongo-data:
DATABASES

# 5. Scripts helper
cat > ~/dev/dev-env.sh << 'HELPER'
#!/bin/bash

CMD="$1"
shift

case "$CMD" in
    start)
        echo "[RAPIDE] Démarrage environnement $1..."
        cd ~/dev/$1 && docker-compose up -d
        ;;
    stop)
        echo "[BLACK_SQUARE_FOR_STOP]  Arrêt environnement $1..."
        cd ~/dev/$1 && docker-compose down
        ;;
    exec)
        ENV="$1"
        CONTAINER="$2"
        shift 2
        docker exec -it "dev-$CONTAINER" "$@"
        ;;
    logs)
        cd ~/dev/$1 && docker-compose logs -f
        ;;
    status)
        docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
        ;;
    clean)
        docker system prune -af --volumes
        ;;
    *)
        cat << USAGE
Usage: $0 <command> [args]

Commands:
  start <env>           Démarrer environnement (python|nodejs|databases)
  stop <env>            Arrêter environnement
  exec <env> <container> <cmd>   Exécuter commande
  logs <env>            Voir logs
  status                Status containers
  clean                 Nettoyer Docker

Examples:
  $0 start databases
  $0 exec python python310 python script.py
  $0 stop nodejs
USAGE
        ;;
esac
HELPER

chmod +x ~/dev/dev-env.sh
sudo ln -sf ~/dev/dev-env.sh /usr/local/bin/dev

# 6. VSCode devcontainers
mkdir -p ~/dev/.devcontainer

cat > ~/dev/.devcontainer/devcontainer.json << 'VSCODE'
{
    "name": "Dev Environment",
    "dockerComposeFile": "../databases/docker-compose.yml",
    "service": "mysql",
    "workspaceFolder": "/workspace",
    "extensions": [
        "ms-python.python",
        "dbaeumer.vscode-eslint",
        "ms-azuretools.vscode-docker"
    ],
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    }
}
VSCODE

# Récapitulatif
echo ""
echo "[OK] ENVIRONNEMENT DEV CONTAINERISÉ PRÊT"
echo "======================================"
echo ""
echo "[DOSSIER] Structure :"
echo "   ~/dev/python/       - Python 3.8, 3.9, 3.10, 3.11"
echo "   ~/dev/nodejs/       - Node 16, 18, 20"
echo "   ~/dev/databases/    - MySQL, PostgreSQL, Redis, MongoDB"
echo ""
echo "[VIDEO_GAME] Commandes :"
echo "   dev start databases       - Démarrer DBs"
echo "   dev exec python python310 python test.py"
echo "   dev status                - Voir containers"
echo "   dev stop databases"
echo ""
echo "[PYTHON] Python example :"
echo "   cd ~/dev/python/app"
echo "   echo 'print(\"Hello\")' > test.py"
echo "   dev exec python python310 python test.py"
echo ""
echo "[SAUVEGARDE] Databases :"
echo "   MySQL:      localhost:3306 (root/devpassword)"
echo "   PostgreSQL: localhost:5432 (postgres/devpassword)"
echo "   Redis:      localhost:6379"
echo "   MongoDB:    localhost:27017 (root/devpassword)"
echo ""
echo "[ATTENTION]  NOTE : Déconnectez-vous et reconnectez-vous pour groupe docker"

```

---

Veux-tu que je continue avec les **3 cas d'entreprise restants** :
- CAS 3 : Gestion de versions multiples avec pyenv/nvm
- CAS 4 : Déploiement automatisé avec Ansible
- CAS 5 : Dépôt d'entreprise privé

Ou tu préfères que je passe directement aux **bonnes pratiques et troubleshooting** ?

## CAS 3 : [MELANGE] GESTION DE VERSIONS MULTIPLES AVEC PYENV/NVM

### POURQUOI ce cas ?

```
CONTEXTE : Équipe dev travaillant sur plusieurs projets
PROBLÈME :
- Projet A : Python 3.8 + Node 14
- Projet B : Python 3.11 + Node 18
- Projet C : Python 3.10 + Node 20
- Conflits entre versions système
- Installation globale = risques

SOLUTIONS :
- pyenv : Gestionnaire de versions Python
- nvm : Node Version Manager
- rbenv : Ruby version manager (bonus)

AVANTAGES :
[OK] Multiple versions coexistent
[OK] Switch rapide entre projets
[OK] Isolation complète
[OK] Pas de sudo pour installations
[OK] Configuration par projet (.python-version, .nvmrc)
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-version-managers.sh

echo "[MELANGE] INSTALLATION GESTIONNAIRES DE VERSIONS"
echo "=========================================="

# Variables
PYENV_ROOT="$HOME/.pyenv"
NVM_DIR="$HOME/.nvm"
RBENV_ROOT="$HOME/.rbenv"

# =====================================================
# PARTIE 1 : PYENV (Python Version Manager)
# =====================================================

echo ""
echo "[PYTHON] INSTALLATION PYENV"
echo "===================="

# 1. Dépendances de compilation Python
echo "1⃣  Installation dépendances build Python..."
sudo apt update
sudo apt install -y \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libbz2-dev \
    libreadline-dev \
    libsqlite3-dev \
    wget \
    curl \
    llvm \
    libncursesw5-dev \
    xz-utils \
    tk-dev \
    libxml2-dev \
    libxmlsec1-dev \
    libffi-dev \
    liblzma-dev \
    git

# 2. Installer pyenv
echo "2⃣  Installation pyenv..."
if [ -d "$PYENV_ROOT" ]; then
    echo "   [ATTENTION]  pyenv déjà installé, mise à jour..."
    cd "$PYENV_ROOT" && git pull
else
    git clone https://github.com/pyenv/pyenv.git "$PYENV_ROOT"
fi

# 3. Installer pyenv-virtualenv (environnements virtuels)
echo "3⃣  Installation pyenv-virtualenv..."
if [ -d "$PYENV_ROOT/plugins/pyenv-virtualenv" ]; then
    echo "   [ATTENTION]  pyenv-virtualenv déjà installé, mise à jour..."
    cd "$PYENV_ROOT/plugins/pyenv-virtualenv" && git pull
else
    git clone https://github.com/pyenv/pyenv-virtualenv.git \
        "$PYENV_ROOT/plugins/pyenv-virtualenv"
fi

# 4. Configuration shell
echo "4⃣  Configuration shell..."
cat >> ~/.bashrc << 'PYENV_CONFIG'

# ========== PYENV Configuration ==========
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
# =========================================
PYENV_CONFIG

# Charger pour session actuelle
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

# 5. Installer plusieurs versions Python
echo "5⃣  Installation versions Python..."

PYTHON_VERSIONS=("3.8.18" "3.9.18" "3.10.13" "3.11.7" "3.12.1")

for version in "${PYTHON_VERSIONS[@]}"; do
    echo "   [PACKAGE] Installation Python $version..."
    pyenv install -s $version  # -s = skip if already installed
done

# Définir version globale par défaut
pyenv global 3.11.7

echo "   [OK] Versions Python installées :"
pyenv versions

# =====================================================
# PARTIE 2 : NVM (Node Version Manager)
# =====================================================

echo ""
echo "[VERT] INSTALLATION NVM"
echo "=================="

# 1. Installer nvm
echo "1⃣  Installation nvm..."
if [ -d "$NVM_DIR" ]; then
    echo "   [ATTENTION]  nvm déjà installé"
else
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
fi

# 2. Configuration shell (déjà ajouté par install script, mais on s'assure)
cat >> ~/.bashrc << 'NVM_CONFIG'

# ========== NVM Configuration ==========
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
# =======================================
NVM_CONFIG

# Charger pour session actuelle
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# 3. Installer plusieurs versions Node
echo "2⃣  Installation versions Node.js..."

NODE_VERSIONS=("14" "16" "18" "20")

for version in "${NODE_VERSIONS[@]}"; do
    echo "   [PACKAGE] Installation Node $version..."
    nvm install $version
done

# Définir version par défaut
nvm alias default 20

echo "   [OK] Versions Node installées :"
nvm list

# =====================================================
# PARTIE 3 : RBENV (Ruby Version Manager) - BONUS
# =====================================================

echo ""
echo "[GEM_STONE] INSTALLATION RBENV (optionnel)"
echo "================================="

read -p "Installer rbenv pour Ruby ? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    # Dépendances
    sudo apt install -y \
        autoconf \
        bison \
        build-essential \
        libssl-dev \
        libyaml-dev \
        libreadline-dev \
        zlib1g-dev \
        libncurses5-dev \
        libffi-dev \
        libgdbm-dev

    # Installer rbenv
    if [ -d "$RBENV_ROOT" ]; then
        echo "   [ATTENTION]  rbenv déjà installé"
    else
        git clone https://github.com/rbenv/rbenv.git "$RBENV_ROOT"
        git clone https://github.com/rbenv/ruby-build.git \
            "$RBENV_ROOT/plugins/ruby-build"
    fi

    # Configuration
    cat >> ~/.bashrc << 'RBENV_CONFIG'

# ========== RBENV Configuration ==========
export RBENV_ROOT="$HOME/.rbenv"
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init - bash)"
# =========================================
RBENV_CONFIG

    export RBENV_ROOT="$HOME/.rbenv"
    export PATH="$RBENV_ROOT/bin:$PATH"
    eval "$(rbenv init - bash)"

    # Installer versions Ruby
    RUBY_VERSIONS=("3.0.6" "3.1.4" "3.2.2")
    for version in "${RUBY_VERSIONS[@]}"; do
        echo "   [PACKAGE] Installation Ruby $version..."
        rbenv install -s $version
    done
    
    rbenv global 3.2.2
    echo "   [OK] Versions Ruby installées :"
    rbenv versions
fi

# =====================================================
# PARTIE 4 : SCRIPTS UTILITAIRES
# =====================================================

echo ""
echo "[OUTILS]  CRÉATION SCRIPTS UTILITAIRES"
echo "==============================="

# Script : version-manager.sh
cat > ~/.local/bin/vm << 'VM_SCRIPT'
#!/bin/bash
# Version Manager Helper

CMD="$1"
shift

case "$CMD" in
    list)
        echo "[PYTHON] Python versions (pyenv):"
        pyenv versions
        echo ""
        echo "[VERT] Node.js versions (nvm):"
        nvm list
        [ -x "$(command -v rbenv)" ] && {
            echo ""
            echo "[GEM_STONE] Ruby versions (rbenv):"
            rbenv versions
        }
        ;;
        
    install)
        LANG="$1"
        VERSION="$2"
        case "$LANG" in
            python|py)
                echo "[PACKAGE] Installation Python $VERSION..."
                pyenv install $VERSION
                ;;
            node|nodejs)
                echo "[PACKAGE] Installation Node.js $VERSION..."
                nvm install $VERSION
                ;;
            ruby|rb)
                echo "[PACKAGE] Installation Ruby $VERSION..."
                rbenv install $VERSION
                ;;
            *)
                echo "[X] Langage non reconnu: $LANG"
                echo "Usage: vm install <python|node|ruby> <version>"
                exit 1
                ;;
        esac
        ;;
        
    use)
        LANG="$1"
        VERSION="$2"
        case "$LANG" in
            python|py)
                pyenv global $VERSION
                echo "[OK] Python $VERSION activé globalement"
                ;;
            node|nodejs)
                nvm use $VERSION
                echo "[OK] Node.js $VERSION activé"
                ;;
            ruby|rb)
                rbenv global $VERSION
                echo "[OK] Ruby $VERSION activé globalement"
                ;;
            *)
                echo "[X] Langage non reconnu: $LANG"
                exit 1
                ;;
        esac
        ;;
        
    current)
        echo "[PYTHON] Python: $(python --version 2>&1)"
        echo "[VERT] Node.js: $(node --version 2>&1)"
        echo "[PACKAGE] npm: $(npm --version 2>&1)"
        [ -x "$(command -v ruby)" ] && echo "[GEM_STONE] Ruby: $(ruby --version)"
        ;;
        
    update)
        echo "[SYNC] Mise à jour gestionnaires de versions..."
        
        # pyenv
        echo "Mise à jour pyenv..."
        cd "$HOME/.pyenv" && git pull
        cd "$HOME/.pyenv/plugins/pyenv-virtualenv" && git pull
        
        # nvm
        echo "Mise à jour nvm..."
        (
            cd "$NVM_DIR"
            git fetch --tags origin
            git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" $(git rev-list --tags --max-count=1)`
        ) && \. "$NVM_DIR/nvm.sh"
        
        # rbenv
        [ -d "$HOME/.rbenv" ] && {
            echo "Mise à jour rbenv..."
            cd "$HOME/.rbenv" && git pull
            cd "$HOME/.rbenv/plugins/ruby-build" && git pull
        }
        
        echo "[OK] Mises à jour terminées"
        ;;
        
    *)
        cat << USAGE
[MELANGE] Version Manager Helper

Usage: vm <command> [args]

Commands:
  list                           Liste toutes les versions installées
  install <lang> <version>       Installe une version
  use <lang> <version>           Active une version globalement
  current                        Affiche versions actuelles
  update                         Met à jour les gestionnaires

Languages: python|py, node|nodejs, ruby|rb

Examples:
  vm list
  vm install python 3.12.1
  vm use node 20
  vm current
  vm update
USAGE
        ;;
esac
VM_SCRIPT

mkdir -p ~/.local/bin
chmod +x ~/.local/bin/vm

# Ajouter ~/.local/bin au PATH si pas déjà fait
grep -q 'export PATH="$HOME/.local/bin:$PATH"' ~/.bashrc || \
    echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc

# =====================================================
# PARTIE 5 : CONFIGURATION PAR PROJET
# =====================================================

echo ""
echo "[DOSSIER] EXEMPLE CONFIGURATION PAR PROJET"
echo "===================================="

# Créer projet exemple
mkdir -p ~/projects/example-project
cd ~/projects/example-project

# .python-version (pyenv auto-switch)
echo "3.11.7" > .python-version

# .nvmrc (nvm auto-switch)
echo "20" > .nvmrc

# .ruby-version (rbenv auto-switch)
[ -x "$(command -v rbenv)" ] && echo "3.2.2" > .ruby-version

# Script d'activation automatique
cat > setup-env.sh << 'ENV'
#!/bin/bash
# Auto-activation environnement projet

# Python
if [ -f .python-version ]; then
    PYTHON_VERSION=$(cat .python-version)
    echo "[PYTHON] Activation Python $PYTHON_VERSION..."
    pyenv local $PYTHON_VERSION
    
    # Créer virtualenv si n'existe pas
    VENV_NAME="$(basename $PWD)-py$(echo $PYTHON_VERSION | cut -d. -f1,2)"
    if ! pyenv virtualenvs | grep -q $VENV_NAME; then
        echo "[PACKAGE] Création virtualenv $VENV_NAME..."
        pyenv virtualenv $PYTHON_VERSION $VENV_NAME
    fi
    pyenv local $VENV_NAME
fi

# Node.js
if [ -f .nvmrc ]; then
    NODE_VERSION=$(cat .nvmrc)
    echo "[VERT] Activation Node.js $NODE_VERSION..."
    nvm use $NODE_VERSION || nvm install $NODE_VERSION
fi

# Ruby
if [ -f .ruby-version ] && [ -x "$(command -v rbenv)" ]; then
    RUBY_VERSION=$(cat .ruby-version)
    echo "[GEM_STONE] Activation Ruby $RUBY_VERSION..."
    rbenv local $RUBY_VERSION
fi

echo ""
echo "[OK] Environnement projet activé"
python --version 2>&1
node --version 2>&1
npm --version 2>&1
[ -x "$(command -v ruby)" ] && ruby --version
ENV

chmod +x setup-env.sh

# Créer requirements.txt exemple
cat > requirements.txt << 'REQS'
# Dépendances Python
requests==2.31.0
flask==3.0.0
pytest==7.4.3
black==23.12.1
REQS

# Créer package.json exemple
cat > package.json << 'PKG'
{
  "name": "example-project",
  "version": "1.0.0",
  "description": "Example project with version managers",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "jest"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "jest": "^29.7.0"
  }
}
PKG

echo "   [OK] Projet exemple créé : ~/projects/example-project"

# =====================================================
# PARTIE 6 : AUTO-ACTIVATION AVEC direnv (optionnel)
# =====================================================

echo ""
echo "[OUTIL] INSTALLATION DIRENV (optionnel)"
echo "=================================="

read -p "Installer direnv pour auto-activation ? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    sudo apt install -y direnv
    
    # Hook direnv dans bashrc
    echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
    
    # .envrc exemple pour projet
    cat > ~/projects/example-project/.envrc << 'ENVRC'
# Auto-activation versions avec direnv

# Python
if [ -f .python-version ]; then
    PYTHON_VERSION=$(cat .python-version)
    pyenv local $PYTHON_VERSION
    
    VENV_NAME="$(basename $PWD)-py$(echo $PYTHON_VERSION | cut -d. -f1,2)"
    if ! pyenv virtualenvs | grep -q $VENV_NAME; then
        pyenv virtualenv $PYTHON_VERSION $VENV_NAME
    fi
    pyenv local $VENV_NAME
fi

# Node.js
if [ -f .nvmrc ]; then
    use node $(cat .nvmrc)
fi

# Variables d'environnement projet
export PROJECT_ENV=development
export DEBUG=true

echo "[OK] Environnement projet chargé automatiquement"
ENVRC

    echo "   [OK] direnv installé"
    echo "   [ATTENTION]  Autorisez .envrc : cd ~/projects/example-project && direnv allow"
fi

# =====================================================
# PARTIE 7 : DOCUMENTATION
# =====================================================

echo ""
echo "[DOCS] CRÉATION DOCUMENTATION"
echo "========================="

cat > ~/VERSION_MANAGERS_GUIDE.md << 'GUIDE'
# [MELANGE] Guide des Gestionnaires de Versions

## [PYTHON] PYENV - Python Version Manager

### Installation versions
```bash
# Lister versions disponibles
pyenv install --list

# Installer version spécifique
pyenv install 3.11.7

# Lister versions installées
pyenv versions
```

### Utilisation
```bash
# Version globale (toutes les sessions)
pyenv global 3.11.7

# Version locale (dossier courant)
pyenv local 3.10.13

# Version pour session shell uniquement
pyenv shell 3.9.18

# Vérifier version active
pyenv version
```

### Virtualenvs
```bash
# Créer virtualenv
pyenv virtualenv 3.11.7 myproject-env

# Activer
pyenv activate myproject-env

# Désactiver
pyenv deactivate

# Lister virtualenvs
pyenv virtualenvs

# Supprimer
pyenv virtualenv-delete myproject-env
```

### Configuration par projet
```bash
# Créer .python-version
echo "3.11.7" > .python-version

# Auto-switch en entrant dans le dossier
cd /path/to/project  # Active automatiquement 3.11.7
```

---

## [VERT] NVM - Node Version Manager

### Installation versions
```bash
# Lister versions disponibles
nvm ls-remote

# Installer dernière version
nvm install node

# Installer version LTS
nvm install --lts

# Installer version spécifique
nvm install 18.19.0
```

### Utilisation
```bash
# Utiliser version
nvm use 18

# Version par défaut
nvm alias default 18

# Lister versions installées
nvm list

# Version actuelle
nvm current
```

### Configuration par projet
```bash
# Créer .nvmrc
echo "18" > .nvmrc

# Auto-use
nvm use  # Lit .nvmrc automatiquement
```

### Packages globaux
```bash
# Installer package global
npm install -g yarn pm2

# Lister packages globaux
npm list -g --depth=0

# Copier packages entre versions
nvm install 20 --reinstall-packages-from=18
```

---

## [GEM_STONE] RBENV - Ruby Version Manager

### Installation versions
```bash
# Lister versions disponibles
rbenv install --list

# Installer version
rbenv install 3.2.2

# Lister versions installées
rbenv versions
```

### Utilisation
```bash
# Version globale
rbenv global 3.2.2

# Version locale
rbenv local 3.1.4

# Version shell
rbenv shell 3.0.6
```

---

## [OUTILS] COMMANDES UTILES

### Script vm (helper)
```bash
# Lister toutes les versions
vm list

# Installer version
vm install python 3.12.1
vm install node 20

# Activer version
vm use python 3.11.7
vm use node 18

# Versions actuelles
vm current

# Mettre à jour gestionnaires
vm update
```

---

## [DOSSIER] WORKFLOW PROJET

### Nouveau projet Python
```bash
mkdir myproject && cd myproject

# Définir version Python
echo "3.11.7" > .python-version

# Créer virtualenv
pyenv virtualenv 3.11.7 myproject-env
pyenv local myproject-env

# Installer dépendances
pip install -r requirements.txt
```

### Nouveau projet Node.js
```bash
mkdir myproject && cd myproject

# Définir version Node
echo "20" > .nvmrc

# Activer
nvm use

# Initialiser projet
npm init -y
npm install express
```

---

## [OUTIL] TROUBLESHOOTING

### Python : command not found après installation
```bash
# Vérifier PATH
echo $PATH | grep pyenv

# Recharger shell
exec $SHELL

# Réinitialiser pyenv
pyenv rehash
```

### Node : version not found
```bash
# Mettre à jour nvm
cd "$NVM_DIR" && git pull && exec $SHELL
```

### Compilation Python échoue
```bash
# Installer toutes les dépendances
sudo apt update
sudo apt install -y build-essential libssl-dev zlib1g-dev \
    libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
    libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
    libffi-dev liblzma-dev

# Réessayer
pyenv install 3.11.7
```

---

## [IDEE] BONNES PRATIQUES

1. **Un virtualenv par projet Python**
   ```bash
   pyenv virtualenv $(cat .python-version) $(basename $PWD)-env
   pyenv local $(basename $PWD)-env
   ```

2. **Fichiers de version dans git**
   ```bash
   git add .python-version .nvmrc
   ```

3. **Documentation dépendances**
   - Python: `requirements.txt` ou `Pipfile`
   - Node: `package.json` avec versions exactes

4. **Script setup automatique**
   ```bash
   # setup-env.sh dans chaque projet
   ./setup-env.sh  # Active tout automatiquement
   ```

5. **Utiliser direnv pour auto-activation**
   ```bash
   # .envrc par projet
   direnv allow
   ```

---

## [GRAPHIQUE] COMPARAISON

| Gestionnaire | Langage | Auto-switch | Virtualenvs | Popularité |
|--------------|---------|-------------|-------------|------------|
| pyenv        | Python  | [OK] (.python-version) | [OK] | ***** |
| nvm          | Node.js | [OK] (.nvmrc) | [X] (pas nécessaire) | ***** |
| rbenv        | Ruby    | [OK] (.ruby-version) | [X] (gems) | **** |
| asdf         | Multi   | [OK] (.tool-versions) | Selon plugin | *** |

---

## [LIEN] RESSOURCES

- pyenv: https://github.com/pyenv/pyenv
- nvm: https://github.com/nvm-sh/nvm
- rbenv: https://github.com/rbenv/rbenv
- direnv: https://direnv.net/

GUIDE

echo "   [OK] Guide créé : ~/VERSION_MANAGERS_GUIDE.md"

# =====================================================
# RÉCAPITULATIF
# =====================================================

echo ""
echo "[OK] INSTALLATION TERMINÉE"
echo "========================"
echo ""
echo "[PACKAGE] Installé :"
echo "   [PYTHON] pyenv - Python Version Manager"
echo "   [VERT] nvm - Node Version Manager"
[ -x "$(command -v rbenv)" ] && echo "   [GEM_STONE] rbenv - Ruby Version Manager"
echo ""
echo "[PYTHON] Versions Python :"
pyenv versions | sed 's/^/   /'
echo ""
echo "[VERT] Versions Node.js :"
nvm list | sed 's/^/   /'
echo ""
[ -x "$(command -v rbenv)" ] && {
    echo "[GEM_STONE] Versions Ruby :"
    rbenv versions | sed 's/^/   /'
    echo ""
}
echo "[OUTILS]  Commande helper : vm"
echo "   vm list        - Liste versions"
echo "   vm install python 3.12.1"
echo "   vm use node 20"
echo "   vm current"
echo "   vm update"
echo ""
echo "[DOSSIER] Projet exemple : ~/projects/example-project"
echo "   cd ~/projects/example-project"
echo "   ./setup-env.sh"
echo ""
echo "[DOCS] Guide complet : ~/VERSION_MANAGERS_GUIDE.md"
echo "   cat ~/VERSION_MANAGERS_GUIDE.md"
echo ""
echo "[ATTENTION]  IMPORTANT :"
echo "   Redémarrez votre terminal ou exécutez :"
echo "   source ~/.bashrc"
echo ""
echo "[BRAVO] Vous pouvez maintenant gérer plusieurs versions facilement !"

```

---

### Scripts d'automatisation avancés

**Script 1 : Auto-setup projet existant**

```bash
#!/bin/bash
# auto-setup-project.sh
# Détecte et configure automatiquement l'environnement d'un projet

PROJECT_DIR="${1:-.}"

cd "$PROJECT_DIR" || exit 1

echo "[RECHERCHE] Analyse du projet : $(basename $PWD)"
echo "======================================"

# Détecter et configurer Python
if [ -f "requirements.txt" ] || [ -f "Pipfile" ] || [ -f "pyproject.toml" ]; then
    echo ""
    echo "[PYTHON] Projet Python détecté"
    
    # Chercher version dans fichiers
    PYTHON_VERSION=""
    
    # .python-version existe ?
    if [ -f ".python-version" ]; then
        PYTHON_VERSION=$(cat .python-version)
        echo "   Version trouvée : $PYTHON_VERSION (.python-version)"
    
    # pyproject.toml (Poetry, etc.)
    elif [ -f "pyproject.toml" ] && grep -q "python" pyproject.toml; then
        # Extraire version de requires-python
        PYTHON_VERSION=$(grep "requires-python" pyproject.toml | sed 's/.*"\^\?\([0-9.]*\).*/\1/')
        echo "   Version trouvée : $PYTHON_VERSION (pyproject.toml)"
        echo "$PYTHON_VERSION" > .python-version
    
    # Pipfile
    elif [ -f "Pipfile" ] && grep -q "python_version" Pipfile; then
        PYTHON_VERSION=$(grep python_version Pipfile | sed 's/.*"\([0-9.]*\)".*/\1/')
        echo "   Version trouvée : $PYTHON_VERSION (Pipfile)"
        echo "$PYTHON_VERSION" > .python-version
    
    # Demander
    else
        echo "   [ATTENTION]  Version Python non détectée"
        read -p "   Entrez version Python (ex: 3.11): " PYTHON_VERSION
        echo "$PYTHON_VERSION" > .python-version
    fi
    
    # Installer version si pas disponible
    if ! pyenv versions | grep -q "$PYTHON_VERSION"; then
        echo "   [PACKAGE] Installation Python $PYTHON_VERSION..."
        pyenv install $PYTHON_VERSION
    fi
    
    # Créer virtualenv
    VENV_NAME="$(basename $PWD)-py${PYTHON_VERSION}"
    if ! pyenv virtualenvs | grep -q "$VENV_NAME"; then
        echo "   [PACKAGE] Création virtualenv $VENV_NAME..."
        pyenv virtualenv $PYTHON_VERSION $VENV_NAME
    fi
    
    pyenv local $VENV_NAME
    
    # Installer dépendances
    if [ -f "requirements.txt" ]; then
        echo "   [PACKAGE] Installation dépendances (requirements.txt)..."
        pip install -r requirements.txt
    elif [ -f "Pipfile" ]; then
        echo "   [PACKAGE] Installation dépendances (Pipfile)..."
        pip install pipenv
        pipenv install
    elif [ -f "pyproject.toml" ]; then
        echo "   [PACKAGE] Installation dépendances (pyproject.toml)..."
        pip install poetry
        poetry install
    fi
    
    echo "   [OK] Environnement Python prêt"
fi

# Détecter et configurer Node.js
if [ -f "package.json" ]; then
    echo ""
    echo "[VERT] Projet Node.js détecté"
    
    # Chercher version
    NODE_VERSION=""
    
    if [ -f ".nvmrc" ]; then
        NODE_VERSION=$(cat .nvmrc)
        echo "   Version trouvée : $NODE_VERSION (.nvmrc)"
    elif grep -q "\"node\":" package.json; then
        # Extraire de engines.node
        NODE_VERSION=$(grep "\"node\":" package.json | sed 's/.*"\^\?\([0-9]*\).*/\1/')
        echo "   Version trouvée : $NODE_VERSION (package.json)"
        echo "$NODE_VERSION" > .nvmrc
    else
        echo "   [ATTENTION]  Version Node non détectée, utilisation de LTS"
        NODE_VERSION="--lts"
        nvm install --lts
        nvm current > .nvmrc
    fi
    
    # Installer version
    if [ "$NODE_VERSION" != "--lts" ]; then
        nvm install $NODE_VERSION
        nvm use $NODE_VERSION
    fi
    
    # Installer dépendances
    if [ -f "package-lock.json" ]; then
        echo "   [PACKAGE] Installation dépendances (npm)..."
        npm ci
    elif [ -f "yarn.lock" ]; then
        echo "   [PACKAGE] Installation dépendances (yarn)..."
        npm install -g yarn
        yarn install
    elif [ -f "pnpm-lock.yaml" ]; then
        echo "   [PACKAGE] Installation dépendances (pnpm)..."
        npm install -g pnpm
        pnpm install
    else
        echo "   [PACKAGE] Installation dépendances (npm)..."
        npm install
    fi
    
    echo "   [OK] Environnement Node.js prêt"
fi

# Détecter Ruby
if [ -f "Gemfile" ]; then
    echo ""
    echo "[GEM_STONE] Projet Ruby détecté"
    
    if [ -x "$(command -v rbenv)" ]; then
        RUBY_VERSION=""
        
        if [ -f ".ruby-version" ]; then
            RUBY_VERSION=$(cat .ruby-version)
        else
            echo "   [ATTENTION]  Version Ruby non détectée"
            read -p "   Entrez version Ruby (ex: 3.2.2): " RUBY_VERSION
            echo "$RUBY_VERSION" > .ruby-version
        fi
        
        if ! rbenv versions | grep -q "$RUBY_VERSION"; then
            echo "   [PACKAGE] Installation Ruby $RUBY_VERSION..."
            rbenv install $RUBY_VERSION
        fi
        
        rbenv local $RUBY_VERSION
        
        echo "   [PACKAGE] Installation gems..."
        gem install bundler
        bundle install
        
        echo "   [OK] Environnement Ruby prêt"
    else
        echo "   [ATTENTION]  rbenv non installé, installation ignorée"
    fi
fi

# Créer script d'activation
cat > .activate-env.sh << 'ACTIVATE'
#!/bin/bash
# Auto-généré par auto-setup-project.sh

echo "[SYNC] Activation environnement projet..."

# Python
[ -f .python-version ] && {
    pyenv local $(cat .python-version) 2>/dev/null || \
    pyenv activate $(cat .python-version) 2>/dev/null
}

# Node.js
[ -f .nvmrc ] && nvm use 2>/dev/null

# Ruby
[ -f .ruby-version ] && rbenv local $(cat .ruby-version) 2>/dev/null

echo "[OK] Environnement activé"
python --version 2>&1 | grep -q "Python" && python --version
node --version 2>&1 | grep -q "v" && node --version
ACTIVATE

chmod +x .activate-env.sh

echo ""
echo "[OK] CONFIGURATION PROJET TERMINÉE"
echo "==============================="
echo ""
echo "[DOSSIER] Fichiers créés :"
[ -f .python-version ] && echo "   .python-version"
[ -f .nvmrc ] && echo "   .nvmrc"
[ -f .ruby-version ] && echo "   .ruby-version"
echo "   .activate-env.sh"
echo ""
echo "[RAPIDE] Pour activer l'environnement :"
echo "   source .activate-env.sh"
echo ""
echo "   OU utiliser direnv :"
echo "   direnv allow"

```

---

**Script 2 : Synchronisation entre machines**

```bash
#!/bin/bash
# sync-versions.sh
# Synchronise les versions installées entre machines

ACTION="${1:-export}"
FILE="${2:-versions-backup.txt}"

case "$ACTION" in
    export)
        echo "[SORTIE] Export des versions installées..."
        
        {
            echo "# Versions installées - $(date)"
            echo ""
            
            echo "[PYENV]"
            pyenv versions --bare
            echo ""
            
            echo "[NVM]"
            nvm list | grep -v "^->" | grep "v" | sed 's/.*v//'
            echo ""
            
            if [ -x "$(command -v rbenv)" ]; then
                echo "[RBENV]"
                rbenv versions --bare
                echo ""
            fi
            
        } > "$FILE"
        
        echo "[OK] Versions exportées vers : $FILE"
        cat "$FILE"
        ;;
        
    import)
        echo "[ENTREE] Import et installation des versions..."
        
        if [ ! -f "$FILE" ]; then
            echo "[X] Fichier $FILE non trouvé"
            exit 1
        fi
        
        SECTION=""
        while IFS= read -r line; do
            # Ignorer commentaires et lignes vides
            [[ "$line" =~ ^#.*$ ]] && continue
            [[ -z "$line" ]] && continue
            
            # Détecter section
            if [[ "$line" =~ ^\[.*\]$ ]]; then
                SECTION=$(echo "$line" | tr -d '[]')
                continue
            fi
            
            # Installer selon section
            case "$SECTION" in
                PYENV)
                    echo "[PYTHON] Installation Python $line..."
                    pyenv install -s "$line"
                    ;;
                NVM)
                    echo "[VERT] Installation Node.js $line..."
                    nvm install "$line"
                    ;;
                RBENV)
                    if [ -x "$(command -v rbenv)" ]; then
                        echo "[GEM_STONE] Installation Ruby $line..."
                        rbenv install -s "$line"
                    fi
                    ;;
            esac
        done < "$FILE"
        
        echo "[OK] Import terminé"
        ;;
        
    *)
        cat << USAGE
Usage: $0 <export|import> [fichier]

Commands:
  export [fichier]    Exporte les versions installées
  import [fichier]    Installe les versions depuis fichier

Examples:
  $0 export versions.txt
  $0 import versions.txt
USAGE
        ;;
esac
```

---

**Script 3 : Nettoyage versions obsolètes**

```bash
#!/bin/bash
# cleanup-versions.sh
# Nettoie les anciennes versions non utilisées

echo "[NETTOYAGE] NETTOYAGE VERSIONS NON UTILISÉES"
echo "==================================="

# Python
echo ""
echo "[PYTHON] Python (pyenv):"
CURRENT_PYTHON=$(pyenv global)
echo "   Version globale : $CURRENT_PYTHON"

pyenv versions --bare | while read version; do
    # Skip si c'est la version globale
    [[ "$version" == "$CURRENT_PYTHON" ]] && continue
    
    # Chercher si utilisée dans des projets
    USED=$(find ~/projects -name ".python-version" -exec grep -l "^${version}$" {} \; 2>/dev/null | wc -l)
    
    if [ $USED -eq 0 ]; then
        echo "   [ATTENTION]  $version non utilisée"
        read -p "   Supprimer ? (y/N) " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            pyenv uninstall -f "$version"
            echo "   [OK] $version supprimée"
        fi
    else
        echo "   [OK] $version utilisée dans $USED projet(s)"
    fi
done

# Node.js
echo ""
echo "[VERT] Node.js (nvm):"
CURRENT_NODE=$(nvm current)
echo "   Version courante : $CURRENT_NODE"

nvm list | grep -v "^->" | grep "v" | sed 's/.*v//' | while read version; do
    [[ "v$version" == "$CURRENT_NODE" ]] && continue
    
    USED=$(find ~/projects -name ".nvmrc" -exec grep -l "^${version}$" {} \; 2>/dev/null | wc -l)
    
    if [ $USED -eq 0 ]; then
        echo "   [ATTENTION]  v$version non utilisée"
        read -p "   Supprimer ? (y/N) " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            nvm uninstall "$version"
            echo "   [OK] v$version supprimée"
        fi
    else
        echo "   [OK] v$version utilisée dans $USED projet(s)"
    fi
done

echo ""
echo "[OK] Nettoyage terminé"
```

---

### QUAND utiliser ce cas ?

```
[OK] Équipes dev multi-projets
[OK] Freelances avec clients différents
[OK] Maintenance legacy + nouveaux projets
[OK] Tests compatibilité multi-versions
[OK] CI/CD avec plusieurs versions
[X] Projet unique version fixe
[X] Serveurs production (Docker préférable)
```

---

### Avantages vs Docker

```
PYENV/NVM :
[OK] Plus léger (pas de containers)
[OK] Performance native
[OK] Intégration IDE facilitée
[OK] Moins de RAM
[OK] Bon pour développement local

DOCKER :
[OK] Isolation complète
[OK] Reproductibilité exacte
[OK] Bon pour production
[OK] Multi-services facilement
[OK] Portabilité totale
```

---

## [COURS] CONCLUSION CAS 3

**[OK] Tu sais maintenant :**
- Installer et gérer pyenv, nvm, rbenv
- Switcher facilement entre versions
- Configurer projets avec auto-activation
- Synchroniser versions entre machines
- Nettoyer versions obsolètes
- Automatiser setup projets

**Temps moyen :** 1h

---

Tu veux continuer avec les **2 cas d'entreprise restants** :
- CAS 4 : Déploiement automatisé avec Ansible
- CAS 5 : Dépôt d'entreprise privé

Ou passer directement aux **bonnes pratiques, troubleshooting et conclusion** ?

## CAS 5 : [PACKAGE] DÉPÔT D'ENTREPRISE PRIVÉ

### POURQUOI un dépôt privé ?

```
CONTEXTE : Entreprise avec applications internes
PROBLÈMES :
[ROUGE] Paquets internes non publiables publiquement
[ROUGE] Versions spécifiques/patchées nécessaires
[ROUGE] Contrôle des versions déployées
[ROUGE] Dépendance aux dépôts externes (indisponibilité)
[ROUGE] Bande passante limitée (téléchargements répétés)
[ROUGE] Conformité/sécurité (scan avant déploiement)

SOLUTIONS :
[OK] Dépôt APT privé (Debian/Ubuntu)
[OK] Dépôt YUM/DNF privé (RHEL/CentOS)
[OK] PyPI privé (Python)
[OK] NPM privé (Node.js)
[OK] Mirroring dépôts publics

AVANTAGES :
[OK] Contrôle total des versions
[OK] Déploiement rapide (réseau local)
[OK] Disponibilité garantie
[OK] Audit et conformité
[OK] Patch security personnalisés
[OK] Économie bande passante
```

---

### COMMENT implémenter ?

## PARTIE 1 : DÉPÔT APT PRIVÉ (Debian/Ubuntu)

```bash
#!/bin/bash
# setup-private-apt-repo.sh

echo "[PACKAGE] CRÉATION DÉPÔT APT PRIVÉ D'ENTREPRISE"
echo "========================================"

# Variables de configuration
REPO_NAME="company-repo"
REPO_ROOT="/var/www/apt"
REPO_DOMAIN="repo.company.internal"
GPG_KEY_NAME="Company APT Repository"
GPG_KEY_EMAIL="sysadmin@company.com"
DISTRIBUTIONS=("focal" "jammy" "noble")  # Ubuntu 20.04, 22.04, 24.04
COMPONENTS=("main" "internal" "testing")

# =====================================================
# ÉTAPE 1 : INSTALLATION ET CONFIGURATION
# =====================================================

echo ""
echo "1⃣  INSTALLATION DES OUTILS"
echo "=========================="

# Installer les outils nécessaires
sudo apt update
sudo apt install -y \
    nginx \
    dpkg-dev \
    dpkg-sig \
    reprepro \
    gnupg \
    apache2-utils \
    curl

# Créer structure de répertoires
echo ""
echo "2⃣  CRÉATION STRUCTURE RÉPERTOIRES"
echo "================================="

sudo mkdir -p "$REPO_ROOT"/{conf,incoming,logs}
sudo mkdir -p "$REPO_ROOT"/dists
sudo mkdir -p "$REPO_ROOT"/pool

# =====================================================
# ÉTAPE 2 : GÉNÉRATION CLÉ GPG
# =====================================================

echo ""
echo "3⃣  GÉNÉRATION CLÉ GPG"
echo "===================="

# Vérifier si clé existe déjà
if gpg --list-keys | grep -q "$GPG_KEY_EMAIL"; then
    echo "   [OK] Clé GPG existe déjà"
    GPG_KEY_ID=$(gpg --list-keys --keyid-format LONG "$GPG_KEY_EMAIL" | grep pub | awk '{print $2}' | cut -d'/' -f2)
else
    echo "   [CLE] Génération nouvelle clé GPG..."
    
    # Configuration GPG batch
    cat > /tmp/gpg-batch << EOF
%echo Generating APT repository key
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: $GPG_KEY_NAME
Name-Email: $GPG_KEY_EMAIL
Expire-Date: 0
%no-protection
%commit
%echo Done
EOF

    # Générer la clé
    gpg --batch --generate-key /tmp/gpg-batch
    rm /tmp/gpg-batch
    
    # Récupérer l'ID
    GPG_KEY_ID=$(gpg --list-keys --keyid-format LONG "$GPG_KEY_EMAIL" | grep pub | awk '{print $2}' | cut -d'/' -f2)
    
    echo "   [OK] Clé générée : $GPG_KEY_ID"
fi

# Exporter la clé publique
gpg --armor --export "$GPG_KEY_ID" | sudo tee "$REPO_ROOT/company-repo.gpg.key" > /dev/null

echo "   [FICHIER] Clé publique : $REPO_ROOT/company-repo.gpg.key"

# =====================================================
# ÉTAPE 3 : CONFIGURATION REPREPRO
# =====================================================

echo ""
echo "4⃣  CONFIGURATION REPREPRO"
echo "========================="

# Fichier de configuration principal
sudo tee "$REPO_ROOT/conf/distributions" << EOF
# Ubuntu 20.04 LTS (Focal)
Origin: Company Repository
Label: Company APT Repository
Codename: focal
Architectures: amd64 arm64 source
Components: main internal testing
Description: Company private packages for Ubuntu 20.04
SignWith: $GPG_KEY_ID

# Ubuntu 22.04 LTS (Jammy)
Origin: Company Repository
Label: Company APT Repository
Codename: jammy
Architectures: amd64 arm64 source
Components: main internal testing
Description: Company private packages for Ubuntu 22.04
SignWith: $GPG_KEY_ID

# Ubuntu 24.04 LTS (Noble)
Origin: Company Repository
Label: Company APT Repository
Codename: noble
Architectures: amd64 arm64 source
Components: main internal testing
Description: Company private packages for Ubuntu 24.04
SignWith: $GPG_KEY_ID
EOF

# Configuration des options
sudo tee "$REPO_ROOT/conf/options" << EOF
verbose
ask-passphrase
basedir $REPO_ROOT
EOF

# Configuration incoming (upload automatique)
sudo tee "$REPO_ROOT/conf/incoming" << EOF
Name: incoming
IncomingDir: incoming
TempDir: /tmp
Allow: focal jammy noble
Cleanup: on_deny on_error
EOF

echo "   [OK] Configuration reprepro créée"

# =====================================================
# ÉTAPE 4 : CONFIGURATION NGINX
# =====================================================

echo ""
echo "5⃣  CONFIGURATION NGINX"
echo "====================="

# Configuration Nginx avec authentification
sudo tee /etc/nginx/sites-available/apt-repo << EOF
server {
    listen 80;
    server_name $REPO_DOMAIN;
    
    # Redirection HTTPS (à activer en production)
    # return 301 https://\$server_name\$request_uri;
    
    root $REPO_ROOT;
    
    # Logs
    access_log $REPO_ROOT/logs/access.log;
    error_log $REPO_ROOT/logs/error.log;
    
    # Autoindex pour navigation
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
    
    # Headers de sécurité
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # Clé GPG publique (sans auth)
    location ~ ^/[^/]+\.gpg\.key$ {
        auth_basic off;
    }
    
    # Métadonnées publiques (sans auth)
    location ~ ^/dists/.*/Release$ {
        auth_basic off;
    }
    
    location ~ ^/dists/.*/Release\.gpg$ {
        auth_basic off;
    }
    
    location ~ ^/dists/.*/InRelease$ {
        auth_basic off;
    }
    
    # Authentification pour téléchargements
    location ~ ^/pool/ {
        auth_basic "Company APT Repository";
        auth_basic_user_file /etc/nginx/.htpasswd-apt;
    }
    
    # Authentification pour packages lists
    location ~ ^/dists/.*/Packages {
        auth_basic "Company APT Repository";
        auth_basic_user_file /etc/nginx/.htpasswd-apt;
    }
    
    # Upload de paquets (restrict to specific IPs)
    location /incoming/ {
        # Limiter aux IPs internes
        allow 192.168.0.0/16;
        allow 10.0.0.0/8;
        deny all;
        
        client_max_body_size 1G;
        auth_basic "APT Repository Upload";
        auth_basic_user_file /etc/nginx/.htpasswd-apt-upload;
        
        dav_methods PUT;
        create_full_put_path on;
        dav_access user:rw group:rw all:r;
    }
}

# HTTPS (production)
# server {
#     listen 443 ssl http2;
#     server_name $REPO_DOMAIN;
#     
#     ssl_certificate /etc/ssl/certs/apt-repo.crt;
#     ssl_certificate_key /etc/ssl/private/apt-repo.key;
#     
#     # Même config que HTTP...
# }
EOF

# Activer le site
sudo ln -sf /etc/nginx/sites-available/apt-repo /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

# Créer utilisateurs pour authentification
echo ""
echo "   [SECURISE] Création utilisateurs d'accès..."

# Utilisateur lecture
sudo htpasswd -cb /etc/nginx/.htpasswd-apt apt-reader "reader_password_123"
echo "      [OK] Utilisateur lecture : apt-reader / reader_password_123"

# Utilisateur upload
sudo htpasswd -cb /etc/nginx/.htpasswd-apt-upload apt-uploader "uploader_password_456"
echo "      [OK] Utilisateur upload : apt-uploader / uploader_password_456"

# Permissions
sudo chown -R www-data:www-data "$REPO_ROOT"
sudo chmod -R 755 "$REPO_ROOT"
sudo chmod 775 "$REPO_ROOT/incoming"

# Tester et recharger Nginx
sudo nginx -t && sudo systemctl reload nginx

echo "   [OK] Nginx configuré et rechargé"

# =====================================================
# ÉTAPE 5 : SCRIPTS DE GESTION
# =====================================================

echo ""
echo "6⃣  CRÉATION SCRIPTS DE GESTION"
echo "=============================="

# Script : Ajouter un paquet
sudo tee /usr/local/bin/apt-repo-add << 'SCRIPT'
#!/bin/bash
# Ajouter un paquet au dépôt

REPO_ROOT="/var/www/apt"
DEB_FILE="$1"
DISTRIBUTION="${2:-jammy}"
COMPONENT="${3:-main}"

if [ -z "$DEB_FILE" ] || [ ! -f "$DEB_FILE" ]; then
    echo "Usage: $0 <fichier.deb> [distribution] [component]"
    echo ""
    echo "Distributions: focal, jammy, noble"
    echo "Components: main, internal, testing"
    exit 1
fi

echo "[PACKAGE] Ajout paquet au dépôt"
echo "======================="
echo "Fichier      : $DEB_FILE"
echo "Distribution : $DISTRIBUTION"
echo "Component    : $COMPONENT"
echo ""

# Vérifier le paquet
if ! dpkg-deb -I "$DEB_FILE" > /dev/null 2>&1; then
    echo "[X] Fichier .deb invalide"
    exit 1
fi

# Informations du paquet
PACKAGE_NAME=$(dpkg-deb -f "$DEB_FILE" Package)
PACKAGE_VERSION=$(dpkg-deb -f "$DEB_FILE" Version)
PACKAGE_ARCH=$(dpkg-deb -f "$DEB_FILE" Architecture)

echo "Package : $PACKAGE_NAME"
echo "Version : $PACKAGE_VERSION"
echo "Arch    : $PACKAGE_ARCH"
echo ""

# Ajouter au dépôt avec reprepro
cd "$REPO_ROOT"
if reprepro -C "$COMPONENT" includedeb "$DISTRIBUTION" "$DEB_FILE"; then
    echo "[OK] Paquet ajouté avec succès"
    echo ""
    echo "[LISTE] Pour installer sur un client :"
    echo "   sudo apt update"
    echo "   sudo apt install $PACKAGE_NAME"
else
    echo "[X] Erreur lors de l'ajout"
    exit 1
fi
SCRIPT

sudo chmod +x /usr/local/bin/apt-repo-add

# Script : Supprimer un paquet
sudo tee /usr/local/bin/apt-repo-remove << 'SCRIPT'
#!/bin/bash
# Supprimer un paquet du dépôt

REPO_ROOT="/var/www/apt"
PACKAGE_NAME="$1"
DISTRIBUTION="${2:-jammy}"

if [ -z "$PACKAGE_NAME" ]; then
    echo "Usage: $0 <package-name> [distribution]"
    exit 1
fi

cd "$REPO_ROOT"
if reprepro remove "$DISTRIBUTION" "$PACKAGE_NAME"; then
    echo "[OK] Paquet $PACKAGE_NAME supprimé de $DISTRIBUTION"
else
    echo "[X] Erreur lors de la suppression"
    exit 1
fi
SCRIPT

sudo chmod +x /usr/local/bin/apt-repo-remove

# Script : Lister les paquets
sudo tee /usr/local/bin/apt-repo-list << 'SCRIPT'
#!/bin/bash
# Lister les paquets du dépôt

REPO_ROOT="/var/www/apt"
DISTRIBUTION="${1:-jammy}"

cd "$REPO_ROOT"

echo "[LISTE] Paquets dans le dépôt : $DISTRIBUTION"
echo "========================================"
echo ""

reprepro list "$DISTRIBUTION" | column -t

echo ""
echo "Total : $(reprepro list "$DISTRIBUTION" | wc -l) paquets"
SCRIPT

sudo chmod +x /usr/local/bin/apt-repo-list

# Script : Processus incoming (automatique)
sudo tee /usr/local/bin/apt-repo-process-incoming << 'SCRIPT'
#!/bin/bash
# Traiter les paquets uploadés dans incoming/

REPO_ROOT="/var/www/apt"
INCOMING_DIR="$REPO_ROOT/incoming"

cd "$REPO_ROOT"

if [ ! -d "$INCOMING_DIR" ]; then
    echo "[X] Dossier incoming non trouvé"
    exit 1
fi

# Compter les fichiers
COUNT=$(find "$INCOMING_DIR" -name "*.deb" | wc -l)

if [ $COUNT -eq 0 ]; then
    echo "ℹ  Aucun paquet en attente"
    exit 0
fi

echo "[PACKAGE] Traitement de $COUNT paquet(s)..."

# Traiter avec reprepro
if reprepro processincoming incoming; then
    echo "[OK] Paquets traités avec succès"
else
    echo "[X] Erreur lors du traitement"
    exit 1
fi
SCRIPT

sudo chmod +x /usr/local/bin/apt-repo-process-incoming

# Script : Export complet du dépôt
sudo tee /usr/local/bin/apt-repo-export << 'SCRIPT'
#!/bin/bash
# Exporter le dépôt complet

REPO_ROOT="/var/www/apt"
EXPORT_DIR="${1:-/tmp/apt-repo-export}"

echo "[SORTIE] Export du dépôt vers : $EXPORT_DIR"

mkdir -p "$EXPORT_DIR"

cd "$REPO_ROOT"

# Export avec reprepro
for dist in focal jammy noble; do
    echo "   Export $dist..."
    reprepro export "$dist"
done

# Copier
rsync -av --delete \
    --exclude 'incoming' \
    --exclude 'logs' \
    "$REPO_ROOT/" "$EXPORT_DIR/"

# Créer archive
ARCHIVE="apt-repo-$(date +%Y%m%d).tar.gz"
tar -czf "/tmp/$ARCHIVE" -C "$EXPORT_DIR" .

echo "[OK] Export terminé : /tmp/$ARCHIVE"
SCRIPT

sudo chmod +x /usr/local/bin/apt-repo-export

echo "   [OK] Scripts de gestion créés :"
echo "      - apt-repo-add <deb> [dist] [component]"
echo "      - apt-repo-remove <package> [dist]"
echo "      - apt-repo-list [dist]"
echo "      - apt-repo-process-incoming"
echo "      - apt-repo-export [dir]"

# =====================================================
# ÉTAPE 6 : CRÉATION PAQUET EXEMPLE
# =====================================================

echo ""
echo "7⃣  CRÉATION PAQUET EXEMPLE"
echo "=========================="

# Créer un paquet exemple
PACKAGE_NAME="company-hello"
PACKAGE_VERSION="1.0.0"
BUILD_DIR="/tmp/${PACKAGE_NAME}_${PACKAGE_VERSION}"

mkdir -p "$BUILD_DIR"/{DEBIAN,usr/bin,usr/share/doc/$PACKAGE_NAME}

# Fichier de contrôle
cat > "$BUILD_DIR/DEBIAN/control" << EOF
Package: $PACKAGE_NAME
Version: $PACKAGE_VERSION
Section: utils
Priority: optional
Architecture: all
Maintainer: Company SysAdmin <sysadmin@company.com>
Description: Company Hello World Application
 This is an example package for the company repository.
 It demonstrates how to create and distribute internal packages.
Depends: bash (>= 4.0)
EOF

# Scripts de maintenance
cat > "$BUILD_DIR/DEBIAN/postinst" << 'EOF'
#!/bin/bash
set -e

echo "[OK] Company Hello installed successfully"
exit 0
EOF

chmod 755 "$BUILD_DIR/DEBIAN/postinst"

# Application
cat > "$BUILD_DIR/usr/bin/company-hello" << 'EOF'
#!/bin/bash
echo "[ENTREPRISE] Hello from Company Repository!"
echo "Version: 1.0.0"
echo "This package was installed from our private APT repository."
EOF

chmod 755 "$BUILD_DIR/usr/bin/company-hello"

# Documentation
cat > "$BUILD_DIR/usr/share/doc/$PACKAGE_NAME/README" << 'EOF'
Company Hello - Example Package

This is a demonstration package to show how to:
1. Create .deb packages
2. Add them to the company repository
3. Install them on client machines

Usage:
  company-hello

EOF

cat > "$BUILD_DIR/usr/share/doc/$PACKAGE_NAME/changelog.Debian" << EOF
$PACKAGE_NAME ($PACKAGE_VERSION) stable; urgency=low

  * Initial release
  * Example package for company repository

 -- Company SysAdmin <sysadmin@company.com>  $(date -R)
EOF

gzip -9 "$BUILD_DIR/usr/share/doc/$PACKAGE_NAME/changelog.Debian"

cat > "$BUILD_DIR/usr/share/doc/$PACKAGE_NAME/copyright" << 'EOF'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: company-hello
Source: https://company.internal

Files: *
Copyright: 2024 Company Inc.
License: Proprietary
 This software is proprietary to Company Inc.
 Unauthorized distribution is prohibited.
EOF

# Construire le paquet
dpkg-deb --build "$BUILD_DIR"

echo "   [OK] Paquet créé : ${BUILD_DIR}.deb"

# Ajouter au dépôt
echo ""
echo "   [PACKAGE] Ajout au dépôt..."
sudo /usr/local/bin/apt-repo-add "${BUILD_DIR}.deb" jammy main

echo "   [OK] Paquet example ajouté au dépôt"

# =====================================================
# ÉTAPE 7 : CONFIGURATION CLIENT
# =====================================================

echo ""
echo "8⃣  GÉNÉRATION CONFIGURATION CLIENT"
echo "==================================="

# Script de configuration pour les clients
sudo tee "$REPO_ROOT/setup-client.sh" << 'CLIENT'
#!/bin/bash
# Configuration client pour dépôt APT privé

REPO_URL="http://repo.company.internal"
REPO_NAME="company-repo"

echo "[ENTREE] Configuration dépôt APT privé"
echo "==============================="

# 1. Télécharger et ajouter la clé GPG
echo "1⃣  Import clé GPG..."
wget -qO - "$REPO_URL/company-repo.gpg.key" | \
    gpg --dearmor | \
    sudo tee /usr/share/keyrings/company-repo.gpg > /dev/null

# 2. Ajouter le dépôt
echo "2⃣  Ajout dépôt..."
CODENAME=$(lsb_release -cs)

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/company-repo.gpg] $REPO_URL $CODENAME main" | \
    sudo tee /etc/apt/sources.list.d/company-repo.list

# 3. Configurer authentification
echo "3⃣  Configuration authentification..."
read -p "Username APT: " APT_USER
read -sp "Password APT: " APT_PASS
echo ""

sudo tee /etc/apt/auth.conf.d/company-repo.conf << EOF
machine repo.company.internal
login $APT_USER
password $APT_PASS
EOF

sudo chmod 600 /etc/apt/auth.conf.d/company-repo.conf

# 4. Mise à jour
echo "4⃣  Mise à jour index..."
sudo apt update

echo ""
echo "[OK] Configuration terminée !"
echo ""
echo "Pour installer un paquet :"
echo "  sudo apt install company-hello"
CLIENT

chmod +x "$REPO_ROOT/setup-client.sh"

echo "   [OK] Script client créé : $REPO_ROOT/setup-client.sh"

# =====================================================
# ÉTAPE 8 : MONITORING ET MAINTENANCE
# =====================================================

echo ""
echo "9⃣  CONFIGURATION MONITORING"
echo "==========================="

# Script de vérification santé
sudo tee /usr/local/bin/apt-repo-health << 'HEALTH'
#!/bin/bash
# Vérification santé du dépôt

REPO_ROOT="/var/www/apt"

echo "[HOPITAL] HEALTH CHECK - DÉPÔT APT"
echo "==========================="
echo ""

# 1. Nginx
if systemctl is-active --quiet nginx; then
    echo "[OK] Nginx running"
else
    echo "[X] Nginx DOWN"
fi

# 2. Espace disque
DISK_USAGE=$(df -h "$REPO_ROOT" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -lt 80 ]; then
    echo "[OK] Disk: $DISK_USAGE%"
else
    echo "[ATTENTION]  Disk: $DISK_USAGE% (critical)"
fi

# 3. Nombre de paquets
cd "$REPO_ROOT"
for dist in focal jammy noble; do
    COUNT=$(reprepro list "$dist" 2>/dev/null | wc -l)
    echo "[PACKAGE] $dist: $COUNT paquets"
done

# 4. Fichiers en attente
INCOMING_COUNT=$(find "$REPO_ROOT/incoming" -name "*.deb" 2>/dev/null | wc -l)
if [ $INCOMING_COUNT -gt 0 ]; then
    echo "[ENTREE] Incoming: $INCOMING_COUNT paquet(s) en attente"
fi

# 5. Logs récents
echo ""
echo "[GRAPHIQUE] Activité récente (24h):"
if [ -f "$REPO_ROOT/logs/access.log" ]; then
    REQUESTS=$(awk -v d=$(date -d '24 hours ago' +%d/%b/%Y) '$4 >= "["d' "$REPO_ROOT/logs/access.log" 2>/dev/null | wc -l)
    echo "   Requêtes: $REQUESTS"
fi

# 6. Test accès
echo ""
echo "[PLUGIN] Test accès repository:"
if curl -s -o /dev/null -w "%{http_code}" "http://localhost/dists/jammy/Release" | grep -q "200\|401"; then
    echo "[OK] Repository accessible"
else
    echo "[X] Repository inaccessible"
fi
HEALTH

sudo chmod +x /usr/local/bin/apt-repo-health

# Cron pour traitement automatique incoming
(sudo crontab -l 2>/dev/null; echo "*/15 * * * * /usr/local/bin/apt-repo-process-incoming >> /var/log/apt-repo-process.log 2>&1") | sudo crontab -

echo "   [OK] Monitoring configuré"
echo "      - Health check: apt-repo-health"
echo "      - Cron: Traitement incoming toutes les 15 min"

# =====================================================
# ÉTAPE 9 : DOCUMENTATION
# =====================================================

echo ""
echo "[10] CRÉATION DOCUMENTATION"
echo "========================"

sudo tee "$REPO_ROOT/README.html" << 'HTML'
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Company APT Repository</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 1000px; margin: 50px auto; padding: 20px; background: #f5f5f5; }
        .container { background: white; padding: 30px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
        h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
        h2 { color: #34495e; margin-top: 30px; }
        code { background: #ecf0f1; padding: 2px 6px; border-radius: 3px; font-family: monospace; }
        pre { background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 5px; overflow-x: auto; }
        .info { background: #d4edda; border-left: 4px solid #28a745; padding: 15px; margin: 15px 0; }
        .warning { background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 15px 0; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background: #34495e; color: white; }
    </style>
</head>
<body>
<div class="container">
    <h1>[ENTREPRISE] Company Private APT Repository</h1>
    
    <div class="info">
        <strong>Repository URL:</strong> http://repo.company.internal<br>
        <strong>Distributions:</strong> focal (20.04), jammy (22.04), noble (24.04)<br>
        <strong>Components:</strong> main, internal, testing
    </div>

    <h2>[ENTREE] Configuration Client (Ubuntu/Debian)</h2>
    
    <h3>Méthode Automatique</h3>
    <pre>wget http://repo.company.internal/setup-client.sh
chmod +x setup-client.sh
sudo ./setup-client.sh</pre>

    <h3>Méthode Manuelle</h3>
    <pre># 1. Ajouter la clé GPG
wget -qO - http://repo.company.internal/company-repo.gpg.key | \
    gpg --dearmor | \
    sudo tee /usr/share/keyrings/company-repo.gpg

# 2. Ajouter le dépôt
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/company-repo.gpg] http://repo.company.internal $(lsb_release -cs) main" | \
    sudo tee /etc/apt/sources.list.d/company-repo.list

# 3. Configurer authentification
sudo tee /etc/apt/auth.conf.d/company-repo.conf << EOF
machine repo.company.internal
login apt-reader
password reader_password_123
EOF

sudo chmod 600 /etc/apt/auth.conf.d/company-repo.conf

# 4. Mettre à jour
sudo apt update</pre>

    <h2>[PACKAGE] Utilisation</h2>
    
    <h3>Rechercher un paquet</h3>
    <pre>apt search company-</pre>
    
    <h3>Installer un paquet</h3>
    <pre>sudo apt install company-hello</pre>
    
    <h3>Voir les paquets disponibles</h3>
    <pre>apt-cache policy | grep company</pre>

    <h2>[OUTIL] Administration</h2>
    
    <table>
        <tr>
            <th>Commande</th>
            <th>Description</th>
        </tr>
        <tr>
            <td><code>apt-repo-add package.deb</code></td>
            <td>Ajouter un paquet au dépôt</td>
        </tr>
        <tr>
            <td><code>apt-repo-remove package-name</code></td>
            <td>Supprimer un paquet</td>
        </tr>
        <tr>
            <td><code>apt-repo-list</code></td>
            <td>Lister tous les paquets</td>
        </tr>
        <tr>
            <td><code>apt-repo-health</code></td>
            <td>Vérifier santé du dépôt</td>
        </tr>
        <tr>
            <td><code>apt-repo-export</code></td>
            <td>Exporter le dépôt complet</td>
        </tr>
    </table>

    <h2>[NOTE] Créer un Paquet</h2>
    
    <pre># 1. Créer structure
mkdir -p mypackage_1.0/{DEBIAN,usr/bin}

# 2. Fichier control
cat > mypackage_1.0/DEBIAN/control << EOF
Package: mypackage
Version: 1.0
Architecture: all
Maintainer: Your Name <you@company.com>
Description: My custom package
EOF

# 3. Application
cat > mypackage_1.0/usr/bin/myapp << EOF
#!/bin/bash
echo "Hello from myapp"
EOF
chmod 755 mypackage_1.0/usr/bin/myapp

# 4. Construire
dpkg-deb --build mypackage_1.0

# 5. Ajouter au dépôt
apt-repo-add mypackage_1.0.deb jammy main</pre>

    <div class="warning">
        <strong>[ATTENTION] Sécurité</strong><br>
        - Ne partagez jamais les credentials d'upload<br>
        - Scannez les paquets avant ajout<br>
        - Utilisez HTTPS en production<br>
        - Auditez régulièrement les accès
    </div>

    <h2>[GRAPHIQUE] Statistiques</h2>
    
    <p>Pour voir les statistiques d'utilisation :</p>
    <pre>tail -f /var/www/apt/logs/access.log</pre>

    <h2>🆘 Support</h2>
    
    <p>Pour toute question ou problème :</p>
    <ul>
        <li>Email: sysadmin@company.com</li>
        <li>Wiki: https://wiki.company.internal/apt-repo</li>
    </ul>
</div>
</body>
</html>
HTML

echo "   [OK] Documentation créée : $REPO_ROOT/README.html"

# =====================================================
# RÉCAPITULATIF FINAL
# =====================================================

echo ""
echo "[OK] DÉPÔT APT PRIVÉ CONFIGURÉ"
echo "============================"
echo ""
echo "[WEB] URLs :"
echo "   Documentation : http://$REPO_DOMAIN/README.html"
echo "   Clé GPG       : http://$REPO_DOMAIN/company-repo.gpg.key"
echo "   Setup client  : http://$REPO_DOMAIN/setup-client.sh"
echo ""
echo "[SECURISE] Authentification :"
echo "   Lecture : apt-reader / reader_password_123"
echo "   Upload  : apt-uploader / uploader_password_456"
echo ""
echo "[PACKAGE] Paquet exemple :"
echo "   company-hello (version 1.0.0)"
echo ""
echo "[OUTILS]  Commandes admin :"
echo "   apt-repo-add <deb> [dist] [component]"
echo "   apt-repo-remove <package> [dist]"
echo "   apt-repo-list [dist]"
echo "   apt-repo-process-incoming"
echo "   apt-repo-health"
echo "   apt-repo-export [dir]"
echo ""
echo "[LISTE] Distributions supportées :"
echo "   - focal (Ubuntu 20.04 LTS)"
echo "   - jammy (Ubuntu 22.04 LTS)"
echo "   - noble (Ubuntu 24.04 LTS)"
echo ""
echo "[GRAPHIQUE] Components :"
echo "   - main     : Paquets stables"
echo "   - internal : Paquets internes"
echo "   - testing  : Paquets en test"
echo ""
echo "[ATTENTION]  TODO Production :"
echo "   1. Configurer HTTPS (Let's Encrypt)"
echo "   2. Backup automatique (rsync + cron)"
echo "   3. Monitoring (Prometheus/Grafana)"
echo "   4. High Availability (plusieurs serveurs)"
echo "   5. CDN/Mirror pour distribution géographique"
echo ""
echo "[TEST] Test rapide :"
echo "   curl http://localhost/README.html"
echo "   apt-repo-health"
echo "   apt-repo-list jammy"

```

---

## PARTIE 2 : MIRRORING DÉPÔTS PUBLICS

```bash
#!/bin/bash
# setup-apt-mirror.sh

echo "[MIRROR] CONFIGURATION APT MIRROR (dépôts Ubuntu)"
echo "==========================================="

# Installation
sudo apt install -y apt-mirror apache2

# Configuration
sudo tee /etc/apt/mirror.list << 'MIRROR'
############# config ##################
set base_path    /var/spool/apt-mirror
set mirror_path  $base_path/mirror
set skel_path    $base_path/skel
set var_path     $base_path/var
set cleanscript $var_path/clean.sh
set defaultarch  amd64
set postmirror_script $var_path/postmirror.sh
set run_postmirror 0
set nthreads     20
set limit_rate   100m
set _tilde 0

############# end config ##############

# Ubuntu Jammy (22.04)
deb http://archive.ubuntu.com/ubuntu jammy main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse

# Ubuntu Noble (24.04)
deb http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu noble-security main restricted universe multiverse

clean http://archive.ubuntu.com/ubuntu
MIRROR

# Lancer le mirroring (ATTENTION : peut prendre plusieurs heures et >100GB)
echo "[ATTENTION]  Le mirroring complet peut prendre plusieurs heures et >100GB"
read -p "Lancer le mirroring maintenant ? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    sudo apt-mirror
fi

# Configuration Apache pour servir le mirror
sudo tee /etc/apache2/sites-available/apt-mirror.conf << 'APACHE'
<VirtualHost *:80>
    ServerName mirror.company.internal
    DocumentRoot /var/spool/apt-mirror/mirror
    
    <Directory /var/spool/apt-mirror/mirror>
        Options +Indexes +FollowSymLinks
        Require all granted
    </Directory>
    
    ErrorLog ${APACHE_LOG_DIR}/apt-mirror-error.log
    CustomLog ${APACHE_LOG_DIR}/apt-mirror-access.log combined
</VirtualHost>
APACHE

sudo a2ensite apt-mirror
sudo systemctl reload apache2

# Cron pour mise à jour quotidienne
echo "0 2 * * * /usr/bin/apt-mirror >> /var/log/apt-mirror.log 2>&1" | sudo crontab -

echo "[OK] Mirror configuré"
echo "   URL : http://mirror.company.internal"
echo "   Mise à jour : Quotidienne à 2h"
```

---

## PARTIE 3 : DÉPÔT YUM/DNF (RHEL/CentOS)

```bash
#!/bin/bash
# setup-private-yum-repo.sh

echo "[PACKAGE] CRÉATION DÉPÔT YUM/DNF PRIVÉ"
echo "==============================="

REPO_ROOT="/var/www/yum"
REPO_NAME="company-repo"

# Installation
sudo dnf install -y nginx createrepo mod_ssl

# Structure
sudo mkdir -p "$REPO_ROOT"/{el8,el9}/{x86_64,SRPMS}

# Créer metadata
for el in el8 el9; do
    sudo createrepo "$REPO_ROOT/$el/x86_64"
done

# Configuration Nginx
sudo tee /etc/nginx/conf.d/yum-repo.conf << EOF
server {
    listen 80;
    server_name yum.company.internal;
    
    root $REPO_ROOT;
    autoindex on;
    
    location / {
        auth_basic "Company YUM Repository";
        auth_basic_user_file /etc/nginx/.htpasswd-yum;
    }
}
EOF

sudo htpasswd -cb /etc/nginx/.htpasswd-yum yum-user yum_password_123
sudo systemctl reload nginx

# Script ajout RPM
sudo tee /usr/local/bin/yum-repo-add << 'SCRIPT'
#!/bin/bash
RPM_FILE="$1"
EL_VERSION="${2:-el9}"

if [ -z "$RPM_FILE" ] || [ ! -f "$RPM_FILE" ]; then
    echo "Usage: $0 <fichier.rpm> [el8|el9]"
    exit 1
fi

REPO_ROOT="/var/www/yum"
sudo cp "$RPM_FILE" "$REPO_ROOT/$EL_VERSION/x86_64/"
sudo createrepo --update "$REPO_ROOT/$EL_VERSION/x86_64/"

echo "[OK] RPM ajouté au dépôt $EL_VERSION"
SCRIPT

sudo chmod +x /usr/local/bin/yum-repo-add

# Configuration client
cat > /tmp/company-repo.repo << 'REPO'
[company-repo]
name=Company Repository
baseurl=http://yum.company.internal/el$releasever/$basearch
enabled=1
gpgcheck=0
username=yum-user
password=yum_password_123
REPO

echo "[OK] Dépôt YUM configuré"
echo "   Configuration client : /tmp/company-repo.repo"
echo "   Copier vers : /etc/yum.repos.d/ sur les clients"
```

---

## PARTIE 4 : CI/CD INTEGRATION

```bash
#!/bin/bash
# ci-cd-package-publish.sh
# Script pour intégration CI/CD (GitLab CI, Jenkins, etc.)

set -e

# Variables d'environnement (définies par CI)
: ${PACKAGE_FILE:?} # Fichier .deb ou .rpm
: ${DISTRIBUTION:=jammy}
: ${COMPONENT:=main}
: ${REPO_SERVER:=repo.company.internal}
: ${REPO_USER:=apt-uploader}
: ${REPO_PASSWORD:?}

echo "[RAPIDE] CI/CD - Publication paquet"
echo "============================="
echo "Package      : $PACKAGE_FILE"
echo "Distribution : $DISTRIBUTION"
echo "Component    : $COMPONENT"
echo "Repository   : $REPO_SERVER"
echo ""

# Vérifier le paquet
if [[ "$PACKAGE_FILE" == *.deb ]]; then
    echo "[PACKAGE] Vérification .deb..."
    dpkg-deb -I "$PACKAGE_FILE"
    
    # Upload vers incoming
    echo "[SORTIE] Upload vers dépôt..."
    curl -u "$REPO_USER:$REPO_PASSWORD" \
        -T "$PACKAGE_FILE" \
        "http://$REPO_SERVER/incoming/$(basename $PACKAGE_FILE)"
    
elif [[ "$PACKAGE_FILE" == *.rpm ]]; then
    echo "[PACKAGE] Vérification .rpm..."
    rpm -qip "$PACKAGE_FILE"
    
    # Upload
    echo "[SORTIE] Upload vers dépôt..."
    curl -u "$REPO_USER:$REPO_PASSWORD" \
        -T "$PACKAGE_FILE" \
        "http://$REPO_SERVER/el9/x86_64/$(basename $PACKAGE_FILE)"
else
    echo "[X] Format non supporté"
    exit 1
fi

echo "[OK] Package publié avec succès"

# Notification Slack (optionnel)
if [ -n "$SLACK_WEBHOOK" ]; then
    curl -X POST "$SLACK_WEBHOOK" \
        -H 'Content-Type: application/json' \
        -d "{\"text\":\"[PACKAGE] New package published: $(basename $PACKAGE_FILE)\"}"
fi
```

**Exemple GitLab CI (.gitlab-ci.yml) :**

```yaml
stages:
  - build
  - package
  - publish

build:
  stage: build
  script:
    - make build

package:
  stage: package
  script:
    - make package
  artifacts:
    paths:
      - "*.deb"

publish:
  stage: publish
  only:
    - main
  script:
    - chmod +x ci-cd-package-publish.sh
    - export PACKAGE_FILE=$(ls *.deb)
    - ./ci-cd-package-publish.sh
  variables:
    DISTRIBUTION: "jammy"
    COMPONENT: "main"
```

---

## PARTIE 5 : MONITORING & ALERTES

```bash
#!/bin/bash
# monitoring-repo.sh

cat > /usr/local/bin/repo-monitoring << 'MONITORING'
#!/bin/bash
# Monitoring avancé du dépôt

REPO_ROOT="/var/www/apt"
PROMETHEUS_DIR="/var/lib/prometheus/textfile_collector"

mkdir -p "$PROMETHEUS_DIR"

# Métriques Prometheus
cat > "$PROMETHEUS_DIR/apt_repo.prom" << PROM
# HELP apt_repo_packages_total Total packages in repository
# TYPE apt_repo_packages_total gauge
apt_repo_packages_total{distribution="jammy"} $(reprepro -b $REPO_ROOT list jammy 2>/dev/null | wc -l)
apt_repo_packages_total{distribution="focal"} $(reprepro -b $REPO_ROOT list focal 2>/dev/null | wc -l)

# HELP apt_repo_disk_usage_bytes Disk usage in bytes
# TYPE apt_repo_disk_usage_bytes gauge
apt_repo_disk_usage_bytes $(du -sb $REPO_ROOT | awk '{print $1}')

# HELP apt_repo_requests_24h HTTP requests in last 24 hours
# TYPE apt_repo_requests_24h counter
apt_repo_requests_24h $(awk -v d=$(date -d '24 hours ago' +%d/%b/%Y) '$4 >= "["d' $REPO_ROOT/logs/access.log 2>/dev/null | wc -l)
PROM

# Alertes critiques
DISK_USAGE=$(df -h $REPO_ROOT | awk 'NR==2 {print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 90 ]; then
    echo "CRITICAL: Repository disk usage at ${DISK_USAGE}%" | \
        mail -s "APT Repository Alert" sysadmin@company.com
fi

# Vérifier disponibilité
if ! curl -s -o /dev/null -w "%{http_code}" "http://localhost/dists/jammy/Release" | grep -q "200\|401"; then
    echo "CRITICAL: Repository not accessible" | \
        mail -s "APT Repository Down" sysadmin@company.com
fi
MONITORING

chmod +x /usr/local/bin/repo-monitoring

# Cron toutes les 5 minutes
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/repo-monitoring") | crontab -
```

---

### QUAND utiliser un dépôt privé ?

```
[OK] Entreprise avec >10 serveurs
[OK] Applications propriétaires
[OK] Besoin de versions spécifiques
[OK] Environnements déconnectés (air-gapped)
[OK] Conformité/audit requis
[OK] Économies bande passante importantes
[OK] CI/CD avec artifacts binaires

[X] Petit projet personnel
[X] Peu de serveurs (<5)
[X] Seulement paquets publics standard
[X] Ressources limitées
```

---

### Alternatives cloud

```
SOLUTIONS HÉBERGÉES :

1. Artifactory (JFrog) - Enterprise
   [OK] APT, YUM, PyPI, NPM, Docker, Maven...
   [OK] HA, CDN, scanning
   [X] Coût élevé

2. Nexus Repository (Sonatype)
   [OK] Multi-format
   [OK] Open source (OSS) ou Pro
   [X] Complexe

3. AWS CodeArtifact
   [OK] Managé AWS
   [OK] PyPI, NPM, Maven
   [X] Pas APT/YUM natif

4. Cloudsmith
   [OK] SaaS
   [OK] Multi-format
   [X] Coût par bande passante

5. packagecloud.io
   [OK] Simple
   [OK] APT, YUM, RubyGems
   [X] Limite packages (plan gratuit)
```

---

## [COURS] CONCLUSION CAS 5

**[OK] Tu sais maintenant :**
- Créer dépôt APT privé complet
- Signer avec GPG
- Sécuriser (authentification, HTTPS)
- Créer des paquets .deb
- Gérer avec reprepro
- Configurer clients
- Intégrer CI/CD
- Mirroring dépôts publics
- Dépôt YUM/DNF (bonus)
- Monitoring et alertes

**Infrastructure complète :**
- Serveur de dépôt
- Authentification
- Scripts automatisation
- Documentation
- Monitoring
- Backup

**Temps moyen :** 2-3h

---

Veux-tu maintenant :
1. **CAS 4 : Déploiement automatisé avec Ansible** ?
2. Passer directement aux **bonnes pratiques et troubleshooting final** ?
3. Voir un **récapitulatif complet de toute la section** ?

# PARTIE 4 : [ENTREPRISE] 5 CAS RÉELS ENTREPRISE

## CAS 1 : [RAPIDE] APPLICATION WEB MULTI-WORKERS HAUTE DISPONIBILITÉ

### POURQUOI ce cas ?

```
CONTEXTE : API REST critique en production
BESOIN :
- Application Node.js/Python avec plusieurs workers
- Haute disponibilité (restart automatique)
- Load balancing entre workers
- Démarrage échelonné (pas tous en même temps)
- Logs centralisés et structurés
- Métriques de performance
- Graceful shutdown (fermeture propre)
- Rolling restart sans downtime

CONTRAINTES :
- Zéro downtime
- Réponse < 200ms
- Gestion mémoire (éviter leaks)
- Supervision 24/7
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-ha-webapp.sh

echo "[RAPIDE] CONFIGURATION APPLICATION WEB HAUTE DISPONIBILITÉ"
echo "===================================================="

APP_NAME="api-backend"
APP_USER="apiuser"
APP_DIR="/opt/${APP_NAME}"
NUM_WORKERS=4

# =====================================================
# ÉTAPE 1 : PRÉPARATION ENVIRONNEMENT
# =====================================================

echo ""
echo "1⃣  PRÉPARATION ENVIRONNEMENT"
echo "============================"

# Créer utilisateur système
sudo useradd -r -s /usr/sbin/nologin -d "$APP_DIR" "$APP_USER"

# Structure de répertoires
sudo mkdir -p "$APP_DIR"/{bin,config,logs,tmp}
sudo mkdir -p /etc/${APP_NAME}
sudo chown -R "$APP_USER:$APP_USER" "$APP_DIR"

echo "   [OK] Environnement créé"

# =====================================================
# ÉTAPE 2 : APPLICATION EXEMPLE (Node.js)
# =====================================================

echo ""
echo "2⃣  CRÉATION APPLICATION"
echo "======================="

# Installer Node.js si nécessaire
if ! command -v node &> /dev/null; then
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt install -y nodejs
fi

# Application Node.js avec clustering
sudo tee "$APP_DIR/bin/app.js" << 'NODEJS'
const express = require('express');
const cluster = require('cluster');
const os = require('os');

const PORT = process.env.PORT || 3000;
const WORKER_ID = process.env.WORKER_ID || cluster.worker?.id || 'master';

const app = express();

// Middleware logging
app.use((req, res, next) => {
    const start = Date.now();
    res.on('finish', () => {
        const duration = Date.now() - start;
        console.log(JSON.stringify({
            timestamp: new Date().toISOString(),
            worker: WORKER_ID,
            method: req.method,
            path: req.path,
            status: res.statusCode,
            duration: duration,
            ip: req.ip
        }));
    });
    next();
});

// Routes
app.get('/', (req, res) => {
    res.json({
        message: 'API Backend',
        worker: WORKER_ID,
        pid: process.pid,
        uptime: process.uptime(),
        memory: process.memoryUsage()
    });
});

app.get('/health', (req, res) => {
    const health = {
        status: 'healthy',
        worker: WORKER_ID,
        pid: process.pid,
        uptime: process.uptime(),
        memory: process.memoryUsage(),
        timestamp: new Date().toISOString()
    };
    res.json(health);
});

app.get('/metrics', (req, res) => {
    res.json({
        worker: WORKER_ID,
        pid: process.pid,
        uptime: process.uptime(),
        memory: process.memoryUsage(),
        cpu: process.cpuUsage()
    });
});

// Graceful shutdown
process.on('SIGTERM', () => {
    console.log(JSON.stringify({
        timestamp: new Date().toISOString(),
        worker: WORKER_ID,
        event: 'SIGTERM received',
        message: 'Graceful shutdown...'
    }));
    
    server.close(() => {
        console.log(JSON.stringify({
            timestamp: new Date().toISOString(),
            worker: WORKER_ID,
            event: 'shutdown_complete'
        }));
        process.exit(0);
    });
    
    // Force shutdown après 10s
    setTimeout(() => {
        console.error('Force shutdown');
        process.exit(1);
    }, 10000);
});

const server = app.listen(PORT, () => {
    console.log(JSON.stringify({
        timestamp: new Date().toISOString(),
        worker: WORKER_ID,
        event: 'started',
        port: PORT,
        pid: process.pid
    }));
});
NODEJS

# Package.json
sudo tee "$APP_DIR/package.json" << 'PKG'
{
  "name": "api-backend",
  "version": "1.0.0",
  "description": "High Availability API Backend",
  "main": "bin/app.js",
  "scripts": {
    "start": "node bin/app.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}
PKG

# Installer dépendances
sudo -u "$APP_USER" bash << 'INSTALL'
cd /opt/api-backend
npm install --production
INSTALL

echo "   [OK] Application créée"

# =====================================================
# ÉTAPE 3 : CONFIGURATION
# =====================================================

echo ""
echo "3⃣  CONFIGURATION"
echo "================"

# Fichier de configuration
sudo tee /etc/${APP_NAME}/config.env << 'CONFIG'
# Environment
NODE_ENV=production

# Application
PORT=3000
LOG_LEVEL=info

# Performance
UV_THREADPOOL_SIZE=128

# Timeouts
TIMEOUT_SHUTDOWN=10000
CONFIG

sudo chmod 640 /etc/${APP_NAME}/config.env
sudo chown "$APP_USER:$APP_USER" /etc/${APP_NAME}/config.env

echo "   [OK] Configuration créée"

# =====================================================
# ÉTAPE 4 : SERVICES SYSTEMD (UN PAR WORKER)
# =====================================================

echo ""
echo "4⃣  CRÉATION SERVICES WORKERS"
echo "============================="

# Template de service worker
for i in $(seq 1 $NUM_WORKERS); do
    WORKER_PORT=$((3000 + i - 1))
    
    sudo tee /etc/systemd/system/${APP_NAME}-worker${i}.service << SERVICE
[Unit]
Description=API Backend Worker ${i}
Documentation=https://company.internal/docs/api-backend
PartOf=${APP_NAME}.target
After=network.target

[Service]
Type=simple
User=${APP_USER}
Group=${APP_USER}
WorkingDirectory=${APP_DIR}

# Variables d'environnement
EnvironmentFile=/etc/${APP_NAME}/config.env
Environment="PORT=${WORKER_PORT}"
Environment="WORKER_ID=${i}"

# Démarrage
ExecStart=/usr/bin/node ${APP_DIR}/bin/app.js

# Restart policy
Restart=always
RestartSec=5s

# Timeouts
TimeoutStartSec=30s
TimeoutStopSec=30s

# Signals
KillMode=mixed
KillSignal=SIGTERM

# Logs
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${APP_NAME}-worker${i}

# Sécurité
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=${APP_DIR}/logs ${APP_DIR}/tmp

# Limites ressources
LimitNOFILE=65536
MemoryMax=512M
CPUQuota=50%

[Install]
WantedBy=${APP_NAME}.target
SERVICE

    echo "   [OK] Worker ${i} créé (port ${WORKER_PORT})"
done

# =====================================================
# ÉTAPE 5 : TARGET POUR GESTION GROUPÉE
# =====================================================

echo ""
echo "5⃣  CRÉATION TARGET"
echo "=================="

# Target pour gérer tous les workers ensemble
sudo tee /etc/systemd/system/${APP_NAME}.target << 'TARGET'
[Unit]
Description=API Backend (All Workers)
Documentation=https://company.internal/docs/api-backend
After=network.target

[Install]
WantedBy=multi-user.target
TARGET

echo "   [OK] Target créée"

# =====================================================
# ÉTAPE 6 : LOAD BALANCER (HAProxy)
# =====================================================

echo ""
echo "6⃣  CONFIGURATION LOAD BALANCER"
echo "==============================="

# Installer HAProxy
sudo apt install -y haproxy

# Configuration HAProxy
sudo tee /etc/haproxy/haproxy.cfg << 'HAPROXY'
global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000
    timeout client  50000
    timeout server  50000

# Stats interface
listen stats
    bind *:8080
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if TRUE

# API Backend
frontend api_frontend
    bind *:80
    default_backend api_backend

backend api_backend
    balance roundrobin
    option httpchk GET /health
    
    server worker1 127.0.0.1:3000 check inter 2000 rise 2 fall 3
    server worker2 127.0.0.1:3001 check inter 2000 rise 2 fall 3
    server worker3 127.0.0.1:3002 check inter 2000 rise 2 fall 3
    server worker4 127.0.0.1:3003 check inter 2000 rise 2 fall 3
HAPROXY

sudo systemctl restart haproxy

echo "   [OK] HAProxy configuré (port 80)"
echo "   [GRAPHIQUE] Stats : http://localhost:8080/stats"

# =====================================================
# ÉTAPE 7 : SCRIPTS DE GESTION
# =====================================================

echo ""
echo "7⃣  SCRIPTS DE GESTION"
echo "====================="

# Script : Démarrage échelonné
sudo tee /usr/local/bin/${APP_NAME}-start-staggered << 'START'
#!/bin/bash
# Démarrage échelonné des workers (éviter pic de charge)

APP_NAME="api-backend"
NUM_WORKERS=4
STAGGER_DELAY=2  # secondes entre chaque worker

echo "[RAPIDE] Démarrage échelonné des workers..."

for i in $(seq 1 $NUM_WORKERS); do
    echo "   Démarrage worker ${i}..."
    sudo systemctl start ${APP_NAME}-worker${i}
    
    if [ $i -lt $NUM_WORKERS ]; then
        echo "   Attente ${STAGGER_DELAY}s..."
        sleep $STAGGER_DELAY
    fi
done

echo "[OK] Tous les workers démarrés"
START

sudo chmod +x /usr/local/bin/${APP_NAME}-start-staggered

# Script : Rolling restart (zéro downtime)
sudo tee /usr/local/bin/${APP_NAME}-rolling-restart << 'ROLLING'
#!/bin/bash
# Rolling restart - un worker à la fois

APP_NAME="api-backend"
NUM_WORKERS=4
WAIT_HEALTHY=5  # secondes d'attente après restart

echo "[SYNC] Rolling restart (zéro downtime)..."

for i in $(seq 1 $NUM_WORKERS); do
    WORKER="${APP_NAME}-worker${i}"
    
    echo "   Restart ${WORKER}..."
    sudo systemctl restart "$WORKER"
    
    # Attendre que le worker soit healthy
    echo "   Attente stabilisation ($WAIT_HEALTHY s)..."
    sleep $WAIT_HEALTHY
    
    # Vérifier health
    if systemctl is-active --quiet "$WORKER"; then
        echo "   [OK] ${WORKER} healthy"
    else
        echo "   [X] ${WORKER} failed!"
        exit 1
    fi
done

echo "[OK] Rolling restart terminé"
ROLLING

sudo chmod +x /usr/local/bin/${APP_NAME}-rolling-restart

# Script : Health check global
sudo tee /usr/local/bin/${APP_NAME}-health << 'HEALTH'
#!/bin/bash
# Vérification santé de l'application

APP_NAME="api-backend"
NUM_WORKERS=4

echo "[HOPITAL] HEALTH CHECK - ${APP_NAME^^}"
echo "==============================="
echo ""

HEALTHY=0
UNHEALTHY=0

# Vérifier chaque worker
for i in $(seq 1 $NUM_WORKERS); do
    WORKER="${APP_NAME}-worker${i}"
    PORT=$((3000 + i - 1))
    
    if systemctl is-active --quiet "$WORKER"; then
        # Vérifier endpoint /health
        if curl -sf "http://localhost:${PORT}/health" > /dev/null 2>&1; then
            echo "[OK] ${WORKER} (port ${PORT}) - HEALTHY"
            ((HEALTHY++))
        else
            echo "[ATTENTION]  ${WORKER} (port ${PORT}) - SERVICE UP, HEALTH CHECK FAILED"
            ((UNHEALTHY++))
        fi
    else
        echo "[X] ${WORKER} (port ${PORT}) - DOWN"
        ((UNHEALTHY++))
    fi
done

echo ""
echo "[GRAPHIQUE] Résumé :"
echo "   Healthy   : ${HEALTHY}/${NUM_WORKERS}"
echo "   Unhealthy : ${UNHEALTHY}/${NUM_WORKERS}"

# HAProxy
echo ""
if systemctl is-active --quiet haproxy; then
    echo "[OK] HAProxy - ACTIVE"
    echo "   Stats : http://localhost:8080/stats"
else
    echo "[X] HAProxy - DOWN"
fi

# Code de sortie
if [ $UNHEALTHY -gt 0 ]; then
    exit 1
else
    exit 0
fi
HEALTH

sudo chmod +x /usr/local/bin/${APP_NAME}-health

# Script : Logs agrégés
sudo tee /usr/local/bin/${APP_NAME}-logs << 'LOGS'
#!/bin/bash
# Voir logs de tous les workers

APP_NAME="api-backend"

# Suivre logs de tous les workers
journalctl -u "${APP_NAME}-worker*" -f --output=json | \
    jq -r '[.MESSAGE] | @tsv'
LOGS

sudo chmod +x /usr/local/bin/${APP_NAME}-logs

echo "   [OK] Scripts créés :"
echo "      - ${APP_NAME}-start-staggered"
echo "      - ${APP_NAME}-rolling-restart"
echo "      - ${APP_NAME}-health"
echo "      - ${APP_NAME}-logs"

# =====================================================
# ÉTAPE 8 : MONITORING AVEC PROMETHEUS (optionnel)
# =====================================================

echo ""
echo "8⃣  MONITORING"
echo "============="

# Exporter Prometheus pour systemd
sudo tee /etc/systemd/system/${APP_NAME}-exporter.service << 'EXPORTER'
[Unit]
Description=Prometheus Metrics Exporter for API Backend
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/api-backend-metrics-exporter.sh
Restart=always
StandardOutput=journal

[Install]
WantedBy=multi-user.target
EXPORTER

# Script exporter simple
sudo tee /usr/local/bin/${APP_NAME}-metrics-exporter.sh << 'METRICS'
#!/bin/bash
# Exporter simple de métriques

while true; do
    # Collecter métriques de chaque worker
    for port in 3000 3001 3002 3003; do
        curl -s "http://localhost:${port}/metrics" | \
            jq -r '. | "worker_\(.worker)_uptime \(.uptime)"'
    done
    sleep 10
done
METRICS

sudo chmod +x /usr/local/bin/${APP_NAME}-metrics-exporter.sh

echo "   [OK] Monitoring configuré"

# =====================================================
# ÉTAPE 9 : ACTIVATION
# =====================================================

echo ""
echo "9⃣  ACTIVATION"
echo "============="

# Recharger systemd
sudo systemctl daemon-reload

# Enable target
sudo systemctl enable ${APP_NAME}.target

# Enable tous les workers
for i in $(seq 1 $NUM_WORKERS); do
    sudo systemctl enable ${APP_NAME}-worker${i}
done

# Démarrage échelonné
/usr/local/bin/${APP_NAME}-start-staggered

# Attendre stabilisation
sleep 5

echo ""
echo "[10] VÉRIFICATION"
echo "==============="

# Health check
/usr/local/bin/${APP_NAME}-health

# =====================================================
# RÉCAPITULATIF
# =====================================================

echo ""
echo "[OK] APPLICATION HAUTE DISPONIBILITÉ CONFIGURÉE"
echo "============================================="
echo ""
echo "[GRAPHIQUE] Architecture :"
echo "   HAProxy (port 80) -> Workers (ports 3000-3003)"
echo ""
echo "[OUTIL] Services :"
for i in $(seq 1 $NUM_WORKERS); do
    echo "   - ${APP_NAME}-worker${i} (port $((3000 + i - 1)))"
done
echo "   - ${APP_NAME}.target (tous les workers)"
echo "   - haproxy (load balancer)"
echo ""
echo "[OUTILS]  Commandes :"
echo "   # Gestion groupée (tous les workers)"
echo "   systemctl start ${APP_NAME}.target"
echo "   systemctl stop ${APP_NAME}.target"
echo "   systemctl status ${APP_NAME}.target"
echo ""
echo "   # Gestion individuelle"
echo "   systemctl restart ${APP_NAME}-worker1"
echo ""
echo "   # Scripts helper"
echo "   ${APP_NAME}-start-staggered     # Démarrage échelonné"
echo "   ${APP_NAME}-rolling-restart     # Restart sans downtime"
echo "   ${APP_NAME}-health              # Health check"
echo "   ${APP_NAME}-logs                # Logs agrégés"
echo ""
echo "[WEB] URLs :"
echo "   Application : http://localhost/"
echo "   Health      : http://localhost/health"
echo "   HAProxy Stats : http://localhost:8080/stats"
echo ""
echo "[NOTE] Tests :"
echo "   curl http://localhost/"
echo "   curl http://localhost/health"
echo "   ab -n 10000 -c 100 http://localhost/  # Load test"
echo ""
echo "[GRAPHIQUE] Monitoring :"
echo "   journalctl -u ${APP_NAME}-worker1 -f"
echo "   ${APP_NAME}-logs"

```

---

### QUAND utiliser ce cas ?

```
[OK] API REST/GraphQL en production
[OK] Applications Node.js/Python/Go
[OK] Besoin haute disponibilité
[OK] Charge importante (>1000 req/s)
[OK] Zéro downtime requis
[OK] Scaling horizontal
[X] Application simple mono-instance
[X] Faible trafic (<100 req/s)
```

---

## CAS 2 : [LIEN] STACK MICROSERVICES AVEC DÉPENDANCES

### POURQUOI ce cas ?

```
CONTEXTE : Architecture microservices
BESOIN :
- Frontend (React/Vue)
- Backend API (Node.js)
- Worker Queue (Python)
- Database (PostgreSQL)
- Cache (Redis)
- Message Queue (RabbitMQ)
- Ordre de démarrage strict
- Gestion dépendances complexes
- Health checks inter-services

CONTRAINTES :
- Démarrage dans le bon ordre
- Frontend attend Backend
- Backend attend DB + Redis
- Worker attend RabbitMQ
- Restart en cascade si nécessaire
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-microservices-stack.sh

echo "[LIEN] CONFIGURATION STACK MICROSERVICES"
echo "===================================="

STACK_NAME="myapp"

# =====================================================
# ÉTAPE 1 : INSTALLATION COMPOSANTS
# =====================================================

echo ""
echo "1⃣  INSTALLATION COMPOSANTS"
echo "=========================="

# PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# Redis
sudo apt install -y redis-server

# RabbitMQ
sudo apt install -y rabbitmq-server

echo "   [OK] Composants installés"

# =====================================================
# ÉTAPE 2 : SERVICE DATABASE (PostgreSQL)
# =====================================================

echo ""
echo "2⃣  CONFIGURATION DATABASE"
echo "========================="

# PostgreSQL est déjà en service systemd
# Créer database pour l'app
sudo -u postgres psql << SQL
CREATE DATABASE myapp_db;
CREATE USER myapp_user WITH PASSWORD 'secure_password_123';
GRANT ALL PRIVILEGES ON DATABASE myapp_db TO myapp_user;
SQL

echo "   [OK] Database configurée"

# =====================================================
# ÉTAPE 3 : SERVICE BACKEND API
# =====================================================

echo ""
echo "3⃣  SERVICE BACKEND API"
echo "======================"

# Créer utilisateur
sudo useradd -r -s /usr/sbin/nologin backend-api

# Structure
sudo mkdir -p /opt/backend-api
sudo chown backend-api:backend-api /opt/backend-api

# Application exemple (Express.js)
sudo tee /opt/backend-api/server.js << 'BACKEND'
const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');

const app = express();
const PORT = process.env.PORT || 4000;

// PostgreSQL
const pool = new Pool({
    host: process.env.DB_HOST || 'localhost',
    database: process.env.DB_NAME || 'myapp_db',
    user: process.env.DB_USER || 'myapp_user',
    password: process.env.DB_PASS || 'secure_password_123'
});

// Redis
const redisClient = redis.createClient({
    url: process.env.REDIS_URL || 'redis://localhost:6379'
});

redisClient.connect();

app.get('/health', async (req, res) => {
    try {
        // Check DB
        await pool.query('SELECT 1');
        
        // Check Redis
        await redisClient.ping();
        
        res.json({
            status: 'healthy',
            database: 'connected',
            redis: 'connected',
            timestamp: new Date().toISOString()
        });
    } catch (error) {
        res.status(503).json({
            status: 'unhealthy',
            error: error.message
        });
    }
});

app.get('/api/data', async (req, res) => {
    // Exemple endpoint
    res.json({ message: 'Backend API' });
});

app.listen(PORT, () => {
    console.log(`Backend API listening on port ${PORT}`);
});
BACKEND

# Dependencies
sudo tee /opt/backend-api/package.json << 'PKG'
{
  "name": "backend-api",
  "version": "1.0.0",
  "dependencies": {
    "express": "^4.18.2",
    "pg": "^8.11.0",
    "redis": "^4.6.0"
  }
}
PKG

# Installer
sudo -u backend-api bash << 'INSTALL'
cd /opt/backend-api
npm install --production
INSTALL

# Service systemd avec dépendances
sudo tee /etc/systemd/system/backend-api.service << 'SERVICE'
[Unit]
Description=Backend API Service
Documentation=https://company.internal/docs/backend-api

# Dépendances strictes (REQUIRES = si fail -> fail)
Requires=postgresql.service redis-server.service
After=postgresql.service redis-server.service

# Attendre que PostgreSQL soit vraiment prêt
After=network.target

[Service]
Type=simple
User=backend-api
Group=backend-api
WorkingDirectory=/opt/backend-api

# Variables
Environment="NODE_ENV=production"
Environment="PORT=4000"
Environment="DB_HOST=localhost"
Environment="DB_NAME=myapp_db"
Environment="DB_USER=myapp_user"
Environment="DB_PASS=secure_password_123"
Environment="REDIS_URL=redis://localhost:6379"

# Health check avant démarrage
ExecStartPre=/usr/local/bin/wait-for-postgres.sh
ExecStartPre=/usr/local/bin/wait-for-redis.sh

# Démarrage
ExecStart=/usr/bin/node /opt/backend-api/server.js

# Restart
Restart=always
RestartSec=5s

# Si DB/Redis fail -> restart backend aussi
BindsTo=postgresql.service redis-server.service

StandardOutput=journal
StandardError=journal
SyslogIdentifier=backend-api

[Install]
WantedBy=multi-user.target
SERVICE

echo "   [OK] Backend API configuré"

# =====================================================
# ÉTAPE 4 : SERVICE WORKER
# =====================================================

echo ""
echo "4⃣  SERVICE WORKER"
echo "================="

sudo useradd -r -s /usr/sbin/nologin worker-queue
sudo mkdir -p /opt/worker-queue
sudo chown worker-queue:worker-queue /opt/worker-queue

# Worker Python
sudo tee /opt/worker-queue/worker.py << 'WORKER'
import pika
import time
import json
import sys

def callback(ch, method, properties, body):
    print(f"Processing: {body.decode()}", flush=True)
    time.sleep(1)  # Simulate work
    ch.basic_ack(delivery_tag=method.delivery_tag)

def main():
    # Connect to RabbitMQ
    connection = pika.BlockingConnection(
        pika.ConnectionParameters('localhost')
    )
    channel = connection.channel()
    channel.queue_declare(queue='tasks', durable=True)
    
    print('Worker waiting for messages...', flush=True)
    
    channel.basic_qos(prefetch_count=1)
    channel.basic_consume(queue='tasks', on_message_callback=callback)
    
    try:
        channel.start_consuming()
    except KeyboardInterrupt:
        channel.stop_consuming()
    
    connection.close()

if __name__ == '__main__':
    main()
WORKER

sudo tee /opt/worker-queue/requirements.txt << 'REQ'
pika==1.3.2
REQ

# Installer
sudo -u worker-queue bash << 'INSTALL'
cd /opt/worker-queue
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
INSTALL

# Service avec dépendance RabbitMQ
sudo tee /etc/systemd/system/worker-queue.service << 'SERVICE'
[Unit]
Description=Worker Queue Service
Documentation=https://company.internal/docs/worker

Requires=rabbitmq-server.service
After=rabbitmq-server.service

[Service]
Type=simple
User=worker-queue
Group=worker-queue
WorkingDirectory=/opt/worker-queue

ExecStartPre=/usr/local/bin/wait-for-rabbitmq.sh
ExecStart=/opt/worker-queue/venv/bin/python /opt/worker-queue/worker.py

Restart=always
RestartSec=5s

StandardOutput=journal
StandardError=journal
SyslogIdentifier=worker-queue

[Install]
WantedBy=multi-user.target
SERVICE

echo "   [OK] Worker configuré"

# =====================================================
# ÉTAPE 5 : SERVICE FRONTEND
# =====================================================

echo ""
echo "5⃣  SERVICE FRONTEND"
echo "==================="

sudo useradd -r -s /usr/sbin/nologin frontend-app
sudo mkdir -p /opt/frontend-app
sudo chown frontend-app:frontend-app /opt/frontend-app

# Nginx pour servir le frontend
sudo apt install -y nginx

sudo tee /etc/nginx/sites-available/frontend << 'NGINX'
server {
    listen 80;
    server_name _;
    
    root /opt/frontend-app/dist;
    index index.html;
    
    location / {
        try_files $uri $uri/ /index.html;
    }
    
    # Proxy vers Backend API
    location /api/ {
        proxy_pass http://localhost:4000/api/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
NGINX

sudo ln -sf /etc/nginx/sites-available/frontend /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

# Frontend simple
sudo mkdir -p /opt/frontend-app/dist
sudo tee /opt/frontend-app/dist/index.html << 'HTML'
<!DOCTYPE html>
<html>
<head>
    <title>MyApp Frontend</title>
</head>
<body>
    <h1>MyApp Frontend</h1>
    <button onclick="testBackend()">Test Backend</button>
    <div id="result"></div>
    
    <script>
        async function testBackend() {
            const res = await fetch('/api/data');
            const data = await res.json();
            document.getElementById('result').innerText = JSON.stringify(data);
        }
    </script>
</body>
</html>
HTML

# Override Nginx service pour dépendances
sudo mkdir -p /etc/systemd/system/nginx.service.d
sudo tee /etc/systemd/system/nginx.service.d/dependencies.conf << 'OVERRIDE'
[Unit]
Requires=backend-api.service
After=backend-api.service
OVERRIDE

echo "   [OK] Frontend configuré"

# =====================================================
# ÉTAPE 6 : SCRIPTS WAIT-FOR
# =====================================================

echo ""
echo "6⃣  SCRIPTS HEALTH CHECK"
echo "======================="

# Wait for PostgreSQL
sudo tee /usr/local/bin/wait-for-postgres.sh << 'WAIT_PG'
#!/bin/bash
MAX_TRIES=30
COUNT=0

echo "Waiting for PostgreSQL..."

while [ $COUNT -lt $MAX_TRIES ]; do
    if pg_isready -h localhost -U postgres > /dev/null 2>&1; then
        echo "PostgreSQL is ready"
        exit 0
    fi
    
    COUNT=$((COUNT + 1))
    echo "PostgreSQL not ready yet ($COUNT/$MAX_TRIES)..."
    sleep 2
done

echo "PostgreSQL failed to start"
exit 1
WAIT_PG

sudo chmod +x /usr/local/bin/wait-for-postgres.sh

# Wait for Redis
sudo tee /usr/local/bin/wait-for-redis.sh << 'WAIT_REDIS'
#!/bin/bash
MAX_TRIES=30
COUNT=0

echo "Waiting for Redis..."

while [ $COUNT -lt $MAX_TRIES ]; do
    if redis-cli ping > /dev/null 2>&1; then
        echo "Redis is ready"
        exit 0
    fi
    
    COUNT=$((COUNT + 1))
    echo "Redis not ready yet ($COUNT/$MAX_TRIES)..."
    sleep 2
done

echo "Redis failed to start"
exit 1
WAIT_REDIS

sudo chmod +x /usr/local/bin/wait-for-redis.sh

# Wait for RabbitMQ
sudo tee /usr/local/bin/wait-for-rabbitmq.sh << 'WAIT_RABBIT'
#!/bin/bash
MAX_TRIES=30
COUNT=0

echo "Waiting for RabbitMQ..."

while [ $COUNT -lt $MAX_TRIES ]; do
    if rabbitmqctl status > /dev/null 2>&1; then
        echo "RabbitMQ is ready"
        exit 0
    fi
    
    COUNT=$((COUNT + 1))
    echo "RabbitMQ not ready yet ($COUNT/$MAX_TRIES)..."
    sleep 2
done

echo "RabbitMQ failed to start"
exit 1
WAIT_RABBIT

sudo chmod +x /usr/local/bin/wait-for-rabbitmq.sh

echo "   [OK] Scripts health check créés"

# =====================================================
# ÉTAPE 7 : TARGET POUR LA STACK COMPLÈTE
# =====================================================

echo ""
echo "7⃣  TARGET STACK"
echo "==============="

sudo tee /etc/systemd/system/myapp-stack.target << 'TARGET'
[Unit]
Description=MyApp Complete Stack
Documentation=https://company.internal/docs/myapp

# Tous les services de la stack
Wants=postgresql.service
Wants=redis-server.service
Wants=rabbitmq-server.service
Wants=backend-api.service
Wants=worker-queue.service
Wants=nginx.service

# Ordre
After=postgresql.service
After=redis-server.service
After=rabbitmq-server.service
After=backend-api.service
After=worker-queue.service

[Install]
WantedBy=multi-user.target
TARGET

echo "   [OK] Target créée"

# =====================================================
# ÉTAPE 8 : SCRIPTS DE GESTION
# =====================================================

echo ""
echo "8⃣  SCRIPTS DE GESTION"
echo "====================="

# Script de démarrage complet
sudo tee /usr/local/bin/myapp-start-stack << 'START'
#!/bin/bash

echo "[RAPIDE] Démarrage stack MyApp..."
echo ""

# Ordre de démarrage
SERVICES=(
    "postgresql"
    "redis-server"
    "rabbitmq-server"
    "backend-api"
    "worker-queue"
    "nginx"
)

for service in "${SERVICES[@]}"; do
    echo "   Starting ${service}..."
    sudo systemctl start "$service"
    sleep 2
    
    if systemctl is-active --quiet "$service"; then
        echo "   [OK] ${service} started"
    else
        echo "   [X] ${service} failed"
        exit 1
    fi
done

echo ""
echo "[OK] Stack complète démarrée"
START

sudo chmod +x /usr/local/bin/myapp-start-stack

# Health check global
sudo tee /usr/local/bin/myapp-health-stack << 'HEALTH'
#!/bin/bash

echo "[HOPITAL] HEALTH CHECK - MyApp Stack"
echo "============================="
echo ""

SERVICES=(
    "postgresql:5432:PostgreSQL"
    "redis-server:6379:Redis"
    "rabbitmq-server:5672:RabbitMQ"
    "backend-api:4000:Backend API"
    "worker-queue::Worker"
    "nginx:80:Frontend"
)

ALL_HEALTHY=true

for service in "${SERVICES[@]}"; do
    IFS=: read -r name port label <<< "$service"
    
    if systemctl is-active --quiet "$name"; then
        if [ -n "$port" ]; then
            if nc -zv localhost "$port" 2>&1 | grep -q succeeded; then
                echo "[OK] ${label} - HEALTHY (port ${port})"
            else
                echo "[ATTENTION]  ${label} - SERVICE UP, PORT ${port} NOT LISTENING"
                ALL_HEALTHY=false
            fi
        else
            echo "[OK] ${label} - RUNNING"
        fi
    else
        echo "[X] ${label} - DOWN"
        ALL_HEALTHY=false
    fi
done

echo ""
if $ALL_HEALTHY; then
    echo "[OK] Stack entièrement healthy"
    exit 0
else
    echo "[X] Problèmes détectés"
    exit 1
fi
HEALTH

sudo chmod +x /usr/local/bin/myapp-health-stack

echo "   [OK] Scripts créés"

# =====================================================
# ÉTAPE 9 : ACTIVATION
# =====================================================

echo ""
echo "9⃣  ACTIVATION"
echo "============="

sudo systemctl daemon-reload

# Enable services
sudo systemctl enable postgresql redis-server rabbitmq-server
sudo systemctl enable backend-api worker-queue nginx
sudo systemctl enable myapp-stack.target

# Démarrer
/usr/local/bin/myapp-start-stack

sleep 5

# Health check
/usr/local/bin/myapp-health-stack

# =====================================================
# RÉCAPITULATIF
# =====================================================

echo ""
echo "[OK] STACK MICROSERVICES CONFIGURÉE"
echo "================================="
echo ""
echo "[GRAPHIQUE] Architecture :"
echo "   Frontend (Nginx :80)"
echo "        v"
echo "   Backend API (:4000)"
echo "        v"
echo "   PostgreSQL + Redis + RabbitMQ"
echo "        v"
echo "   Worker Queue"
echo ""
echo "[LIEN] Dépendances :"
echo "   nginx -> backend-api -> (postgresql + redis)"
echo "   worker-queue -> rabbitmq"
echo ""
echo "[OUTILS]  Commandes :"
echo "   # Stack complète"
echo "   systemctl start myapp-stack.target"
echo "   systemctl stop myapp-stack.target"
echo "   myapp-start-stack           # Démarrage ordonné"
echo "   myapp-health-stack          # Health check"
echo ""
echo "   # Services individuels"
echo "   systemctl status backend-api"
echo "   systemctl status worker-queue"
echo ""
echo "[WEB] URLs :"
echo "   Frontend : http://localhost/"
echo "   Backend  : http://localhost:4000/health"
echo ""
echo "[NOTE] Tests :"
echo "   curl http://localhost/"
echo "   curl http://localhost/api/data"
echo "   curl http://localhost:4000/health"

```

---

**Je continue avec les 3 cas restants** :
- CAS 3 : Système de monitoring et alertes
- CAS 4 : Maintenance automatisée avec timers
- CAS 5 : Service avec isolation de sécurité avancée

Ou tu veux que je passe à la **conclusion complète de la section** ?

# [DOCS] SECTION 13 : PROCESSUS & MONITORING SYSTÈME

# [JAUNE] GESTION COMPLÈTE DES PROCESSUS ET MONITORING (INTERMÉDIAIRE/AVANCÉ)

## [LISTE] INTRODUCTION

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Surveiller les processus en cours d'exécution
- Identifier les processus qui consomment trop de ressources
- Diagnostiquer les problèmes de performance
- Tuer des processus bloqués ou incontrôlables
- Gérer les priorités (nice/renice)
- Monitorer CPU, RAM, disque, réseau
- Analyser les goulots d'étranglement
- Mettre en place des alertes automatiques
- Optimiser les performances système
- Débugger des applications en production

### Méthodologie : POURQUOI / COMMENT / QUAND

Chaque concept sera expliqué selon :
- **POURQUOI** : Raison d'être, problème résolu
- **COMMENT** : Mise en œuvre technique détaillée
- **QUAND** : Cas d'usage, timing optimal

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cette section, tu sauras :
- [OK] Comprendre l'architecture des processus Linux
- [OK] Utiliser ps, top, htop efficacement
- [OK] Gérer les priorités (nice, renice)
- [OK] Envoyer des signaux (kill, killall, pkill)
- [OK] Monitorer les ressources système
- [OK] Utiliser les outils avancés (iotop, nethogs, etc.)
- [OK] Analyser les performances avec perf, strace
- [OK] Créer des dashboards de monitoring
- [OK] Mettre en place des alertes automatiques
- [OK] Implémenter 5 cas réels d'entreprise
- [OK] Troubleshooter les problèmes de performance
- [OK] Automatiser le monitoring

---

## [DOCS] TABLE DES MATIÈRES

1. **Concepts fondamentaux**
2. **Gestion des processus (ps, top, htop)**
3. **Signaux et contrôle de processus**
4. **Priorités et ordonnancement**
5. **Monitoring ressources système**
6. **Outils avancés de diagnostic**
7. **Analyse de performance**
8. **[ENTREPRISE] 5 CAS RÉELS ENTREPRISE**
9. **Troubleshooting avancé**
10. **Automatisation et alertes**

---

# PARTIE 1 : CONCEPTS FONDAMENTAUX

## [REFLEXION] POURQUOI comprendre les processus ?

```
PROBLÈMES SANS GESTION APPROPRIÉE :
[ROUGE] Serveur lent -> Impossible d'identifier la cause
[ROUGE] Application freeze -> Pas de moyen de débloquer
[ROUGE] Consommation RAM excessive -> Système crash
[ROUGE] CPU à 100% -> Quelle application ?
[ROUGE] Disque saturé -> Quel processus écrit ?
[ROUGE] Réseau lent -> Qui utilise la bande passante ?

SOLUTIONS APPORTÉES :
[OK] Visibilité complète sur les processus
[OK] Identification rapide des problèmes
[OK] Contrôle total (kill, renice)
[OK] Monitoring en temps réel
[OK] Alertes automatiques
[OK] Optimisation des performances
```

---

## [GRAPHIQUE] Architecture des processus Linux

### Hiérarchie des processus

```
POURQUOI une hiérarchie ?
- Chaque processus a un parent
- Organisation en arbre
- Héritage des propriétés (user, env, etc.)
- Propagation des signaux
```

```bash
# Créer environnement de démonstration
mkdir -p ~/process-exercise
cd ~/process-exercise
```

**Visualisation de la hiérarchie :**

```bash
cat > process-tree-demo.sh << 'EOF'
#!/bin/bash

echo "[ARBRE] ARBRE DES PROCESSUS"
echo "====================="
echo ""

# Arbre complet
echo "1⃣  Arbre complet (simplifié) :"
pstree | head -20
echo "   ..."
echo ""

# Avec PIDs
echo "2⃣  Avec PIDs :"
pstree -p | head -10
echo "   ..."
echo ""

# Processus d'un utilisateur
echo "3⃣  Processus de l'utilisateur courant :"
pstree -u $(whoami) | head -10
echo ""

# Focus sur un processus
echo "4⃣  Descendants d'un processus (systemd) :"
pstree -p 1 | head -15

EOF

chmod +x process-tree-demo.sh
./process-tree-demo.sh
```

---

### États d'un processus

```
POURQUOI différents états ?
- Optimiser utilisation CPU
- Gérer l'attente I/O
- Permettre multitâche
```

**États possibles :**

```
R  Running / Runnable
   QUOI : Processus en cours d'exécution ou prêt à s'exécuter
   QUAND : Travail CPU actif
   
S  Sleeping (interruptible)
   QUOI : En attente d'un événement (I/O, signal)
   QUAND : Attente données réseau, disque, clavier
   PEUT : Être réveillé par signal
   
D  Sleeping (uninterruptible)
   QUOI : Attente I/O critique (disque généralement)
   QUAND : Lecture/écriture disque
   NE PEUT PAS : Être interrompu (même kill -9)
   DANGER : Si bloqué = processus zombie
   
T  Stopped
   QUOI : Arrêté par signal (SIGSTOP, Ctrl+Z)
   QUAND : Debug, suspension manuelle
   REPRISE : SIGCONT ou fg/bg
   
Z  Zombie (defunct)
   QUOI : Processus terminé mais pas nettoyé par parent
   QUAND : Processus enfant terminé, parent n'a pas récupéré exit code
   PROBLÈME : Consomme PID, pas de ressources réelles
   SOLUTION : Kill parent ou attendre qu'il nettoie
   
I  Idle (kernel thread)
   QUOI : Thread kernel inactif
   QUAND : Threads noyau en attente
```

---

**Visualisation des états :**

```bash
cat > process-states-demo.sh << 'EOF'
#!/bin/bash

echo "[GRAPHIQUE] ÉTATS DES PROCESSUS"
echo "====================="
echo ""

echo "Statistiques des états :"
echo ""

# Compter par état
for state in R S D T Z I; do
    COUNT=$(ps aux | awk '{print $8}' | grep -c "^${state}")
    case $state in
        R) DESC="Running/Runnable" ;;
        S) DESC="Sleeping (interruptible)" ;;
        D) DESC="Sleeping (uninterruptible)" ;;
        T) DESC="Stopped" ;;
        Z) DESC="Zombie" ;;
        I) DESC="Idle" ;;
    esac
    printf "%-3s : %-30s %3d processus\n" "$state" "$DESC" "$COUNT"
done

echo ""
echo "Processus par état (exemples) :"
echo ""

# Exemples par état
echo "Running (R) :"
ps aux | awk '$8 ~ /^R/ {print "   " $2, $11}' | head -3

echo ""
echo "Sleeping (S) :"
ps aux | awk '$8 ~ /^S/ {print "   " $2, $11}' | head -5

# Détecter zombies
ZOMBIES=$(ps aux | awk '$8 ~ /^Z/ {print $2}' | wc -l)
if [ $ZOMBIES -gt 0 ]; then
    echo ""
    echo "[ATTENTION]  ZOMBIES DÉTECTÉS ($ZOMBIES) :"
    ps aux | awk '$8 ~ /^Z/ {print "   PID:", $2, "Parent:", $3, "Command:", $11}'
else
    echo ""
    echo "[OK] Aucun zombie"
fi

EOF

chmod +x process-states-demo.sh
./process-states-demo.sh
```

---

### Attributs d'un processus

```
CHAQUE PROCESSUS A :

PID     Process ID (identifiant unique)
PPID    Parent Process ID
UID     User ID (propriétaire)
GID     Group ID
EUID    Effective User ID (permissions réelles)
Priority Nice value (-20 à 19)
State   État actuel (R, S, D, T, Z)
%CPU    Utilisation CPU
%MEM    Utilisation mémoire
VSZ     Virtual memory Size (mémoire virtuelle)
RSS     Resident Set Size (mémoire physique réelle)
TTY     Terminal associé
START   Heure de démarrage
TIME    Temps CPU cumulé
COMMAND Commande exécutée
```

---

## [NOMBRE] PID, PPID et init/systemd

```
POURQUOI PID 1 est spécial ?

PID 1 = systemd (ou init)
- Premier processus au boot
- Parent de tous les orphelins
- Ne peut jamais mourir
- Si crash = kernel panic

PPID (Parent PID)
- Si parent meurt -> orphelin -> adopté par PID 1
- Héritage des variables d'environnement
- Gestion des zombies
```

**Démonstration :**

```bash
cat > pid-demo.sh << 'EOF'
#!/bin/bash

echo "[NOMBRE] PID, PPID et HIÉRARCHIE"
echo "========================="
echo ""

# PID 1
echo "PID 1 (systemd/init) :"
ps -p 1 -o pid,ppid,cmd
echo ""

# PID du shell courant
echo "Shell courant :"
echo "   PID  : $$"
echo "   PPID : $PPID"
ps -p $$ -o pid,ppid,cmd
echo ""

# Créer processus enfant
echo "Création processus enfant :"
(sleep 100 &)
CHILD_PID=$!
echo "   PID enfant : $CHILD_PID"
ps -p $CHILD_PID -o pid,ppid,cmd
echo ""

# Tuer enfant
kill $CHILD_PID 2>/dev/null
wait $CHILD_PID 2>/dev/null

# Montrer processus orphelins (adoptés par PID 1)
echo "Processus avec PPID=1 (orphelins adoptés) :"
ps -eo pid,ppid,cmd | awk '$2 == 1' | head -10

EOF

chmod +x pid-demo.sh
./pid-demo.sh
```

---

# PARTIE 2 : GESTION DES PROCESSUS

## [LISTE] Commande ps (Process Status)

### POURQUOI ps ?

```
PROBLÈME : Voir les processus en cours
SOLUTION : ps affiche snapshot des processus
QUAND : Diagnostic, recherche processus, scripts
```

---

### Syntaxes ps (BSD vs UNIX)

```
ATTENTION : ps a 3 syntaxes différentes !

BSD style    : ps aux (sans tirets)
UNIX style   : ps -ef (avec tirets)
GNU style    : ps --everyone --format ... (long)

RECOMMANDATION : Choisir une syntaxe et s'y tenir
```

---

**Test 1 : Syntaxe BSD (la plus courante)**

```bash
# Tous les processus, tous les utilisateurs
ps aux

# Format :
# USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
# root         1  0.0  0.1 168624 11504 ?        Ss   Dec30   0:03 /sbin/init

# Colonnes :
# USER  : Propriétaire
# PID   : Process ID
# %CPU  : Utilisation CPU
# %MEM  : Utilisation mémoire
# VSZ   : Mémoire virtuelle (KB)
# RSS   : Mémoire résidente physique (KB)
# TTY   : Terminal (? = pas de terminal)
# STAT  : État (R,S,D,T,Z + modificateurs)
# START : Heure démarrage
# TIME  : Temps CPU cumulé
# COMMAND : Commande

# Trier par CPU
ps aux --sort=-%cpu | head -10

# Trier par mémoire
ps aux --sort=-%mem | head -10

# Filtrer par utilisateur
ps aux | grep ^$USER

# Filtrer par nom de processus
ps aux | grep nginx
```

---

**Test 2 : Syntaxe UNIX**

```bash
# Tous les processus, format complet
ps -ef

# Format :
# UID        PID  PPID  C STIME TTY          TIME CMD
# root         1     0  0 Dec30 ?        00:00:03 /sbin/init

# Avec hiérarchie (arbre)
ps -efH

# Avec threads
ps -efL

# Format personnalisé
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -10
```

---

**Test 3 : Formats personnalisés**

```bash
# Format minimal
ps -eo pid,cmd

# Format détaillé personnalisé
ps -eo pid,ppid,user,%cpu,%mem,vsz,rss,stat,start,time,cmd

# Largeur illimitée (pas de troncature)
ps auxww

# Seulement certains processus
ps -C nginx          # Processus nginx
ps -u www-data       # Processus de www-data
ps -t pts/0          # Processus sur terminal pts/0
```

---

**Script d'analyse ps :**

```bash
cat > ps-analysis.sh << 'EOF'
#!/bin/bash

echo "[GRAPHIQUE] ANALYSE PROCESSUS (ps)"
echo "========================"
echo ""

# Top 10 CPU
echo "[HOT] Top 10 CPU :"
ps aux --sort=-%cpu | head -11 | tail -10 | \
    awk '{printf "   %5s  %6s%%  %-20s\n", $2, $3, $11}'
echo ""

# Top 10 Mémoire
echo "[SAUVEGARDE] Top 10 Mémoire :"
ps aux --sort=-%mem | head -11 | tail -10 | \
    awk '{printf "   %5s  %6s%%  %-20s\n", $2, $4, $11}'
echo ""

# Nombre de processus par utilisateur
echo "[UTILISATEURS] Processus par utilisateur :"
ps aux | awk 'NR>1 {count[$1]++} END {for (user in count) printf "   %-15s : %3d\n", user, count[user]}' | \
    sort -k3 -rn | head -10
echo ""

# Nombre de processus par état
echo "[HAUSSE] Processus par état :"
ps aux | awk 'NR>1 {count[substr($8,1,1)]++} END {for (state in count) printf "   %s : %3d\n", state, count[state]}'
echo ""

# Processus consommant le plus de mémoire (total)
echo "[ARGENT] Mémoire totale par processus :"
ps aux | awk 'NR>1 {mem[$11]+=$4} END {for (cmd in mem) printf "   %-30s : %.2f%%\n", cmd, mem[cmd]}' | \
    sort -t: -k2 -rn | head -10

EOF

chmod +x ps-analysis.sh
./ps-analysis.sh
```

---

## [GRAPHIQUE] Commande top (Monitoring temps réel)

### POURQUOI top ?

```
PROBLÈME : ps = snapshot statique
SOLUTION : top = mise à jour temps réel
QUAND : Monitoring actif, troubleshooting live
```

---

**Test 1 : Utilisation basique de top**

```bash
# Lancer top
top

# Interface :
# ┌─────────────────────────────────────────────────────┐
# │ top - 10:30:00 up 2 days, 3:15,  2 users            │
# │ Tasks: 157 total,   1 running, 156 sleeping         │
# │ %Cpu(s):  2.3 us,  1.1 sy,  0.0 ni, 96.5 id         │
# │ MiB Mem :   7977.0 total,   1234.5 free            │
# │ MiB Swap:   2048.0 total,   2048.0 free            │
# ├─────────────────────────────────────────────────────┤
# │  PID USER      PR  NI    VIRT    RES    SHR S  %CPU │
# │  123 user      20   0  123456   7890   1234 S   5.3 │
# └─────────────────────────────────────────────────────┘

# Commandes interactives dans top :
# h  : Aide
# q  : Quitter
# k  : Kill processus (demande PID)
# r  : Renice (changer priorité)
# M  : Trier par mémoire
# P  : Trier par CPU (défaut)
# T  : Trier par temps
# c  : Afficher commande complète
# 1  : Afficher tous les CPU individuellement
# W  : Sauvegarder configuration
```

---

**Test 2 : Options de lancement top**

```bash
# Lancer en mode batch (non-interactif)
top -b

# N itérations puis quitter
top -b -n 3

# Délai de rafraîchissement (secondes)
top -d 2

# Filtrer par utilisateur
top -u www-data

# Afficher processus d'un PID spécifique
top -p 1234

# Plusieurs PIDs
top -p 1234,5678,9012

# Sortie dans fichier (monitoring)
top -b -n 1 > /tmp/top-snapshot.txt
```

---

**Script de monitoring avec top :**

```bash
cat > top-monitoring.sh << 'EOF'
#!/bin/bash

echo "[GRAPHIQUE] MONITORING SYSTÈME (top)"
echo "=========================="
echo ""

# Snapshot top
SNAPSHOT=$(top -b -n 1)

# Uptime
UPTIME=$(echo "$SNAPSHOT" | head -1 | awk -F'up' '{print $2}' | awk -F',' '{print $1}')
echo "[TEMPS]  Uptime : $UPTIME"
echo ""

# Load average
LOAD=$(echo "$SNAPSHOT" | head -1 | awk -F'load average:' '{print $2}')
echo "[HAUSSE] Load average : $LOAD"
echo "   (1min, 5min, 15min)"
echo ""

# CPU
CPU_LINE=$(echo "$SNAPSHOT" | grep "Cpu(s)")
echo "[HOT] CPU :"
echo "   $CPU_LINE"
echo ""

# Mémoire
MEM_LINE=$(echo "$SNAPSHOT" | grep "MiB Mem")
SWAP_LINE=$(echo "$SNAPSHOT" | grep "MiB Swap")
echo "[SAUVEGARDE] Mémoire :"
echo "   $MEM_LINE"
echo "   $SWAP_LINE"
echo ""

# Top 5 processus CPU
echo "[TOP_WITH_UPWARDS_ARROW_ABOVE] Top 5 CPU :"
echo "$SNAPSHOT" | tail -n +8 | head -5 | \
    awk '{printf "   %5s  %6s%%  %-30s\n", $1, $9, $12}'

EOF

chmod +x top-monitoring.sh
./top-monitoring.sh
```

---

## [DESIGN] htop (top amélioré)

### POURQUOI htop ?

```
AVANTAGES vs top :
[OK] Interface colorée et intuitive
[OK] Scroll vertical et horizontal (souris)
[OK] Sélection visuelle des processus
[OK] Arbre des processus intégré
[OK] Kill/renice sans taper PID
[OK] Affichage tous les CPU
[OK] Recherche intégrée
```

---

**Installation et utilisation :**

```bash
# Installer htop
sudo apt install htop

# Lancer
htop

# Interface :
# ┌─ Barres CPU/Mémoire graphiques ─────────────────────┐
# │ 1 [|||||||||||||||                    25.3%]        │
# │ 2 [||||||||||                          15.1%]       │
# │ Mem[||||||||||||||||||||||||       3.5G/8.0G]      │
# │ Swp[                                0K/2.0G]        │
# ├──────────────────────────────────────────────────────┤
# │  PID USER   PRI  NI  VIRT   RES  SHR S CPU% MEM%    │
# │  123 user    20   0  123M  7890  1234 S  5.3  0.1   │
# └──────────────────────────────────────────────────────┘

# Raccourcis clavier :
# F1   : Aide
# F2   : Setup (configuration)
# F3   : Recherche
# F4   : Filtrer
# F5   : Arbre
# F6   : Trier par
# F7/F8: Nice +/-
# F9   : Kill
# F10  : Quit
# Space: Marquer processus
# u    : Filtrer par utilisateur
# t    : Arbre
# /    : Recherche
# \    : Filtrer
# k    : Kill
# a    : CPU affinity
# l    : Lister fichiers ouverts (lsof)
# s    : Tracer syscalls (strace)
```

---

**Configuration htop :**

```bash
# Fichier de configuration
cat ~/.config/htop/htoprc

# Configuration recommandée
mkdir -p ~/.config/htop
cat > ~/.config/htop/htoprc << 'HTOP'
# Afficher l'arbre par défaut
tree_view=1

# Trier par CPU
sort_key=46

# Masquer threads kernel
hide_kernel_threads=1

# Masquer threads utilisateur
hide_userland_threads=0

# Afficher noms complets
show_program_path=0

# Highlight program basename
highlight_base_name=1

# Couleurs
color_scheme=0

# Délai de mise à jour (dixièmes de seconde)
delay=15

# Colonnes affichées
fields=0 48 17 18 38 39 40 2 46 47 49 1
HTOP

echo "[OK] Configuration htop créée"
```

---

## [RECHERCHE] Autres commandes de visualisation

**pgrep - Chercher processus par nom**

```bash
# Trouver PID par nom
pgrep nginx

# Avec détails
pgrep -a nginx
# 1234 nginx: master process /usr/sbin/nginx
# 1235 nginx: worker process

# Par utilisateur
pgrep -u www-data

# Compter
pgrep -c nginx

# Inversé (qui N'est PAS)
pgrep -v -u root

# Avec PPID
pgrep -P 1  # Enfants directs de init/systemd
```

---

**pidof - PID d'un programme**

```bash
# PID de nginx
pidof nginx
# 1235 1234

# Premier PID seulement
pidof -s nginx
# 1234
```

---

**pstree - Arbre visuel**

```bash
# Arbre complet
pstree

# Avec PIDs
pstree -p

# D'un processus spécifique
pstree -p 1234

# Avec arguments
pstree -a

# Compact
pstree -c

# Seulement descendants d'un utilisateur
pstree $(whoami)
```

---

# PARTIE 3 : SIGNAUX ET CONTRÔLE

## [RAPIDE] Signaux Linux

### POURQUOI les signaux ?

```
PROBLÈME : Communication avec processus
SOLUTION : Signaux = messages au processus
QUAND : Arrêter, recharger config, débugger
```

---

### Liste des signaux importants

```bash
# Voir tous les signaux
kill -l

# Résultat :
#  1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
#  5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
#  9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
# 13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
# 17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
# ...
```

**Signaux principaux :**

| Signal | Numéro | Action | Description | Peut être intercepté ? |
|--------|--------|--------|-------------|------------------------|
| `SIGHUP` | 1 | Reload | Hang Up - Recharger config | [OK] Oui |
| `SIGINT` | 2 | Interrupt | Interruption (Ctrl+C) | [OK] Oui |
| `SIGQUIT` | 3 | Quit | Quit avec core dump (Ctrl+\) | [OK] Oui |
| `SIGKILL` | 9 | Kill | Tuer immédiatement | [X] **NON** |
| `SIGUSR1` | 10 | Custom | Signal utilisateur 1 | [OK] Oui |
| `SIGUSR2` | 12 | Custom | Signal utilisateur 2 | [OK] Oui |
| `SIGPIPE` | 13 | Pipe | Écriture sur pipe fermé | [OK] Oui |
| `SIGALRM` | 14 | Alarm | Timer expiré | [OK] Oui |
| `SIGTERM` | 15 | Terminate | Terminaison propre (défaut) | [OK] Oui |
| `SIGCHLD` | 17 | Child | Enfant terminé | [OK] Oui |
| `SIGCONT` | 18 | Continue | Reprendre après STOP | - |
| `SIGSTOP` | 19 | Stop | Arrêter processus | [X] **NON** |
| `SIGTSTP` | 20 | Stop | Arrêt interactif (Ctrl+Z) | [OK] Oui |

---

### QUAND utiliser chaque signal ?

```
SIGTERM (15) - Terminaison propre
  QUAND : Arrêt normal souhaité
  EFFET : Processus peut nettoyer (fermer fichiers, connexions)
  EXEMPLE : systemctl stop nginx

SIGKILL (9) - Tuer immédiatement
  QUAND : Processus bloqué, ne répond pas à SIGTERM
  EFFET : Terminaison immédiate, PAS de nettoyage
  DANGER : Peut laisser fichiers corrompus, locks
  EXEMPLE : Processus en état D (uninterruptible sleep)

SIGHUP (1) - Recharger configuration
  QUAND : Modifier config sans redémarrage
  EFFET : Application relit fichiers de config
  EXEMPLE : nginx -s reload (envoie SIGHUP)

SIGINT (2) - Interruption
  QUAND : Arrêt interactif depuis terminal
  EFFET : Comme Ctrl+C
  EXEMPLE : Arrêter script en cours

SIGSTOP/SIGCONT (19/18) - Pause/Reprise
  QUAND : Suspendre temporairement
  EFFET : Freeze/Unfreeze processus
  EXEMPLE : Ctrl+Z puis fg/bg
```

---

## [SKULL] Commandes kill, killall, pkill

**Test 1 : kill (par PID)**

```bash
# Syntaxe
kill [SIGNAL] PID

# Envoyer SIGTERM (défaut)
kill 1234

# Envoyer signal spécifique
kill -TERM 1234   # Même chose (SIGTERM)
kill -15 1234     # Même chose (numéro)
kill -9 1234      # SIGKILL (force)
kill -HUP 1234    # SIGHUP (reload)
kill -1 1234      # Même chose

# Vérifier si processus existe
kill -0 1234
# Exit code 0 = existe, 1 = n'existe pas

# Envoyer à plusieurs
kill 1234 5678 9012
```

---

**Test 2 : killall (par nom)**

```bash
# Tuer tous les processus avec ce nom
killall nginx

# Avec signal
killall -TERM nginx
killall -9 nginx

# Interactif (demande confirmation)
killall -i nginx

# Par utilisateur
killall -u www-data

# Attendre que processus se termine
killall -w nginx
# Bloque jusqu'à terminaison

# Dry-run (voir ce qui serait tué)
killall --dry-run nginx
```

---

**Test 3 : pkill (par pattern)**

```bash
# Tuer par pattern
pkill nginx

# Avec regex
pkill -f "nginx.*worker"

# Par utilisateur
pkill -u www-data

# Par groupe
pkill -g developers

# Par terminal
pkill -t pts/0

# Envoyer signal
pkill -HUP nginx
pkill -9 python

# Inverser match
pkill -v -u root  # Tous SAUF root
```

---

**Script de gestion de processus :**

```bash
cat > process-manager.sh << 'EOF'
#!/bin/bash

ACTION="${1}"
PROCESS="${2}"

case "$ACTION" in
    stop)
        echo "[STOP] Arrêt propre de $PROCESS..."
        pkill -TERM "$PROCESS"
        
        # Attendre 10 secondes
        for i in {1..10}; do
            if ! pgrep "$PROCESS" > /dev/null; then
                echo "[OK] $PROCESS arrêté"
                exit 0
            fi
            sleep 1
        done
        
        # Si toujours en vie, force kill
        echo "[ATTENTION]  $PROCESS ne répond pas, force kill..."
        pkill -KILL "$PROCESS"
        sleep 1
        
        if ! pgrep "$PROCESS" > /dev/null; then
            echo "[OK] $PROCESS tué"
        else
            echo "[X] Impossible de tuer $PROCESS"
            exit 1
        fi
        ;;
        
    restart)
        echo "[SYNC] Redémarrage de $PROCESS..."
        pkill -HUP "$PROCESS"
        ;;
        
    reload)
        echo "[SYNC] Rechargement config de $PROCESS..."
        pkill -USR1 "$PROCESS"
        ;;
        
    status)
        PIDS=$(pgrep "$PROCESS")
        if [ -z "$PIDS" ]; then
            echo "[X] $PROCESS n'est pas en cours d'exécution"
            exit 1
        else
            COUNT=$(echo "$PIDS" | wc -l)
            echo "[OK] $PROCESS est actif ($COUNT processus)"
            echo "   PIDs : $PIDS"
        fi
        ;;
        
    *)
        cat << USAGE
Usage: $0 <action> <process>

Actions:
  stop      - Arrêt propre (SIGTERM, puis SIGKILL si nécessaire)
  restart   - Recharger (SIGHUP)
  reload    - Signal utilisateur (SIGUSR1)
  status    - Vérifier si actif

Examples:
  $0 stop nginx
  $0 restart mysql
  $0 status apache2
USAGE
        exit 1
        ;;
esac
EOF

chmod +x process-manager.sh
```

---

**Je continue avec priorités (nice/renice), monitoring ressources et les 5 cas d'entreprise** ?

Ou tu préfères passer directement aux **5 cas d'entreprise** de cette section ?

# PARTIE 4 : PRIORITÉS ET ORDONNANCEMENT

## [LEVEL_SLIDER] Nice et Renice

### POURQUOI gérer les priorités ?

```
PROBLÈME : Tous les processus égaux -> CPU mal réparti
EXEMPLES :
- Backup consomme tout le CPU -> Site web lent
- Compilation bloque serveur -> Utilisateurs impactés
- Tâche batch prioritaire sur service web

SOLUTION : nice/renice pour contrôler priorités
EFFET : Ordonnanceur CPU favorise processus prioritaires
```

---

### COMMENT fonctionnent les priorités ?

```
NICE VALUE (NI)
├── Range : -20 (plus haute priorité) à +19 (plus basse)
├── Défaut : 0 (priorité normale)
├── Seul root peut : valeurs négatives (augmenter priorité)
└── Users peuvent : valeurs positives (baisser priorité)

PRIORITY (PR)
├── Valeur kernel = 20 + nice value
├── Range kernel : 0 à 39
├── PR = 20 -> nice = 0 (normal)
├── PR = 0  -> nice = -20 (max priorité)
└── PR = 39 -> nice = +19 (min priorité)

EFFET SUR CPU :
nice -20 -> 10x plus de CPU qu'un processus normal
nice  0  -> Répartition équitable
nice +19 -> Exécution seulement si CPU disponible
```

---

### Commande nice (démarrer avec priorité)

**Test 1 : Lancer processus avec nice**

```bash
# Syntaxe
nice -n NICE_VALUE command

# Lancer avec basse priorité (+10)
nice -n 10 tar -czf backup.tar.gz /home

# Lancer avec haute priorité (-10, nécessite root)
sudo nice -n -10 /usr/bin/critical-app

# Valeur par défaut (+10 si pas spécifié)
nice command  # Équivalent à nice -n 10

# Vérifier la priorité
ps -eo pid,ni,cmd | grep backup
```

---

**Test 2 : Exemples pratiques nice**

```bash
cat > nice-examples.sh << 'EOF'
#!/bin/bash

echo "[LEVEL_SLIDER]  EXEMPLES NICE"
echo "================"
echo ""

# 1. Backup avec basse priorité
echo "1⃣  Backup (basse priorité, nice +15) :"
nice -n 15 tar -czf /tmp/backup.tar.gz /etc &
BACKUP_PID=$!
echo "   PID : $BACKUP_PID"
sleep 1
ps -p $BACKUP_PID -o pid,ni,cmd
kill $BACKUP_PID 2>/dev/null
wait $BACKUP_PID 2>/dev/null
echo ""

# 2. Compilation (basse priorité)
echo "2⃣  Compilation (nice +10) :"
echo "   nice -n 10 make -j4"
echo "   -> Compile sans ralentir autres services"
echo ""

# 3. Tâche critique (haute priorité, root)
echo "3⃣  Tâche critique (nice -10, root requis) :"
echo "   sudo nice -n -10 /usr/bin/realtime-monitor"
echo "   -> Priorité maximale pour monitoring temps réel"
echo ""

# 4. Script batch nocturne
echo "4⃣  Script batch (nice +19 = priorité minimale) :"
echo "   nice -n 19 /usr/local/bin/nightly-reports.sh"
echo "   -> S'exécute seulement si CPU libre"

EOF

chmod +x nice-examples.sh
./nice-examples.sh
```

---

### Commande renice (modifier priorité processus en cours)

**Test 1 : Renice basique**

```bash
# Syntaxe
renice NICE_VALUE -p PID

# Augmenter nice (baisser priorité)
renice +10 -p 1234

# Diminuer nice (augmenter priorité, root seulement)
sudo renice -10 -p 1234

# Tous les processus d'un utilisateur
renice +5 -u www-data

# Tous les processus d'un groupe
renice +5 -g developers

# Plusieurs PIDs
renice +10 -p 1234 -p 5678 -p 9012
```

---

**Test 2 : Renice interactif depuis top/htop**

```bash
# Dans top :
# 1. Appuyer sur 'r'
# 2. Entrer PID
# 3. Entrer nouvelle nice value

# Dans htop :
# 1. Sélectionner processus (flèches)
# 2. F7 = diminuer nice (augmenter priorité)
# 3. F8 = augmenter nice (baisser priorité)
```

---

**Script de gestion automatique des priorités :**

```bash
cat > auto-renice.sh << 'EOF'
#!/bin/bash

echo "[LEVEL_SLIDER]  AUTO-RENICE : Gestion automatique priorités"
echo "=============================================="

# Configuration
declare -A PROCESS_PRIORITIES=(
    ["nginx"]=-5              # Web server : haute priorité
    ["mysql"]=-5              # Database : haute priorité
    ["php-fpm"]=-5           # PHP : haute priorité
    ["backup"]=15             # Backup : basse priorité
    ["make"]=10               # Compilation : basse priorité
    ["rsync"]=10              # Sync : basse priorité
    ["tar"]=15                # Archive : basse priorité
    ["gzip"]=15               # Compression : basse priorité
)

echo ""
echo "[LISTE] Règles de priorité :"
for proc in "${!PROCESS_PRIORITIES[@]}"; do
    printf "   %-15s : nice %+3d\n" "$proc" "${PROCESS_PRIORITIES[$proc]}"
done
echo ""

echo "[SYNC] Application des priorités..."
echo ""

for proc in "${!PROCESS_PRIORITIES[@]}"; do
    NICE_VALUE="${PROCESS_PRIORITIES[$proc]}"
    
    # Trouver tous les PIDs
    PIDS=$(pgrep -d, "$proc")
    
    if [ -n "$PIDS" ]; then
        # Compter
        COUNT=$(echo "$PIDS" | tr ',' '\n' | wc -l)
        
        # Renice
        if [ $NICE_VALUE -lt 0 ]; then
            # Nécessite root
            sudo renice $NICE_VALUE -p $(echo $PIDS | tr ',' ' ') >/dev/null 2>&1
        else
            renice $NICE_VALUE -p $(echo $PIDS | tr ',' ' ') >/dev/null 2>&1
        fi
        
        if [ $? -eq 0 ]; then
            printf "   [OK] %-15s : %2d processus -> nice %+3d\n" "$proc" "$COUNT" "$NICE_VALUE"
        else
            printf "   [X] %-15s : Échec renice\n" "$proc"
        fi
    else
        printf "   ⊝  %-15s : Aucun processus\n" "$proc"
    fi
done

echo ""
echo "[OK] Terminé"

EOF

chmod +x auto-renice.sh
```

---

**Automatiser avec cron :**

```bash
# Crontab pour ajuster priorités toutes les heures
cat > /etc/cron.hourly/auto-renice << 'CRON'
#!/bin/bash
/usr/local/bin/auto-renice.sh >> /var/log/auto-renice.log 2>&1
CRON

chmod +x /etc/cron.hourly/auto-renice
```

---

## [GRAPHIQUE] CPU Affinity (bonus avancé)

### POURQUOI CPU affinity ?

```
PROBLÈME : Processus saute entre CPUs -> cache invalidé
SOLUTION : Pincer processus sur CPUs spécifiques
QUAND : Applications temps réel, optimisation performance
```

```bash
# Voir affinity actuel
taskset -p 1234

# Définir affinity (CPU 0 et 1)
taskset -cp 0,1 1234

# Lancer avec affinity
taskset -c 0,1 ./mon-application

# Masque binaire (cores 0,2,4)
taskset 0x15 ./mon-application
# 0x15 = 0b10101 = cores 0,2,4
```

---

# PARTIE 5 : MONITORING RESSOURCES SYSTÈME

## [CODE] Monitoring CPU

### uptime et load average

```
POURQUOI load average ?
- Indicateur de charge système
- 3 valeurs : 1min, 5min, 15min
- Tendance sur le temps

INTERPRÉTATION :
Load < nombre de CPUs  -> OK
Load = nombre de CPUs  -> Saturé
Load > nombre de CPUs  -> Overloaded

Exemple 4 CPUs :
Load 2.0  -> 50% utilisé
Load 4.0  -> 100% utilisé
Load 8.0  -> 200% (queue)
```

**Test 1 : Analyser load average**

```bash
# Voir load average
uptime
# 10:30:00 up 2 days, 3:15, 2 users, load average: 1.23, 1.45, 1.67
#                                                    ^     ^     ^
#                                                  1min  5min  15min

# Tendances :
# 1min > 5min > 15min -> Charge augmente
# 1min < 5min < 15min -> Charge diminue
# Tous égaux -> Stable

# Nombre de CPUs
nproc
# 4

# Load per CPU
cat > load-analysis.sh << 'EOF'
#!/bin/bash

CPUS=$(nproc)
LOAD_1=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | xargs)
LOAD_5=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $2}' | xargs)
LOAD_15=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $3}' | xargs)

echo "[GRAPHIQUE] ANALYSE LOAD AVERAGE"
echo "======================"
echo ""
echo "CPUs : $CPUS"
echo ""
echo "Load Average :"
printf "   1min  : %.2f (%.0f%% des CPUs)\n" "$LOAD_1" "$(echo "$LOAD_1 / $CPUS * 100" | bc -l)"
printf "   5min  : %.2f (%.0f%% des CPUs)\n" "$LOAD_5" "$(echo "$LOAD_5 / $CPUS * 100" | bc -l)"
printf "   15min : %.2f (%.0f%% des CPUs)\n" "$LOAD_15" "$(echo "$LOAD_15 / $CPUS * 100" | bc -l)"
echo ""

# Évaluation
LOAD_PERCENT=$(echo "$LOAD_1 / $CPUS * 100" | bc -l | cut -d. -f1)

if [ $LOAD_PERCENT -lt 70 ]; then
    echo "[OK] Charge normale"
elif [ $LOAD_PERCENT -lt 90 ]; then
    echo "[ATTENTION]  Charge élevée"
else
    echo "[ROUGE] SURCHARGE"
fi

EOF

chmod +x load-analysis.sh
./load-analysis.sh
```

---

### mpstat (CPU détaillé)

```bash
# Installer sysstat
sudo apt install sysstat

# Statistiques CPU globales
mpstat

# Tous les CPUs individuellement
mpstat -P ALL

# Rafraîchir toutes les 2 secondes, 5 fois
mpstat 2 5

# Format détaillé
mpstat -P ALL 1 3

# Résultat :
# CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
#   0    2.51    0.00    1.23    0.15    0.00    0.10    0.00    0.00    0.00   96.01
#   1    1.89    0.00    0.98    0.08    0.00    0.05    0.00    0.00    0.00   97.00

# Colonnes importantes :
# %usr    : User space (applications)
# %sys    : Kernel space (système)
# %iowait : Attente I/O (disque lent si élevé)
# %idle   : Inactivité (bon signe si élevé)
# %steal  : Temps volé (VM, si >5% = problème hyperviseur)
```

---

### vmstat (Statistiques complètes)

```bash
# Vue d'ensemble
vmstat

# Rafraîchir chaque seconde
vmstat 1

# 10 itérations, chaque 2 secondes
vmstat 2 10

# Résultat :
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  1  0      0 123456  12345 234567    0    0    10    20  100  200  5  2 93  0  0

# Colonnes importantes :
# PROCS:
#   r : runnable (attendent CPU)
#   b : blocked (attendent I/O)
#
# MEMORY:
#   swpd : swap utilisé
#   free : RAM libre
#   buff : buffers
#   cache : cache
#
# SWAP:
#   si : swap in (MB/s)
#   so : swap out (MB/s)
#
# IO:
#   bi : blocks in (lecture)
#   bo : blocks out (écriture)
#
# CPU:
#   us : user
#   sy : system
#   id : idle
#   wa : iowait
#   st : steal
```

---

## [SAUVEGARDE] Monitoring Mémoire

### free (Mémoire disponible)

```bash
# Affichage basique
free

# Human-readable
free -h

# Résultat :
#               total        used        free      shared  buff/cache   available
# Mem:          7.8Gi       2.1Gi       3.2Gi       234Mi       2.5Gi       5.2Gi
# Swap:         2.0Gi          0B       2.0Gi

# Colonnes :
# total     : RAM totale
# used      : Utilisée par applications
# free      : Vraiment libre (non utilisée)
# shared    : tmpfs, shared memory
# buff/cache: Cache disque (libérable)
# available : Disponible pour nouvelles apps (free + récupérable)

# [ATTENTION] IMPORTANT : Regarder "available", pas "free"

# Rafraîchir toutes les 2 secondes
free -h -s 2

# Afficher totaux
free -h -t
```

---

**Analyse mémoire :**

```bash
cat > memory-analysis.sh << 'EOF'
#!/bin/bash

echo "[SAUVEGARDE] ANALYSE MÉMOIRE"
echo "================="
echo ""

# Récupérer valeurs
MEM_TOTAL=$(free -m | awk 'NR==2 {print $2}')
MEM_USED=$(free -m | awk 'NR==2 {print $3}')
MEM_FREE=$(free -m | awk 'NR==2 {print $4}')
MEM_AVAILABLE=$(free -m | awk 'NR==2 {print $7}')
SWAP_TOTAL=$(free -m | awk 'NR==3 {print $2}')
SWAP_USED=$(free -m | awk 'NR==3 {print $3}')

# Calculer pourcentages
MEM_USED_PERCENT=$((MEM_USED * 100 / MEM_TOTAL))
MEM_AVAILABLE_PERCENT=$((MEM_AVAILABLE * 100 / MEM_TOTAL))

if [ $SWAP_TOTAL -gt 0 ]; then
    SWAP_USED_PERCENT=$((SWAP_USED * 100 / SWAP_TOTAL))
else
    SWAP_USED_PERCENT=0
fi

echo "RAM :"
printf "   Total      : %5d MB\n" "$MEM_TOTAL"
printf "   Utilisée   : %5d MB (%3d%%)\n" "$MEM_USED" "$MEM_USED_PERCENT"
printf "   Disponible : %5d MB (%3d%%)\n" "$MEM_AVAILABLE" "$MEM_AVAILABLE_PERCENT"
echo ""

echo "SWAP :"
printf "   Total    : %5d MB\n" "$SWAP_TOTAL"
printf "   Utilisée : %5d MB (%3d%%)\n" "$SWAP_USED" "$SWAP_USED_PERCENT"
echo ""

# Évaluation
if [ $MEM_AVAILABLE_PERCENT -lt 10 ]; then
    echo "[ROUGE] CRITIQUE : Mémoire très basse (<10%)"
elif [ $MEM_AVAILABLE_PERCENT -lt 20 ]; then
    echo "[ATTENTION]  ATTENTION : Mémoire basse (<20%)"
else
    echo "[OK] Mémoire OK"
fi

if [ $SWAP_USED_PERCENT -gt 50 ]; then
    echo "[ATTENTION]  SWAP fortement utilisé (>50%)"
    echo "   -> Envisager augmenter RAM"
fi

echo ""
echo "[TOP_WITH_UPWARDS_ARROW_ABOVE] Top 5 processus par RAM :"
ps aux --sort=-%mem | head -6 | tail -5 | \
    awk '{printf "   %5s  %6s%%  %-30s\n", $2, $4, $11}'

EOF

chmod +x memory-analysis.sh
./memory-analysis.sh
```

---

### smem (Mémoire par processus, détaillée)

```bash
# Installer
sudo apt install smem

# Vue d'ensemble
smem

# Résumé par utilisateur
smem -u

# Résumé par processus
smem -P

# Top consumers
smem -r

# Avec graphique
smem --pie=name

# Colonnes :
# USS : Unique Set Size (mémoire uniquement ce processus)
# PSS : Proportional Set Size (shared divisé par nb partage)
# RSS : Resident Set Size (tout en RAM, y compris shared)
```

---

## [DISQUE] Monitoring Disque I/O

### iostat (I/O Statistics)

```bash
# Installer (dans sysstat)
sudo apt install sysstat

# Statistiques I/O
iostat

# Détaillé avec tous les disques
iostat -x

# Rafraîchir chaque 2 secondes
iostat -x 2

# Résultat :
# Device            r/s     w/s     rkB/s   wkB/s   await  %util
# sda              1.23    4.56    123.45  456.78    5.67   12.34

# Colonnes :
# r/s    : lectures/seconde
# w/s    : écritures/seconde
# rkB/s  : KB lus/seconde
# wkB/s  : KB écrits/seconde
# await  : temps d'attente moyen (ms)
# %util  : utilisation (si >80% = bottleneck)
```

---

### iotop (top pour I/O)

```bash
# Installer
sudo apt install iotop

# Lancer (nécessite root)
sudo iotop

# Trier par I/O
sudo iotop -o

# Mode batch (non-interactif)
sudo iotop -b

# N itérations
sudo iotop -b -n 3

# Résultat :
# TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO    COMMAND
# 123    be/4 root       11.00 M/s    0.00 B/s  0.00 % 99.99 % tar ...

# Colonnes :
# DISK READ  : Vitesse lecture
# DISK WRITE : Vitesse écriture
# IO         : % temps en I/O
```

---

**Script de monitoring I/O :**

```bash
cat > io-monitoring.sh << 'EOF'
#!/bin/bash

echo "[DISQUE] MONITORING I/O DISQUE"
echo "======================="
echo ""

# Installer si nécessaire
if ! command -v iostat &> /dev/null; then
    echo "[ATTENTION]  iostat non installé, installation..."
    sudo apt install -y sysstat
fi

echo "[GRAPHIQUE] Statistiques I/O (snapshot) :"
echo ""

# Snapshot iostat
iostat -x 1 2 | tail -n +4 | awk '
NR>1 {
    device=$1
    util=$NF
    await=$(NF-2)
    
    if (util > 80) {
        status="[ROUGE] SATURÉ"
    } else if (util > 50) {
        status="[ATTENTION]  ÉLEVÉ"
    } else {
        status="[OK] OK"
    }
    
    printf "   %-10s : %5.1f%% util, %6.2fms await  %s\n", device, util, await, status
}
'

echo ""
echo "[TOP_WITH_UPWARDS_ARROW_ABOVE] Top 5 processus par I/O :"

# Nécessite root pour iotop
if [ "$(id -u)" -eq 0 ]; then
    iotop -b -n 1 -o -P | head -10 | tail -5 | \
        awk '{printf "   %5s  %10s  %-30s\n", $1, $6, $NF}'
else
    echo "   (nécessite root : sudo ./io-monitoring.sh)"
fi

EOF

chmod +x io-monitoring.sh
```

---

## [WEB] Monitoring Réseau

### iftop (top pour réseau)

```bash
# Installer
sudo apt install iftop

# Lancer
sudo iftop

# Interface spécifique
sudo iftop -i eth0

# Afficher ports
sudo iftop -P

# Mode texte (non-interactif)
sudo iftop -t -s 5
```

---

### nethogs (Usage réseau par processus)

```bash
# Installer
sudo apt install nethogs

# Lancer
sudo nethogs

# Interface spécifique
sudo nethogs eth0

# Résultat :
# PID   USER   PROGRAM           DEV   SENT    RECEIVED
# 123   user   firefox           eth0  123 KB  456 KB
# 456   root   nginx             eth0  789 KB  012 KB
```

---

### vnstat (Statistiques réseau historiques)

```bash
# Installer
sudo apt install vnstat

# Activer monitoring
sudo systemctl enable vnstat
sudo systemctl start vnstat

# Voir statistiques
vnstat

# Par interface
vnstat -i eth0

# Dernière heure
vnstat -h

# Aujourd'hui
vnstat -d

# Ce mois
vnstat -m

# Live
vnstat -l
```

---

## [HAUSSE] Dashboard de monitoring complet

```bash
cat > system-dashboard.sh << 'EOF'
#!/bin/bash

# Dashboard complet de monitoring système

clear

while true; do
    clear
    
    echo "╔════════════════════════════════════════════════════════════════╗"
    echo "║           [ECRAN]  DASHBOARD SYSTÈME - $(date '+%Y-%m-%d %H:%M:%S')           ║"
    echo "╚════════════════════════════════════════════════════════════════╝"
    echo ""
    
    # ===== UPTIME ET LOAD =====
    UPTIME_INFO=$(uptime)
    LOAD_1=$(echo "$UPTIME_INFO" | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | xargs)
    LOAD_5=$(echo "$UPTIME_INFO" | awk -F'load average:' '{print $2}' | awk -F, '{print $2}' | xargs)
    LOAD_15=$(echo "$UPTIME_INFO" | awk -F'load average:' '{print $2}' | awk -F, '{print $3}' | xargs)
    
    echo "[TEMPS]  UPTIME & LOAD"
    echo "────────────────────────────────────────────────────────────────"
    echo "   Uptime : $(uptime -p)"
    printf "   Load   : %.2f (1m)  %.2f (5m)  %.2f (15m)\n" "$LOAD_1" "$LOAD_5" "$LOAD_15"
    echo ""
    
    # ===== CPU =====
    echo "[HOT] CPU"
    echo "────────────────────────────────────────────────────────────────"
    mpstat 1 1 | tail -1 | awk '{
        usr=$3
        sys=$5
        iowait=$6
        idle=$NF
        printf "   User: %5.1f%%  System: %5.1f%%  I/O Wait: %5.1f%%  Idle: %5.1f%%\n", usr, sys, iowait, idle
    }'
    echo ""
    
    # ===== MÉMOIRE =====
    echo "[SAUVEGARDE] MÉMOIRE"
    echo "────────────────────────────────────────────────────────────────"
    free -m | awk '
    NR==2 {
        total=$2
        used=$3
        available=$7
        percent=used*100/total
        printf "   RAM  : %5d MB / %5d MB (%3.0f%% utilisé)\n", used, total, percent
    }
    NR==3 {
        if ($2 > 0) {
            percent=$3*100/$2
            printf "   SWAP : %5d MB / %5d MB (%3.0f%% utilisé)\n", $3, $2, percent
        } else {
            printf "   SWAP : Aucun\n"
        }
    }
    '
    echo ""
    
    # ===== DISQUE =====
    echo "[DISQUE] DISQUE"
    echo "────────────────────────────────────────────────────────────────"
    df -h | awk '
    NR>1 && $1 ~ /^\/dev\// {
        device=$1
        size=$2
        used=$3
        avail=$4
        percent=$5
        mount=$6
        
        gsub(/%/, "", percent)
        if (percent > 90) {
            status="[ROUGE]"
        } else if (percent > 80) {
            status="[ATTENTION] "
        } else {
            status="[OK]"
        }
        
        printf "   %s %-15s : %5s / %5s (%3s%%)  %s\n", status, mount, used, size, percent, device
    }
    '
    echo ""
    
    # ===== TOP PROCESSUS CPU =====
    echo "[TOP_WITH_UPWARDS_ARROW_ABOVE] TOP 5 CPU"
    echo "────────────────────────────────────────────────────────────────"
    ps aux --sort=-%cpu | head -6 | tail -5 | \
        awk '{printf "   %5s  %6s%%  %-40s\n", $2, $3, $11}'
    echo ""
    
    # ===== TOP PROCESSUS MÉMOIRE =====
    echo "[TOP_WITH_UPWARDS_ARROW_ABOVE] TOP 5 MÉMOIRE"
    echo "────────────────────────────────────────────────────────────────"
    ps aux --sort=-%mem | head -6 | tail -5 | \
        awk '{printf "   %5s  %6s%%  %-40s\n", $2, $4, $11}'
    echo ""
    
    # ===== RÉSEAU =====
    echo "[WEB] RÉSEAU"
    echo "────────────────────────────────────────────────────────────────"
    for iface in $(ls /sys/class/net/ | grep -v lo); do
        if [ -f "/sys/class/net/$iface/statistics/rx_bytes" ]; then
            RX=$(cat /sys/class/net/$iface/statistics/rx_bytes)
            TX=$(cat /sys/class/net/$iface/statistics/tx_bytes)
            
            RX_MB=$(echo "scale=2; $RX / 1024 / 1024" | bc)
            TX_MB=$(echo "scale=2; $TX / 1024 / 1024" | bc)
            
            printf "   %-10s : v %8.2f MB  ^ %8.2f MB\n" "$iface" "$RX_MB" "$TX_MB"
        fi
    done
    echo ""
    
    # ===== FOOTER =====
    echo "────────────────────────────────────────────────────────────────"
    echo "   Rafraîchissement : 5s  |  Quitter : Ctrl+C"
    
    sleep 5
done

EOF

chmod +x system-dashboard.sh
```

**Lancer le dashboard :**

```bash
./system-dashboard.sh
```

---

# PARTIE 6 : [ENTREPRISE] 5 CAS RÉELS ENTREPRISE

## CAS 1 : [ALERTE] SYSTÈME D'ALERTES AUTOMATIQUES

### POURQUOI ce cas ?

```
CONTEXTE : Serveur de production 24/7
BESOIN :
- Détecter surcharge CPU (>80%)
- Détecter RAM critique (<10%)
- Détecter disque plein (>90%)
- Détecter processus zombie
- Détecter services down
- Alertes par email/Slack
- Logs détaillés
- Actions automatiques (kill, restart)

CONTRAINTES :
- Monitoring 24/7
- Réaction rapide (<1 minute)
- Faux positifs minimaux
- Logs audit complets
```

---

### COMMENT implémenter ?

```bash
#!/bin/bash
# setup-monitoring-alerts.sh

echo "[ALERTE] CONFIGURATION SYSTÈME D'ALERTES"
echo "=================================="

MONITOR_DIR="/opt/monitoring"
LOG_DIR="/var/log/monitoring"
CONFIG_FILE="/etc/monitoring/alerts.conf"

# =====================================================
# ÉTAPE 1 : STRUCTURE
# =====================================================

echo ""
echo "1⃣  CRÉATION STRUCTURE"
echo "===================="

sudo mkdir -p "$MONITOR_DIR"/{scripts,data}
sudo mkdir -p "$LOG_DIR"
sudo mkdir -p /etc/monitoring

echo "   [OK] Structure créée"

# =====================================================
# ÉTAPE 2 : CONFIGURATION
# =====================================================

echo ""
echo "2⃣  CONFIGURATION"
echo "================"

sudo tee "$CONFIG_FILE" << 'CONFIG'
# Configuration alertes système

# Seuils
CPU_THRESHOLD=80
MEMORY_THRESHOLD=90
DISK_THRESHOLD=90
LOAD_THRESHOLD=4.0

# Services critiques à surveiller
CRITICAL_SERVICES="nginx mysql redis-server"

# Email
ALERT_EMAIL="admin@company.com"

# Slack (optionnel)
SLACK_WEBHOOK=""

# Actions automatiques
AUTO_KILL_HIGH_CPU=true
AUTO_RESTART_FAILED_SERVICES=true

# Cooldown (secondes entre alertes identiques)
ALERT_COOLDOWN=300
CONFIG

echo "   [OK] Configuration créée : $CONFIG_FILE"

# =====================================================
# ÉTAPE 3 : SCRIPT DE MONITORING
# =====================================================

echo ""
echo "3⃣  SCRIPT DE MONITORING"
echo "======================="

sudo tee "$MONITOR_DIR/scripts/monitor.sh" << 'MONITOR'
#!/bin/bash

# Charger configuration
source /etc/monitoring/alerts.conf

LOG_FILE="/var/log/monitoring/alerts.log"
STATE_DIR="/opt/monitoring/data"

# Fonctions utilitaires
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

send_alert() {
    local SEVERITY="$1"
    local TITLE="$2"
    local MESSAGE="$3"
    
    # Log
    log_message "[$SEVERITY] $TITLE - $MESSAGE"
    
    # Email
    if [ -n "$ALERT_EMAIL" ]; then
        echo "$MESSAGE" | mail -s "[$SEVERITY] $TITLE - $(hostname)" "$ALERT_EMAIL"
    fi
    
    # Slack
    if [ -n "$SLACK_WEBHOOK" ]; then
        curl -X POST "$SLACK_WEBHOOK" \
            -H 'Content-Type: application/json' \
            -d "{\"text\":\"[$SEVERITY] $TITLE\n$MESSAGE\"}" \
            2>/dev/null
    fi
}

check_cooldown() {
    local ALERT_ID="$1"
    local STATE_FILE="$STATE_DIR/${ALERT_ID}.last"
    
    if [ -f "$STATE_FILE" ]; then
        LAST_ALERT=$(cat "$STATE_FILE")
        NOW=$(date +%s)
        ELAPSED=$((NOW - LAST_ALERT))
        
        if [ $ELAPSED -lt $ALERT_COOLDOWN ]; then
            return 1  # Encore en cooldown
        fi
    fi
    
    # Enregistrer nouvelle alerte
    date +%s > "$STATE_FILE"
    return 0
}

# ===== CHECK CPU =====
check_cpu() {
    CPU_USAGE=$(mpstat 1 1 | tail -1 | awk '{print 100-$NF}' | cut -d. -f1)
    
    if [ "$CPU_USAGE" -gt "$CPU_THRESHOLD" ]; then
        if check_cooldown "cpu_high"; then
            # Identifier processus gourmands
            TOP_PROCESSES=$(ps aux --sort=-%cpu | head -6 | tail -5 | \
                awk '{printf "%s (PID %s): %.1f%%\n", $11, $2, $3}')
            
            send_alert "WARNING" "CPU élevé: ${CPU_USAGE}%" \
                "CPU utilisation: ${CPU_USAGE}%\n\nTop processus:\n$TOP_PROCESSES"
            
            # Action automatique
            if [ "$AUTO_KILL_HIGH_CPU" = "true" ]; then
                # Kill processus non-critiques >50% CPU
                ps aux | awk '$3 > 50 && $11 !~ /(systemd|init|nginx|mysql)/ {print $2}' | \
                while read PID; do
                    PROC_NAME=$(ps -p $PID -o comm=)
                    log_message "AUTO-KILL: Processus $PROC_NAME (PID $PID) consomme >50% CPU"
                    kill -15 $PID
                done
            fi
        fi
    fi
}

# ===== CHECK MÉMOIRE =====
check_memory() {
    MEM_USED=$(free | awk 'NR==2 {printf "%.0f", $3/$2*100}')
    MEM_AVAILABLE=$(free -m | awk 'NR==2 {print $7}')
    
    if [ "$MEM_USED" -gt "$MEMORY_THRESHOLD" ]; then
        if check_cooldown "memory_high"; then
            TOP_MEM=$(ps aux --sort=-%mem | head -6 | tail -5 | \
                awk '{printf "%s (PID %s): %.1f%%\n", $11, $2, $4}')
            
            send_alert "CRITICAL" "Mémoire critique: ${MEM_USED}%" \
                "Mémoire utilisée: ${MEM_USED}%\nDisponible: ${MEM_AVAILABLE} MB\n\nTop processus:\n$TOP_MEM"
        fi
    fi
}

# ===== CHECK DISQUE =====
check_disk() {
    df -h | awk 'NR>1 && $1 ~ /^\/dev\// {gsub(/%/,"",$5); if($5>'"$DISK_THRESHOLD"') print $6":"$5}' | \
    while IFS=: read MOUNT USAGE; do
        ALERT_ID="disk_${MOUNT//\//_}"
        
        if check_cooldown "$ALERT_ID"; then
            # Top fichiers/dossiers
            TOP_DIRS=$(du -h --max-depth=2 "$MOUNT" 2>/dev/null | sort -rh | head -5)
            
            send_alert "WARNING" "Disque plein: $MOUNT (${USAGE}%)" \
                "Partition: $MOUNT\nUtilisation: ${USAGE}%\n\nTop dossiers:\n$TOP_DIRS"
        fi
    done
}

# ===== CHECK LOAD =====
check_load() {
    LOAD_1=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | xargs)
    CPUS=$(nproc)
    
    LOAD_THRESHOLD_CALC=$(echo "$CPUS * 2" | bc)
    
    if (( $(echo "$LOAD_1 > $LOAD_THRESHOLD_CALC" | bc -l) )); then
        if check_cooldown "load_high"; then
            send_alert "WARNING" "Load average élevé: $LOAD_1" \
                "Load: $LOAD_1\nCPUs: $CPUS\nSeuil: $LOAD_THRESHOLD_CALC"
        fi
    fi
}

# ===== CHECK ZOMBIES =====
check_zombies() {
    ZOMBIES=$(ps aux | awk '$8 ~ /^Z/ {print $2}')
    ZOMBIE_COUNT=$(echo "$ZOMBIES" | grep -c '^')
    
    if [ $ZOMBIE_COUNT -gt 0 ]; then
        if check_cooldown "zombies"; then
            ZOMBIE_INFO=$(ps aux | awk '$8 ~ /^Z/ {printf "PID %s (Parent: %s)\n", $2, $3}')
            
            send_alert "WARNING" "$ZOMBIE_COUNT processus zombie(s)" \
                "Zombies détectés:\n$ZOMBIE_INFO"
        fi
    fi
}

# ===== CHECK SERVICES =====
check_services() {
    for SERVICE in $CRITICAL_SERVICES; do
        if ! systemctl is-active --quiet "$SERVICE"; then
            ALERT_ID="service_${SERVICE}"
            
            if check_cooldown "$ALERT_ID"; then
                send_alert "CRITICAL" "Service DOWN: $SERVICE" \
                    "Le service $SERVICE n'est pas actif.\n\nStatus:\n$(systemctl status $SERVICE --no-pager | head -10)"
                
                # Restart automatique
                if [ "$AUTO_RESTART_FAILED_SERVICES" = "true" ]; then
                    log_message "AUTO-RESTART: Redémarrage $SERVICE"
                    systemctl restart "$SERVICE"
                    sleep 2
                    
                    if systemctl is-active --quiet "$SERVICE"; then
                        send_alert "INFO" "Service restauré: $SERVICE" \
                            "Le service $SERVICE a été redémarré avec succès."
                    else
                        send_alert "CRITICAL" "Échec restart: $SERVICE" \
                            "Le redémarrage automatique de $SERVICE a échoué."
                    fi
                fi
            fi
        fi
    done
}

# ===== MAIN =====
main() {
    log_message "=== Début monitoring ==="
    
    check_cpu
    check_memory
    check_disk
    check_load
    check_zombies
    check_services
    
    log_message "=== Fin monitoring ==="
}

main
MONITOR

sudo chmod +x "$MONITOR_DIR/scripts/monitor.sh"

echo "   [OK] Script de monitoring créé"

# =====================================================
# ÉTAPE 4 : SCRIPT DE RAPPORT
# =====================================================

echo ""
echo "4⃣  SCRIPT DE RAPPORT"
echo "===================="

sudo tee "$MONITOR_DIR/scripts/daily-report.sh" << 'REPORT'
#!/bin/bash

LOG_FILE="/var/log/monitoring/alerts.log"
REPORT_FILE="/tmp/daily-report-$(date +%Y%m%d).txt"

{
    echo "═══════════════════════════════════════════════════════════"
    echo "          RAPPORT QUOTIDIEN - $(date '+%Y-%m-%d')"
    echo "═══════════════════════════════════════════════════════════"
    echo ""
    
    # Statistiques alertes
    echo "[GRAPHIQUE] STATISTIQUES ALERTES (dernières 24h)"
    echo "───────────────────────────────────────────────────────────"
    
    YESTERDAY=$(date -d "yesterday" +%Y-%m-%d)
    
    TOTAL=$(grep -c "$YESTERDAY" "$LOG_FILE" 2>/dev/null || echo 0)
    CRITICAL=$(grep "$YESTERDAY" "$LOG_FILE" 2>/dev/null | grep -c "\[CRITICAL\]" || echo 0)
    WARNING=$(grep "$YESTERDAY" "$LOG_FILE" 2>/dev/null | grep -c "\[WARNING\]" || echo 0)
    
    printf "   Total alertes  : %3d\n" "$TOTAL"
    printf "   Critiques      : %3d\n" "$CRITICAL"
    printf "   Warnings       : %3d\n" "$WARNING"
    echo ""
    
    # Alertes par type
    echo "[LISTE] ALERTES PAR TYPE"
    echo "───────────────────────────────────────────────────────────"
    grep "$YESTERDAY" "$LOG_FILE" 2>/dev/null | \
        sed 's/.*\] //' | cut -d' ' -f1-3 | sort | uniq -c | sort -rn | \
        awk '{printf "   %-40s : %3d\n", substr($0, 9), $1}'
    echo ""
    
    # Top processus problématiques
    echo "[HOT] INCIDENTS CRITIQUES"
    echo "───────────────────────────────────────────────────────────"
    grep "$YESTERDAY" "$LOG_FILE" 2>/dev/null | grep "\[CRITICAL\]" | \
        tail -10
    echo ""
    
    # État actuel système
    echo "[CODE] ÉTAT SYSTÈME ACTUEL"
    echo "───────────────────────────────────────────────────────────"
    echo "   Uptime     : $(uptime -p)"
    echo "   Load       : $(uptime | awk -F'load average:' '{print $2}')"
    echo "   CPU usage  : $(mpstat 1 1 | tail -1 | awk '{printf "%.1f%%", 100-$NF}')"
    echo "   RAM usage  : $(free | awk 'NR==2 {printf "%.0f%%", $3/$2*100}')"
    echo "   Disk usage : $(df -h / | awk 'NR==2 {print $5}')"
    echo ""
    
    # Services
    echo "[OUTIL] SERVICES"
    echo "───────────────────────────────────────────────────────────"
    systemctl is-active nginx mysql redis-server 2>/dev/null | \
        paste <(echo -e "nginx\nmysql\nredis") - | \
        awk '{printf "   %-15s : %s\n", $1, ($2=="active" ? "[OK] OK" : "[X] DOWN")}'
    echo ""
    
    echo "═══════════════════════════════════════════════════════════"
    echo "Rapport généré le $(date)"
    
} > "$REPORT_FILE"

# Envoyer par email
mail -s "Rapport quotidien monitoring - $(hostname)" admin@company.com < "$REPORT_FILE"

echo "[OK] Rapport envoyé"
REPORT

sudo chmod +x "$MONITOR_DIR/scripts/daily-report.sh"

echo "   [OK] Script de rapport créé"

# =====================================================
# ÉTAPE 5 : CRON JOBS
# =====================================================

echo ""
echo "5⃣  CONFIGURATION CRON"
echo "====================="

# Monitoring toutes les minutes
(sudo crontab -l 2>/dev/null; echo "* * * * * $MONITOR_DIR/scripts/monitor.sh") | sudo crontab -

# Rapport quotidien à 8h
(sudo crontab -l 2>/dev/null; echo "0 8 * * * $MONITOR_DIR/scripts/daily-report.sh") | sudo crontab -

echo "   [OK] Cron configuré"
echo "      - Monitoring : Chaque minute"
echo "      - Rapport    : Quotidien 8h"

# =====================================================
# ÉTAPE 6 : SERVICE SYSTEMD (alternative)
# =====================================================

echo ""
echo "6⃣  SERVICE SYSTEMD"
echo "=================="

sudo tee /etc/systemd/system/monitoring-alerts.service << 'SERVICE'
[Unit]
Description=System Monitoring and Alerts
After=network.target

[Service]
Type=oneshot
ExecStart=/opt/monitoring/scripts/monitor.sh
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
SERVICE

sudo tee /etc/systemd/system/monitoring-alerts.timer << 'TIMER'
[Unit]
Description=System Monitoring Timer
Requires=monitoring-alerts.service

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
AccuracySec=1s

[Install]
WantedBy=timers.target
TIMER

sudo systemctl daemon-reload
sudo systemctl enable monitoring-alerts.timer
sudo systemctl start monitoring-alerts.timer

echo "   [OK] Service systemd créé et démarré"

# =====================================================
# RÉCAPITULATIF
# =====================================================

echo ""
echo "[OK] SYSTÈME D'ALERTES CONFIGURÉ"
echo "=============================="
echo ""
echo "[DOSSIER] Structure :"
echo "   Scripts    : $MONITOR_DIR/scripts/"
echo "   Config     : $CONFIG_FILE"
echo "   Logs       : $LOG_DIR/alerts.log"
echo ""
echo "[OUTIL] Configuration :"
echo "   CPU threshold    : ${CPU_THRESHOLD}%"
echo "   Memory threshold : ${MEMORY_THRESHOLD}%"
echo "   Disk threshold   : ${DISK_THRESHOLD}%"
echo ""
echo "[GRAPHIQUE] Monitoring :"
echo "   - CPU et Load"
echo "   - Mémoire et Swap"
echo "   - Disque"
echo "   - Processus zombies"
echo "   - Services critiques"
echo ""
echo "[RAPIDE] Statut :"
echo "   systemctl status monitoring-alerts.timer"
echo "   journalctl -u monitoring-alerts -f"
echo ""
echo "[NOTE] Logs :"
echo "   tail -f $LOG_DIR/alerts.log"
echo ""
echo "[CONFIG]  Personnaliser :"
echo "   sudo vim $CONFIG_FILE"
echo "   systemctl restart monitoring-alerts.timer"

```

---

**Je continue avec les 4 cas restants** :
- CAS 2 : Détection et kill automatique processus problématiques
- CAS 3 : Profiling performance application
- CAS 4 : Monitoring multi-serveurs centralisé
- CAS 5 : Optimisation automatique ressources

Ou tu préfères passer à **une autre section** de la liste ?

# [DOCS] SECTION 14 : RÉSEAU & CONFIGURATION RÉSEAU

# [JAUNE] GESTION COMPLÈTE DU RÉSEAU LINUX (INTERMÉDIAIRE/AVANCÉ)

## [LISTE] INTRODUCTION

### Contexte professionnel

Tu es administrateur système et tu dois quotidiennement :
- Configurer les interfaces réseau
- Diagnostiquer les problèmes de connectivité
- Analyser le trafic réseau
- Configurer le routage
- Gérer le DNS et DHCP
- Sécuriser les connexions (firewall, VPN)
- Monitorer la bande passante
- Troubleshooter les performances réseau
- Configurer des VLANs et bridges
- Automatiser la configuration réseau

### Méthodologie : POURQUOI / COMMENT / QUAND

Chaque concept sera expliqué selon :
- **POURQUOI** : Raison d'être, problème résolu
- **COMMENT** : Mise en œuvre technique détaillée
- **QUAND** : Cas d'usage, timing optimal

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cette section, tu sauras :
- [OK] Comprendre l'architecture réseau Linux
- [OK] Configurer les interfaces réseau (ifconfig, ip)
- [OK] Gérer le routage et les tables de routage
- [OK] Diagnostiquer avec ping, traceroute, mtr
- [OK] Analyser avec netstat, ss, tcpdump
- [OK] Configurer DNS et résolution de noms
- [OK] Gérer NetworkManager et systemd-networkd
- [OK] Créer des bridges et VLANs
- [OK] Implémenter 5 cas réels d'entreprise
- [OK] Troubleshooter les problèmes réseau
- [OK] Optimiser les performances réseau

---

## [DOCS] TABLE DES MATIÈRES

1. **Concepts fondamentaux**
2. **Configuration interfaces réseau**
3. **Routage et tables de routage**
4. **Diagnostic réseau (ping, traceroute, mtr)**
5. **Analyse connexions (netstat, ss)**
6. **Capture et analyse (tcpdump, wireshark)**
7. **DNS et résolution de noms**
8. **NetworkManager vs systemd-networkd**
9. **[ENTREPRISE] 5 CAS RÉELS ENTREPRISE**
10. **Troubleshooting avancé**
11. **Optimisation et bonnes pratiques**

---

# PARTIE 1 : CONCEPTS FONDAMENTAUX

## [REFLEXION] POURQUOI comprendre le réseau ?

```
PROBLÈMES SANS COMPÉTENCES RÉSEAU :
[ROUGE] Serveur inaccessible -> Perte de service
[ROUGE] Connexion lente -> Utilisateurs mécontents
[ROUGE] Paquets perdus -> Applications instables
[ROUGE] DNS ne résout pas -> Sites inaccessibles
[ROUGE] Routage incorrect -> Isolation des serveurs
[ROUGE] Saturation bande passante -> Performances dégradées

SOLUTIONS APPORTÉES :
[OK] Diagnostic rapide des problèmes
[OK] Configuration correcte dès le départ
[OK] Monitoring proactif
[OK] Optimisation performances
[OK] Sécurisation du trafic
[OK] Haute disponibilité
```

---

## [GRAPHIQUE] Modèle OSI vs TCP/IP

```bash
# Créer environnement de démonstration
mkdir -p ~/network-exercise
cd ~/network-exercise
```

**Modèle de référence :**

```
MODÈLE OSI (7 couches)          MODÈLE TCP/IP (4 couches)
══════════════════════          ═══════════════════════════

7. Application ──┐
6. Présentation  ├──────────────> 4. Application
5. Session      ─┘                   (HTTP, FTP, SSH, DNS)

4. Transport ────────────────────> 3. Transport
                                      (TCP, UDP)

3. Réseau ───────────────────────> 2. Internet
                                      (IP, ICMP, ARP)

2. Liaison ──┐
1. Physique ─┴────────────────────> 1. Accès réseau
                                      (Ethernet, WiFi)
```

---

**Visualisation pratique :**

```bash
cat > network-stack-demo.sh << 'EOF'
#!/bin/bash

echo "[WEB] STACK RÉSEAU - VISUALISATION"
echo "==============================="
echo ""

# Exemple : Requête HTTP
echo "[RESEAU] Exemple : wget http://example.com/page.html"
echo ""
echo "COUCHES TRAVERSÉES :"
echo ""

echo "┌─ APPLICATION (HTTP)"
echo "│  GET /page.html HTTP/1.1"
echo "│  Host: example.com"
echo "│"
echo "├─ TRANSPORT (TCP)"
echo "│  Port source: 45678 -> Port dest: 80"
echo "│  Flags: SYN, ACK, PSH"
echo "│  Sequence numbers, checksums"
echo "│"
echo "├─ INTERNET (IP)"
echo "│  IP source: 192.168.1.10"
echo "│  IP dest: 93.184.216.34 (example.com)"
echo "│  TTL, fragmentation"
echo "│"
echo "└─ ACCÈS RÉSEAU (Ethernet)"
echo "   MAC source: aa:bb:cc:dd:ee:ff"
echo "   MAC dest: (gateway) 11:22:33:44:55:66"
echo "   Frame Check Sequence"
echo ""

echo "[SYNC] ENCAPSULATION :"
echo "   Application -> [HTTP] GET /page.html"
echo "   Transport   -> [TCP][HTTP] data"
echo "   Internet    -> [IP][TCP][HTTP] data"
echo "   Ethernet    -> [Ethernet][IP][TCP][HTTP] data [CRC]"

EOF

chmod +x network-stack-demo.sh
./network-stack-demo.sh
```

---

## [IMPORTANT] Adressage IP

### IPv4

```
POURQUOI IPv4 ?
- Standard actuel (encore majoritaire)
- 32 bits = 4.3 milliards d'adresses
- Format : X.X.X.X (0-255 par octet)

CLASSES D'ADRESSES :
Classe A : 1.0.0.0 à 126.255.255.255 (grandes entreprises)
Classe B : 128.0.0.0 à 191.255.255.255 (moyennes entreprises)
Classe C : 192.0.0.0 à 223.255.255.255 (petites entreprises)

ADRESSES PRIVÉES (RFC 1918) :
10.0.0.0/8        : 10.0.0.0 à 10.255.255.255
172.16.0.0/12     : 172.16.0.0 à 172.31.255.255
192.168.0.0/16    : 192.168.0.0 à 192.168.255.255

ADRESSES SPÉCIALES :
127.0.0.0/8       : Loopback (localhost)
169.254.0.0/16    : Link-local (APIPA)
0.0.0.0           : Toutes les interfaces
255.255.255.255   : Broadcast
```

---

### Masques de sous-réseau (CIDR)

```bash
cat > cidr-calculator.sh << 'EOF'
#!/bin/bash

echo "[NOMBRE] CALCULATEUR CIDR"
echo "==================="
echo ""

# Exemples
declare -A CIDRS=(
    ["/8"]="255.0.0.0 (16,777,216 hosts)"
    ["/16"]="255.255.0.0 (65,536 hosts)"
    ["/24"]="255.255.255.0 (256 hosts)"
    ["/25"]="255.255.255.128 (128 hosts)"
    ["/26"]="255.255.255.192 (64 hosts)"
    ["/27"]="255.255.255.224 (32 hosts)"
    ["/28"]="255.255.255.240 (16 hosts)"
    ["/29"]="255.255.255.248 (8 hosts)"
    ["/30"]="255.255.255.252 (4 hosts - lien point-à-point)"
    ["/32"]="255.255.255.255 (1 host - adresse unique)"
)

echo "NOTATION CIDR COURANTE :"
echo ""

for cidr in $(echo ${!CIDRS[@]} | tr ' ' '\n' | sort -V); do
    printf "   %-6s -> %s\n" "$cidr" "${CIDRS[$cidr]}"
done

echo ""
echo "EXEMPLE : 192.168.1.0/24"
echo "   Réseau     : 192.168.1.0"
echo "   Masque     : 255.255.255.0"
echo "   Broadcast  : 192.168.1.255"
echo "   Plage hosts: 192.168.1.1 à 192.168.1.254"
echo "   Nombre     : 254 hosts utilisables"

EOF

chmod +x cidr-calculator.sh
./cidr-calculator.sh
```

---

### IPv6 (aperçu)

```
POURQUOI IPv6 ?
- Épuisement IPv4
- 128 bits = 340 undécillions d'adresses
- Pas besoin de NAT
- Auto-configuration
- Sécurité intégrée (IPSec)

FORMAT :
xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
(8 groupes de 4 chiffres hexadécimaux)

EXEMPLES :
2001:0db8:85a3:0000:0000:8a2e:0370:7334
2001:db8:85a3::8a2e:370:7334 (simplifié)

ADRESSES SPÉCIALES :
::1           : Loopback (localhost IPv6)
::            : Toutes les interfaces
fe80::/10     : Link-local
fc00::/7      : Unique local (équivalent privé)
2000::/3      : Global unicast
```

---

## [PLUGIN] Interfaces réseau

```
TYPES D'INTERFACES :

PHYSIQUES :
eth0, eth1    : Ethernet filaire (ancienne notation)
eno1, enp0s3  : Ethernet (nouvelle notation prévisible)
wlan0, wlp2s0 : WiFi
lo            : Loopback (localhost)

VIRTUELLES :
br0           : Bridge (pont)
vlan10        : VLAN
tun0, tap0    : VPN (tunnel)
docker0       : Docker bridge
vethXXX       : Virtual Ethernet (containers)
```

---

**Nouvelle notation (Predictable Network Interface Names) :**

```
POURQUOI changer de eth0 à enp0s3 ?
- eth0 pouvait changer selon ordre détection
- Nom prévisible basé sur hardware

FORMAT :
en  : Ethernet
wl  : Wireless LAN
ww  : Wireless WAN

SUFFIXES :
o<index>      : On-board
s<slot>       : Hotplug slot
p<port>s<slot>: PCI position

EXEMPLES :
eno1     : Ethernet on-board #1
enp0s3   : Ethernet PCI bus 0 slot 3
wlp2s0   : WiFi PCI bus 2 slot 0
```

---

# PARTIE 2 : CONFIGURATION INTERFACES RÉSEAU

## [OUTILS] Commande ip (moderne, recommandée)

### POURQUOI ip au lieu de ifconfig ?

```
ifconfig (obsolète)    vs    ip (moderne)
───────────────────          ──────────────
- Limité en options          + Complet
- Ne supporte pas VLAN       + Supporte VLAN
- Syntaxe incohérente        + Syntaxe uniforme
- Deprecated                 + Activement maintenu
- Package net-tools          + Package iproute2

RECOMMANDATION : Utiliser 'ip' exclusivement
```

---

### Commandes ip essentielles

**Test 1 : Voir les interfaces**

```bash
# Lister toutes les interfaces
ip link show

# Ou simplement
ip link

# Résultat :
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
#     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP
#     link/ether 08:00:27:3c:a5:4f brd ff:ff:ff:ff:ff:ff

# Interface spécifique
ip link show eth0

# Seulement les interfaces UP
ip link show up

# Format concis
ip -br link
# lo     UNKNOWN   00:00:00:00:00:00
# eth0   UP        08:00:27:3c:a5:4f
```

---

**Test 2 : Voir les adresses IP**

```bash
# Toutes les adresses
ip address show

# Ou simplement
ip addr
ip a

# Interface spécifique
ip addr show eth0

# Format concis
ip -br addr
# lo       UNKNOWN   127.0.0.1/8 ::1/128
# eth0     UP        192.168.1.10/24 fe80::a00:27ff:fe3c:a54f/64

# Seulement IPv4
ip -4 addr

# Seulement IPv6
ip -6 addr

# Avec couleurs
ip -c addr
```

---

**Test 3 : Configurer une adresse IP**

```bash
# Ajouter une adresse IP
sudo ip addr add 192.168.1.100/24 dev eth0

# Supprimer une adresse IP
sudo ip addr del 192.168.1.100/24 dev eth0

# Flush toutes les adresses d'une interface
sudo ip addr flush dev eth0

# [ATTENTION] Ces changements sont TEMPORAIRES (perdus au reboot)
```

---

**Test 4 : Activer/Désactiver interface**

```bash
# Activer (up)
sudo ip link set eth0 up

# Désactiver (down)
sudo ip link set eth0 down

# Changer MTU
sudo ip link set eth0 mtu 9000

# Changer adresse MAC
sudo ip link set eth0 address aa:bb:cc:dd:ee:ff
```

---

**Script d'analyse des interfaces :**

```bash
cat > network-interfaces-analysis.sh << 'EOF'
#!/bin/bash

echo "[PLUGIN] ANALYSE DES INTERFACES RÉSEAU"
echo "================================="
echo ""

# Liste des interfaces (sauf lo)
INTERFACES=$(ip -br link | grep -v "^lo" | awk '{print $1}')

for IFACE in $INTERFACES; do
    echo "[RESEAU] Interface : $IFACE"
    echo "   ────────────────────────────────────────"
    
    # État
    STATE=$(ip -br link show $IFACE | awk '{print $2}')
    if [ "$STATE" = "UP" ]; then
        echo "   État      : [OK] UP"
    else
        echo "   État      : [X] DOWN"
    fi
    
    # MAC
    MAC=$(ip link show $IFACE | grep "link/ether" | awk '{print $2}')
    if [ -n "$MAC" ]; then
        echo "   MAC       : $MAC"
    fi
    
    # MTU
    MTU=$(ip link show $IFACE | grep -oP 'mtu \K[0-9]+')
    echo "   MTU       : $MTU"
    
    # IPv4
    IPV4=$(ip -4 addr show $IFACE | grep "inet " | awk '{print $2}')
    if [ -n "$IPV4" ]; then
        echo "   IPv4      : $IPV4"
    else
        echo "   IPv4      : Non configuré"
    fi
    
    # IPv6
    IPV6=$(ip -6 addr show $IFACE | grep "inet6" | grep -v "fe80" | awk '{print $2}')
    if [ -n "$IPV6" ]; then
        echo "   IPv6      : $IPV6"
    fi
    
    # Statistiques
    RX_BYTES=$(ip -s link show $IFACE | awk '/RX:/,/bytes/ {getline; print $1; exit}')
    TX_BYTES=$(ip -s link show $IFACE | awk '/TX:/,/bytes/ {getline; print $1; exit}')
    
    RX_MB=$(echo "scale=2; $RX_BYTES / 1024 / 1024" | bc 2>/dev/null || echo "0")
    TX_MB=$(echo "scale=2; $TX_BYTES / 1024 / 1024" | bc 2>/dev/null || echo "0")
    
    echo "   RX (reçu) : ${RX_MB} MB"
    echo "   TX (envoyé): ${TX_MB} MB"
    
    echo ""
done

EOF

chmod +x network-interfaces-analysis.sh
./network-interfaces-analysis.sh
```

---

## [NOTE] Configuration persistante

### POURQUOI configuration persistante ?

```
PROBLÈME : 'ip addr add' = temporaire, perdu au reboot
SOLUTION : Fichiers de configuration
QUAND : Configuration production
```

---

### Netplan (Ubuntu 18.04+)

```bash
# Emplacement
ls /etc/netplan/

# Fichier de configuration (YAML)
cat > /tmp/example-netplan.yaml << 'YAML'
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: false
      addresses:
        - 192.168.1.10/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
YAML

# Appliquer configuration
sudo netplan apply

# Tester sans appliquer
sudo netplan try

# Debug
sudo netplan --debug apply
```

---

**Exemples de configurations Netplan :**

**1. DHCP simple :**

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: true
      dhcp6: false
```

**2. IP statique :**

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: false
      addresses:
        - 192.168.1.10/24
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
          - 1.1.1.1
```

**3. Plusieurs adresses IP :**

```yaml
network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 192.168.1.10/24
        - 192.168.1.11/24
        - 10.0.0.5/8
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses:
          - 8.8.8.8
```

**4. WiFi :**

```yaml
network:
  version: 2
  wifis:
    wlan0:
      dhcp4: true
      access-points:
        "MyWiFiSSID":
          password: "MyWiFiPassword"
```

**5. Bridge (pour VMs/Containers) :**

```yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: false
  
  bridges:
    br0:
      dhcp4: true
      interfaces:
        - eth0
```

**6. VLAN :**

```yaml
network:
  version: 2
  ethernets:
    eth0:
      dhcp4: false
  
  vlans:
    vlan10:
      id: 10
      link: eth0
      addresses:
        - 192.168.10.5/24
```

---

### Debian/Ubuntu classique (/etc/network/interfaces)

```bash
# Fichier de configuration
cat > /tmp/example-interfaces << 'CONF'
# Loopback
auto lo
iface lo inet loopback

# Interface principale - DHCP
auto eth0
iface eth0 inet dhcp

# Interface secondaire - IP statique
auto eth1
iface eth1 inet static
    address 192.168.1.10
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8 8.8.4.4

# Interface avec IP secondaire
auto eth2
iface eth2 inet static
    address 10.0.0.5
    netmask 255.0.0.0

iface eth2 inet static
    address 10.0.0.6
    netmask 255.0.0.0
CONF

# Redémarrer réseau
sudo systemctl restart networking

# Ou interface spécifique
sudo ifdown eth0 && sudo ifup eth0
```

---

### RHEL/CentOS (/etc/sysconfig/network-scripts/)

```bash
# Fichier par interface
cat > /tmp/ifcfg-eth0 << 'CONF'
# IP statique
TYPE=Ethernet
BOOTPROTO=none
NAME=eth0
DEVICE=eth0
ONBOOT=yes
IPADDR=192.168.1.10
PREFIX=24
GATEWAY=192.168.1.1
DNS1=8.8.8.8
DNS2=8.8.4.4
CONF

# DHCP
cat > /tmp/ifcfg-eth1 << 'CONF'
TYPE=Ethernet
BOOTPROTO=dhcp
NAME=eth1
DEVICE=eth1
ONBOOT=yes
CONF

# Redémarrer interface
sudo ifdown eth0
sudo ifup eth0

# Ou avec nmcli (NetworkManager)
sudo nmcli connection reload
sudo nmcli connection up eth0
```

---

## [WEB] Passerelle (Gateway) et routage

**Test 1 : Voir la passerelle par défaut**

```bash
# Avec ip
ip route show

# Résultat :
# default via 192.168.1.1 dev eth0 proto dhcp metric 100
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10

# Seulement la route par défaut
ip route show default

# Format concis
ip -br route
```

---

**Test 2 : Configurer passerelle**

```bash
# Ajouter passerelle par défaut
sudo ip route add default via 192.168.1.1 dev eth0

# Supprimer passerelle
sudo ip route del default

# Ajouter route spécifique
sudo ip route add 10.0.0.0/8 via 192.168.1.254 dev eth0

# Supprimer route
sudo ip route del 10.0.0.0/8
```

---

**Script de diagnostic réseau complet :**

```bash
cat > network-diagnostic.sh << 'EOF'
#!/bin/bash

echo "[RECHERCHE] DIAGNOSTIC RÉSEAU COMPLET"
echo "============================"
echo ""

# 1. Interfaces
echo "1⃣  INTERFACES"
echo "   ──────────────────────────────────────"
ip -br -c addr
echo ""

# 2. Routes
echo "2⃣  ROUTES"
echo "   ──────────────────────────────────────"
ip route show
echo ""

# 3. Passerelle
echo "3⃣  PASSERELLE PAR DÉFAUT"
echo "   ──────────────────────────────────────"
DEFAULT_GW=$(ip route show default | awk '{print $3}')
if [ -n "$DEFAULT_GW" ]; then
    echo "   Gateway : $DEFAULT_GW"
    
    # Test ping gateway
    if ping -c 1 -W 2 "$DEFAULT_GW" > /dev/null 2>&1; then
        echo "   Status  : [OK] Accessible"
    else
        echo "   Status  : [X] Inaccessible"
    fi
else
    echo "   [X] Aucune passerelle configurée"
fi
echo ""

# 4. DNS
echo "4⃣  DNS"
echo "   ──────────────────────────────────────"
if [ -f /etc/resolv.conf ]; then
    grep "^nameserver" /etc/resolv.conf | while read line; do
        DNS=$(echo $line | awk '{print $2}')
        echo "   Nameserver : $DNS"
        
        # Test DNS
        if host google.com "$DNS" > /dev/null 2>&1; then
            echo "   Status     : [OK] Fonctionne"
        else
            echo "   Status     : [X] Ne répond pas"
        fi
    done
else
    echo "   [ATTENTION]  /etc/resolv.conf absent"
fi
echo ""

# 5. Connectivité Internet
echo "5⃣  CONNECTIVITÉ INTERNET"
echo "   ──────────────────────────────────────"

# Test 1: Ping Google DNS
if ping -c 1 -W 2 8.8.8.8 > /dev/null 2>&1; then
    echo "   Ping 8.8.8.8  : [OK] OK"
else
    echo "   Ping 8.8.8.8  : [X] FAIL"
fi

# Test 2: Résolution DNS
if host google.com > /dev/null 2>&1; then
    echo "   DNS           : [OK] OK"
else
    echo "   DNS           : [X] FAIL"
fi

# Test 3: HTTP
if curl -s -m 5 http://google.com > /dev/null 2>&1; then
    echo "   HTTP          : [OK] OK"
else
    echo "   HTTP          : [X] FAIL"
fi

echo ""

# 6. Statistiques
echo "6⃣  STATISTIQUES"
echo "   ──────────────────────────────────────"

MAIN_IFACE=$(ip route show default | awk '{print $5}')
if [ -n "$MAIN_IFACE" ]; then
    echo "   Interface principale : $MAIN_IFACE"
    
    RX=$(ip -s link show $MAIN_IFACE | awk '/RX:/,/bytes/ {getline; print $1; exit}')
    TX=$(ip -s link show $MAIN_IFACE | awk '/TX:/,/bytes/ {getline; print $1; exit}')
    
    RX_MB=$(echo "scale=2; $RX / 1024 / 1024" | bc 2>/dev/null || echo "0")
    TX_MB=$(echo "scale=2; $TX / 1024 / 1024" | bc 2>/dev/null || echo "0")
    
    echo "   Reçu  (RX)           : ${RX_MB} MB"
    echo "   Envoyé (TX)          : ${TX_MB} MB"
fi

EOF

chmod +x network-diagnostic.sh
./network-diagnostic.sh
```

---

**Je continue avec :**
1. Routage avancé et tables de routage
2. Diagnostic réseau (ping, traceroute, mtr, tcpdump)
3. DNS et résolution
4. **Les 5 cas d'entreprise**

Ou tu préfères passer directement aux **5 cas d'entreprise** de cette section ?

