Teoria Bash 05: systemd i Suport Gràfic

1. Introducció a systemd

Què és systemd?

systemd és el sistema d'inicialització i gestió de serveis més utilitzat en distribucions Linux modernes (Ubuntu, Debian, Fedora, CentOS, Arch, etc.).

Funcions principals:

Per què systemd?

Avantatges sobre SysV init tradicional:

Conceptes bàsics

2. Gestió de serveis amb systemctl

systemctl és la comanda principal per interactuar amb systemd.

Comandes bàsiques

# Iniciar un servei
sudo systemctl start nginx

# Aturar un servei
sudo systemctl stop nginx

# Reiniciar un servei
sudo systemctl restart nginx

# Recarregar configuració sense reiniciar
sudo systemctl reload nginx

# Reiniciar només si està actiu
sudo systemctl try-restart nginx

# Recarregar o reiniciar
sudo systemctl reload-or-restart nginx

Habilitar/deshabilitar serveis

# Habilitar servei al boot (crea symlink)
sudo systemctl enable nginx

# Deshabilitar servei al boot
sudo systemctl disable nginx

# Habilitar i iniciar
sudo systemctl enable --now nginx

# Deshabilitar i aturar
sudo systemctl disable --now nginx

# Comprovar si està habilitat
systemctl is-enabled nginx

Comprovar estat

# Estat detallat d'un servei
systemctl status nginx

# Comprovar si està actiu
systemctl is-active nginx

# Comprovar si ha fallat
systemctl is-failed nginx

# Llistar tots els serveis
systemctl list-units --type=service

# Llistar només serveis actius
systemctl list-units --type=service --state=active

# Llistar serveis que han fallat
systemctl list-units --type=service --state=failed

# Veure totes les units
systemctl list-unit-files

Veure dependències

# Veure què requereix un servei
systemctl list-dependencies nginx

# Veure què depèn d'aquest servei
systemctl list-dependencies --reverse nginx

3. Crear un servei systemd

Estructura d'un fitxer .service

Els fitxers de servei es guarden a:

[Unit]
Description=Descripció del servei
Documentation=https://exemple.cat/docs
After=network.target
Requires=postgresql.service
Wants=redis.service

[Service]
Type=simple
User=usuari
Group=grup
WorkingDirectory=/opt/app
ExecStart=/opt/app/start.sh
ExecStop=/opt/app/stop.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
Environment="VAR1=valor1" "VAR2=valor2"
EnvironmentFile=/etc/app/config

[Install]
WantedBy=multi-user.target

Seccions principals

[Unit]

Metadades i dependències:

[Service]

Configuració del servei:

[Install]

Com instal·lar el servei:

Exemple 1: Script simple

#!/bin/bash
# /opt/monitor/monitor.sh

while true; do
    echo "[$(date)] Monitoritzant sistema..."
    df -h / >> /var/log/monitor.log
    free -h >> /var/log/monitor.log
    sleep 300  # 5 minuts
done

Fitxer de servei /etc/systemd/system/monitor.service:

[Unit]
Description=Monitor del sistema
After=network.target

[Service]
Type=simple
User=root
ExecStart=/opt/monitor/monitor.sh
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target

Activar:

# Fer executable l'script
chmod +x /opt/monitor/monitor.sh

# Recarregar systemd per veure el nou servei
sudo systemctl daemon-reload

# Iniciar i habilitar
sudo systemctl enable --now monitor.service

# Comprovar estat
systemctl status monitor.service

# Veure logs
journalctl -u monitor.service -f

Exemple 2: Aplicació web amb Node.js

// /opt/myapp/server.js
const http = require('http');
const server = http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World!');
});
server.listen(3000, () => {
    console.log('Servidor escoltant al port 3000');
});

Fitxer de servei /etc/systemd/system/myapp.service:

[Unit]
Description=My Node.js App
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

Environment="NODE_ENV=production"
Environment="PORT=3000"

[Install]
WantedBy=multi-user.target

Exemple 3: Script oneshot

Per scripts que s'executen una vegada i acaben:

#!/bin/bash
# /opt/scripts/cleanup.sh

echo "Netejant fitxers temporals..."
find /tmp -type f -mtime +7 -delete
echo "Neteja completada"

Fitxer de servei /etc/systemd/system/cleanup.service:

[Unit]
Description=Neteja de fitxers temporals

[Service]
Type=oneshot
ExecStart=/opt/scripts/cleanup.sh

Executar manualment:

sudo systemctl start cleanup.service

4. Logs amb journalctl

systemd usa journald per gestionar logs.

Comandes bàsiques

# Veure tots els logs
journalctl

