💾 Archived View for it.omarpolo.com › articoli › gmid-ricaricare-configurazione.gmi captured on 2024-06-16 at 12:30:55. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-11-30)
-=-=-=-=-=-=-
Oggi gmid ha guadagnato una nuova funzionalità: ricaricare la configurazione al volo senza scollegare i client attualmente connessi.
Non me lo aspettavo, ma si è trattata di una funzionalità semplice da implementare!
gmid è attualmente composto da due processi: un processo di rete e uno per l’esecuzione degli script CGI. È bastato aggiungere un terzo processo supervisore che rimane in attesa di un SIGHUP e poi genera un’altra coppia di processi ‘listener’ ed ‘executor’.
Quando un ‘listener’ riceve un SIGHUP smette di accettare nuove connessioni e finisce di gestire quelle in corso, per poi terminare. La terminazione del ‘listener’ causa anche la terminazione dell’associato ‘executor’.
Inoltre, a meno che la nuova configurazione non cambi il numero di porta, il socket del server rimane aperto e passato alla nuova coppia di processi, quindi dall’esterno non è possibile notare nulla.
C’è una piccola fregatura però, ed è data dall’utilizzo dei segnali. Per accorgermi se un SIGHUP è stato ricevuto, ho usato una variabile globale che viene aggiornata nel gestore del segnale. Questo però introduce una possibile race-condition all’interno del programma, in quanto il segnale può arrivare in qualunque momento. La soluzione è stata ripassare le API per i segnali: è possibile infatti bloccare tutti i segnali e ri-abilitarli dopo la fork(2).
La scoperta di oggi infatti è stata sigprocmask e sigwait.
sigprocmask(2) permette di salvare l’attuale maschera dei segnali e impostarne una nuova in modo atomico. In questo modo è possibile bloccare, ad esempio, SIGHUP, come avviene in ‘block_signals’, e in un secondo momento riattivarli. I gestori dei segnali vengono infatti ereditati dopo una fork(2), i segnali in attesa no.
#include <signal.h> static sigset_t set; void block_signals(void) { sigset_t new; sigemptyset(&new); sigaddset(&new, SIGHUP); sigprocmask(SIG_BLOCK, &new, &set); } void unblock_signals(void) { sigprocmask(SIG_SETMASK, &set, NULL); }
Sto testando il comportamento di gmid in un ambiente ostile: ogni secondo il demone viene forzato a ricaricarsi e ci sono quattro client che continuano a richiedere l’esecuzione di uno script CGI che impiega cinque secondi a rispondere. Al momento è presente ancora una race-condition da qualche parte, dopo circa venti minuti un client e rimasto “bloccato”.
Il problema è che debuggare situazioni del genere con gdb è pressoché impossibile, in quanto non posso dirgli di mettere un breakpoint in un processo che ancora non esiste, ma in qualche modo si combina.
EDIT 1: altro che race-condition, si trattava di un errore stupidissimo! Invece di ricreare il socket solo se la porta veniva cambiata, a ogni SIGHUP ri-creavo un altro socket (uno stupidissimo != invece di ==). Adesso sta filando tutto liscio, sono a più di 10 minuti senza alcun errore. Ho aggiunto anche un quinto client che esegue:
$ while :; do ./gg gemini://localhost/; done
ad infinitum.
EDIT 2: dopo un’ora di test in queste condizioni senza nessun errore, penso di poter considerare finita questa parte!
$BlogIt: gmid-ricaricare-configurazione.gmi,v 1.1 2021/10/20 07:41:39 op Exp $