đž Archived View for it.omarpolo.com âş articoli âş gmid-ricaricare-configurazione.gmi captured on 2021-11-30 at 20:18:30. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
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 $