# Logs en temps real (com tail -f)
journalctl -f

# Logs d'un servei específic
journalctl -u nginx

# En temps real d'un servei
journalctl -u nginx -f

# Últimes 100 línies
journalctl -u nginx -n 100

# Des d'un moment específic
journalctl -u nginx --since "2024-01-08 10:00:00"
journalctl -u nginx --since today
journalctl -u nginx --since yesterday
journalctl -u nginx --since "1 hour ago"

# Entre dates
journalctl --since "2024-01-08 00:00:00" --until "2024-01-08 23:59:59"

# Només errors
journalctl -u nginx -p err

# Prioritats:
# 0: emerg (emergència)
# 1: alert
# 2: crit (crític)
# 3: err (error)
# 4: warning
# 5: notice
# 6: info
# 7: debug

# Revertir ordre (més antics primer)
journalctl -r

# Format JSON
journalctl -u nginx -o json

# Veure espai ocupat pels logs
journalctl --disk-usage

# Netejar logs antics
sudo journalctl --vacuum-time=7d    # Mantenir només 7 dies
sudo journalctl --vacuum-size=100M  # Mantenir màxim 100MB

5. Timers: Tasques programades amb systemd

Els timers són una alternativa a cron, integrada amb systemd.

Crear un timer

Cal crear 2 fitxers:

  1. El servei (.service)
  2. El timer (.timer)

Exemple: Backup diari

/etc/systemd/system/backup.service:

[Unit]
Description=Backup diari de dades

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
StandardOutput=journal
StandardError=journal

/etc/systemd/system/backup.timer:

[Unit]
Description=Executar backup cada dia a les 2 AM

[Timer]
OnCalendar=daily
OnCalendar=02:00
Persistent=true

[Install]
WantedBy=timers.target

Opcions d'OnCalendar:

OnCalendar=hourly              # Cada hora
OnCalendar=daily               # Cada dia a les 00:00
OnCalendar=weekly              # Cada dilluns a les 00:00
OnCalendar=monthly             # Primer dia del mes a les 00:00
OnCalendar=*-*-* 02:00:00      # Cada dia a les 2 AM
OnCalendar=Mon-Fri 09:00       # De dilluns a divendres a les 9 AM
OnCalendar=*-*-01 00:00:00     # Primer dia de cada mes
OnCalendar=Sat *-*-* 00:00:00  # Cada dissabte

Altres opcions:

OnBootSec=5min              # 5 minuts després del boot
OnUnitActiveSec=1h          # 1 hora després de l'última execució
OnActiveSec=10s             # 10 segons després d'activar el timer

Activar el timer:

# Recarregar systemd
sudo systemctl daemon-reload

# Habilitar i iniciar el timer
sudo systemctl enable --now backup.timer

# Veure estat del timer
systemctl status backup.timer

# Llistar tots els timers
systemctl list-timers

# Veure quan s'executarà
systemctl list-timers --all

# Forçar execució immediata
sudo systemctl start backup.service

6. Interfícies gràfiques per scripts

zenity - Diàlegs gràfics simples

zenity permet crear diàlegs gràfics des de scripts bash.

Instal·lació

sudo apt install zenity

Exemples

Missatge informatiu:

zenity --info --text="Procés completat correctament!"

Pregunta Sí/No:

if zenity --question --text="Vols continuar?"; then
    echo "Usuari va dir SÍ"
else
    echo "Usuari va dir NO"
fi

Entrada de text:

NOM=$(zenity --entry --text="Introdueix el teu nom:")
echo "Hola, $NOM!"

Contrasenya:

PASS=$(zenity --password --title="Autenticació")

Selecció de fitxer:

FITXER=$(zenity --file-selection --title="Selecciona un fitxer")
echo "Has seleccionat: $FITXER"

Selecció de directori:

DIR=$(zenity --file-selection --directory --title="Selecciona un directori")

Llista de selecció:

OPCIO=$(zenity --list \
    --title="Selecciona una opció" \
    --column="ID" --column="Descripció" \
    1 "Opció 1" \
    2 "Opció 2" \
    3 "Opció 3")

echo "Has seleccionat l'opció $OPCIO"

Barra de progrés:

(
    echo "10" ; sleep 1
    echo "# Processant fitxers..." ; sleep 1
    echo "30" ; sleep 1
    echo "# Comprimint..." ; sleep 1
    echo "60" ; sleep 1
    echo "# Gairebé fet..." ; sleep 1
    echo "100"
) | zenity --progress \
    --title="Procés en curs" \
    --text="Iniciant..." \
    --percentage=0 \
    --auto-close

Barra de progrés pulsant:

