💾 Archived View for it.omarpolo.com › articoli › gmid-ricaricare-configurazione.gmi captured on 2022-01-08 at 13:51:27. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-11-30)

-=-=-=-=-=-=-

Ricaricare la configurazione

Oggi gmid ha guadagnato una nuova funzionalità: ricaricare la configurazione al volo senza scollegare i client attualmente connessi.

gmid, un server Gemini

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)

sigwait(2)

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 $