find / -name "*.log" | zenity --progress \
    --pulsate \
    --text="Cercant fitxers log..." \
    --auto-close

Calendari:

DATA=$(zenity --calendar --title="Selecciona una data" --format="%Y-%m-%d")
echo "Data seleccionada: $DATA"

Formulari:

DADES=$(zenity --forms \
    --title="Formulari d'usuari" \
    --text="Introdueix les dades" \
    --add-entry="Nom" \
    --add-entry="Cognoms" \
    --add-entry="Email" \
    --add-calendar="Data de naixement")

echo "Dades: $DADES"

Error:

zenity --error --text="Ha ocorregut un error!"

Avís:

zenity --warning --text="Atenció: Aquesta acció no es pot desfer"

notify-send - Notificacions de desktop

notify-send mostra notificacions al sistema de notificacions del desktop.

Instal·lació

sudo apt install libnotify-bin

Exemples

Notificació simple:

notify-send "Títol" "Missatge de la notificació"

Amb urgència:

# Urgència baixa
notify-send -u low "Info" "Això és informació"

# Urgència normal (per defecte)
notify-send -u normal "Avís" "Això és un avís"

# Urgència crítica
notify-send -u critical "Error" "Això és un error!"

Amb icona:

notify-send -i dialog-information "Info" "Procés completat"
notify-send -i dialog-warning "Avís" "Atenció necessària"
notify-send -i dialog-error "Error" "Ha fallat el procés"

Amb timeout:

# Desapareix després de 5000 ms (5 segons)
notify-send -t 5000 "Missatge temporal" "Aquest missatge desapareixerà aviat"

Exemple pràctic: Monitor de disc

#!/bin/bash

LIMIT=80
US=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')

if [ $US -gt $LIMIT ]; then
    notify-send -u critical \
        -i dialog-warning \
        "Disc gairebé ple" \
        "El disc està al ${US}% de capacitat!"
fi

yad - Alternativa més potent a zenity

yad (Yet Another Dialog) és similar a zenity però amb més opcions.

sudo apt install yad

# Exemple amb múltiples botons
yad --text="Què vols fer?" \
    --button="Backup:0" \
    --button="Restaurar:1" \
    --button="Cancel·lar:2"

case $? in
    0) echo "Fent backup..." ;;
    1) echo "Restaurant..." ;;
    2) echo "Cancel·lat" ;;
esac

7. Exemple complet: Script amb GUI

#!/bin/bash

##############################################
# Script de backup amb interfície gràfica
##############################################

# Funcions per a diàlegs
mostrar_info() {
    zenity --info --title="Backup" --text="$1"
}

mostrar_error() {
    zenity --error --title="Error" --text="$1"
}

preguntar() {
    zenity --question --title="Confirmació" --text="$1"
}

# Menú principal
OPCIO=$(zenity --list \
    --title="Sistema de Backup" \
    --text="Selecciona una opció:" \
    --column="Acció" \
    "Fer backup" \
    "Restaurar backup" \
    "Veure backups" \
    "Configuració" \
    "Sortir")

case "$OPCIO" in
    "Fer backup")
        # Seleccionar directori
        ORIGEN=$(zenity --file-selection --directory \
            --title="Selecciona directori per fer backup")
        
        if [ -z "$ORIGEN" ]; then
            mostrar_error "No s'ha seleccionat cap directori"
            exit 1
        fi
        
        if preguntar "Fer backup de $ORIGEN?"; then
            # Fer backup amb barra de progrés
            (
                echo "20" ; echo "# Preparant..."
                sleep 1
                echo "40" ; echo "# Comprimint fitxers..."
                tar -czf backup.tar.gz "$ORIGEN" 2>&1
                echo "80" ; echo "# Verificant..."
                sleep 1
                echo "100" ; echo "# Completat!"
            ) | zenity --progress \
                --title="Creant backup" \
                --percentage=0 \
                --auto-close
            
            notify-send -i dialog-information \
                "Backup completat" \
                "S'ha creat correctament el backup"
            
            mostrar_info "Backup creat correctament!"
        fi
        ;;
    
    "Veure backups")
        BACKUPS=$(ls -lh backup_*.tar.gz 2>/dev/null | awk '{print $9"|"$5"|"$6" "$7}')
        
        if [ -z "$BACKUPS" ]; then
            mostrar_info "No hi ha backups disponibles"
        else
            echo "$BACKUPS" | zenity --list \
                --title="Backups disponibles" \
                --column="Fitxer" --column="Mida" --column="Data" \
                --width=600 --height=400
        fi
        ;;
    
    "Sortir")
        exit 0
        ;;
esac

Resum