From: Simone Piccardi Date: Mon, 25 Apr 2005 20:42:09 +0000 (+0000) Subject: Risistemate le trattazioni delle opzioni SO_KEEPALIVE e SO_REUSEADDR, con X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=847a6cd39fe3d885ececc0658dfa5a4862534627 Risistemate le trattazioni delle opzioni SO_KEEPALIVE e SO_REUSEADDR, con l'inserimento di nuove opzioni nei server di esempio per usarle. Revisione degli indici e correzioni di alcuni errori relativi a funzioni non definnite o scritta con errori di battitura. Inseriti una serie di riferimenti. --- diff --git a/fileadv.tex b/fileadv.tex index 2f55a32..f4acfef 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -736,7 +736,7 @@ esaurimento. Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a disposizione un'altra operazione, quella di sincronizzazione dell'I/O, -compiuta dalla funzione \func{aio\_fsync}, che ha lo stesso effetto della +compiuta dalla funzione \funcd{aio\_fsync}, che ha lo stesso effetto della analoga \func{fsync}, ma viene eseguita in maniera asincrona; il suo prototipo è: \begin{prototype}{aio.h} @@ -946,7 +946,7 @@ prototipi sono: \end{errlist} ed inoltre \errval{EISDIR}, \errval{ENOMEM}, \errval{EFAULT} (se non sono stato allocati correttamente i buffer specificati nei campi - \func{iov\_base}), più tutti gli ulteriori errori che potrebbero avere le + \var{iov\_base}), più tutti gli ulteriori errori che potrebbero avere le usuali funzioni di lettura e scrittura eseguite su \param{fd}.} \end{functions} diff --git a/listati/TCP_echod_fifth.c b/listati/TCP_echod_fifth.c new file mode 100644 index 0000000..0977366 --- /dev/null +++ b/listati/TCP_echod_fifth.c @@ -0,0 +1,18 @@ +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int keepalive = 0; + int reuse = 0; + ... + /* create and bind socket */ + if ( (list_fd = sockbindopt(argv[optind], "echo", 6, + SOCK_STREAM, reuse)) < 0) { + return 1; + } + ... + /* normal exit, never reached */ + exit(0); +} diff --git a/listati/TCP_echod_fourth.c b/listati/TCP_echod_fourth.c new file mode 100644 index 0000000..5eb9d30 --- /dev/null +++ b/listati/TCP_echod_fourth.c @@ -0,0 +1,20 @@ +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int waiting = 0; + int keepalive = 0; + ... + ... + + if (pid == 0) { /* child */ + close(list_fd); /* close listening socket */ + if (keepalive) { /* enable keepalive ? */ + setsockopt(conn_fd, SOL_SOCKET, SO_KEEPALIVE, + &keepalive, sizeof(keepalive)); + } + ServEcho(conn_fd); /* handle echo */ + ... +} diff --git a/listati/sockbindopt.c b/listati/sockbindopt.c new file mode 100644 index 0000000..19a77c6 --- /dev/null +++ b/listati/sockbindopt.c @@ -0,0 +1,21 @@ +int sockbindopt(char *host, char *serv, int prot, int type, int reuse) +{ + struct addrinfo hint, *addr, *save; + int res; + int sock; + char buf[INET6_ADDRSTRLEN]; + ... + while (addr != NULL) { /* loop on possible addresses */ + /* get a socket */ + sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); + ... + /* connect the socket */ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + &reuse, sizeof(reuse))) { + printf("error on socket options\n"); + return -1; + } + ... + + return sock; +} diff --git a/session.tex b/session.tex index ae8322e..ec42332 100644 --- a/session.tex +++ b/session.tex @@ -549,7 +549,7 @@ o se la password non corrisponde\footnote{il confronto non viene effettuato volta criptato, ed è il risultato che viene confrontato con il valore che viene mantenuto nel database degli utenti.} la richiesta viene ripetuta un certo numero di volte dopo di che \cmd{login} esce ed \cmd{init} provvede a -rilanciare un'altra istanza di \func{getty}. +rilanciare un'altra istanza di \cmd{getty}. Se invece la password corrisponde \cmd{login} esegue \func{chdir} per settare la \textit{home directory} dell'utente, cambia i diritti di accesso al diff --git a/signal.tex b/signal.tex index df3727c..2972c9f 100644 --- a/signal.tex +++ b/signal.tex @@ -1240,7 +1240,7 @@ il processo non viene terminato direttamente dal gestore sia la stessa \func{abort} a farlo al ritorno dello stesso. Inoltre, sempre seguendo lo standard POSIX, prima della terminazione tutti i file aperti e gli stream saranno chiusi ed i buffer scaricati su disco. Non verranno invece eseguite le -eventuali funzioni registrate con \func{at\_exit} e \func{on\_exit}. +eventuali funzioni registrate con \func{atexit} e \func{on\_exit}. \subsection{Le funzioni di pausa e attesa} diff --git a/sockctrl.tex b/sockctrl.tex index 7e8a5b6..8ff56e3 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -2294,26 +2294,28 @@ permetterci di approfondire il significato di alcune di esse, che assumono grande importanza nella programmazione dei socket. Per questo motivo tratteremo ulteriormente l'uso di alcune di esse in questa sezione. -\index{\texttt{SO\_KEEPALIVE} (costante)|(} -La prima opzione da approfondire è \const{SO\_KEEPALIVE}, che come accennato -permette di controllare automaticamente lo stato di una connessione. Una -connessione infatti può restare attiva anche se non viene effettuato alcun -traffico su di essa, ma in certi casi però può essere utile controllarne lo -stato per accorgersi di eventuali problemi. - -Per questo, se si imposta questa opzione, è cura del kernel inviare degli -appositi messaggi sulla rete (detti appunto \textit{keep-alive}) per -verificare se la connessione è attiva. L'opzione funziona soltanto con socket -che supportino le connessioni (non ha senso per socket UDP ad esempio), ed -utilizza per \param{optval} un intero usato come valore logico. - -L'opzione si applica principalmente ai socket TCP. Con le impostazioni di -default (che sono riprese da BSD) Linux emette un messaggio di -\textit{keep-alive} verso l'altro capo della connessione se questa è rimasta -senza traffico per più di due ore. Se è tutto a posto il messaggio viene -ricevuto e verrà emesso un segmento ACK di risposta, alla cui ricezione -ripartirà un'altro ciclo di attesa per altre due ore di inattività; tutto ciò -viene effettuato dal kernel e le applicazioni non riceveranno nessun dato. + +\index{\texttt{SO\_KEEPALIVE} (costante)|(} +\subsubsection{L'opzione \const{SO\_KEEPALIVE}} + +La prima opzione da approfondire è \const{SO\_KEEPALIVE} che permette di +tenere sotto controllo lo stato di una connessione. Una connessione infatti +resta attiva anche quando non viene effettuato alcun traffico su di essa, per +cui un crollo della stessa potrebbe passare inosservato. + +Se si imposta questa opzione, è cura del kernel inviare degli appositi +messaggi sulla rete (detti appunto \textit{keep-alive}) per verificare se la +connessione è attiva. L'opzione funziona soltanto con socket che supportino +le connessioni (non ha senso per socket UDP ad esempio) e si applica +principalmente ai socket TCP. + +Con le impostazioni di default (che sono riprese da BSD) Linux emette un +messaggio di \textit{keep-alive} verso l'altro capo della connessione se +questa è rimasta senza traffico per più di due ore. Se è tutto a posto il +messaggio viene ricevuto e verrà emesso un segmento ACK di risposta, alla cui +ricezione ripartirà un'altro ciclo di attesa per altre due ore di inattività; +il tutto avviene all'interno del kernel e le applicazioni non riceveranno +nessun dato. In caso di problemi invece si possono avere i due casi già illustrati in sez.~\ref{sec:TCP_conn_crash} per il caso di terminazione prococe del server: @@ -2333,9 +2335,11 @@ secondi ad un massimo di 9 volte\footnote{entrambi questi valori possono di kernel ed i valori saranno applicati a \textsl{tutti} i socket.} (per un totale di 11 minuti e 15 secondi) dopo di che, se non si è ricevuta nessuna risposta, il socket viene chiuso dopo aver impostato un errore di -\errcode{ETIMEDOUT}. Se invece si riceve in risposta ad uno di questi messaggi -un pacchetto ICMP di destinazione irraggiungibile, verrà restituito l'errore -corrispondente. +\errcode{ETIMEDOUT}. Qualora la connessione si sia ristabilita e si riceva un +successivo messaggio di risposta il ciclo riparte come se niente fosse +avvenuto. Infine se invece si riceve come risposta un pacchetto ICMP di +destinazione irraggiungibile (vedi sez.~\ref{sec:icmp_protocol_xxx}), verrà +restituito l'errore corrispondente. In generale questa opzione serve per individuare una caduta della connessione,\footnote{il crash di un processo di nuovo comporta la chiusura di @@ -2349,28 +2353,74 @@ assoluta che un errore di \errcode{ETIMEDOUT} corrisponda ad una reale conclusione della connessione, il problema potrebbe essere dovuto ad un problema di routing che perduri per un tempo maggiore di quello impiegato nei vari tentativi di ritrasmissione del \textit{keep-alive}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echod_fourth.c} + \end{minipage} + \normalsize + \caption{La sezione della nuova versione del server del servizio + \textit{echo} che prevede l'attivazione del \textit{keepalive} sui + socket.} + \label{fig:echod_keepalive_code} +\end{figure} + +Come esempio dell'utilizzo di questa opzione introduciamo all'interno del +nostro server per il servizio \textit{echo} la nuova opzione \texttt{-k} che +permette di attivare il \textit{keep-alive} sui socket; tralasciando la parte +relativa alla gestione di detta opzione (che si limita ad assegnare ad 1 la +variabile \var{keepalive}) tutte le modifiche al server sono riportate in +fig.~\ref{fig:echod_keepalive_code}. Al solito il codice completo è contenuto +nel file \texttt{TCP\_echod\_fourth.c} dei sorgenti allegati alla guida. + +Come si può notare la variabile \var{keepalive} è preimpostata (\texttt{\small + 8}) ad un valore nullo; essa viene utilizzata sia come variabile logica per +la condizione (\texttt{\small 14}) che controlla l'attivazione del +\textit{keep-alive} che come valore dell'argomento \param{optval} della +chiamata a \func{setsockopt} (\texttt{\small 16}). A seconda del suo valore +tutte le volta che un processo figlio viene eseguito in risposta ad una +connessione verrà pertanto eseguita o meno la sezione (\texttt{\small 14--17}) +che esegue l'impostazione di \const{SO\_KEEPALIVE} sul socket connesso. + \index{\texttt{SO\_KEEPALIVE} (costante)|)} \index{\texttt{SO\_REUSEADDR} (costante)|(} -La seconda opzione da approfondire è \const{SO\_REUSEADDR}. Come Stevens -sottolinea in \cite{UNP1} si distinguono quattro casi per l'utilizzo di questa -opzione; il primo è quello in cui un server è terminato ma esistono ancora dei -processi figli che mantengono attiva almeno una connessione remota che -utilizza l'indirizzo locale. Quando si riavvia il server questo viene bloccato -sulla chiamata a \func{bind} dato che la porta è ancora utilizzata in una -connessione esistente.\footnote{questa è una delle domande più frequenti sui - newsgroup dedicati allo sviluppo, in quanto è piuttosto comune in questa - situazione quando si sta sviluppando un server che si ferma e si riavvia in - continuazione.} Inoltre se si usa il protocollo TCP questo può avvenire -anche dopo che l'ultimo processo figlio è terminato, dato che la connessione -può restare attiva anche dopo la chiusura del socket mantenendosi nello stato -\texttt{TIME\_WAIT}. +\subsubsection{L'opzione \const{SO\_REUSEADDR}} + +La seconda opzione da approfondire è \const{SO\_REUSEADDR}, che consente di +eseguire \func{bind} su un socket anche quando la porta specificata è già in +uso da parte di un altro socket. Si ricordi infatti che, come accennato in +sez.~\ref{sec:TCP_func_bind}, normalmente la funzione \func{bind} fallisce con +un errore di \errcode{EADDRINUSE} se la porta scelta è già utilizzata da un +altro socket, proprio per evitare che possano essere lanciati due server sullo +stesso indirizzo che verrebbero a contendersi i relativi pacchetti. + +Esistono però dei casi speciali in cui non si vuole che questo accada, ed +allora si può fare ricorso a questa opzione. La questione è comunque +abbastanza complessa (il che rende questa una delle opzioni piu difficili da +capire) in quanto, come sottolinea Stevens in \cite{UNP1}, si distinguono ben +quattro casi diversi in cui è prevista la possibilità di un suo utilizzo. + +Il primo ed il più comune caso in cui si fa ricorso a \const{SO\_REUSEADDR} è +quello in cui un server è terminato ma esistono ancora dei processi figli che +mantengono attiva almeno una connessione remota che utilizza l'indirizzo +locale mantenendo occupata la porta. Quando si riesegue il server allora +questo riceve un errore sulla chiamata a \func{bind} dato che la porta è +ancora utilizzata in una connessione esistente.\footnote{questa è una delle + domande più frequenti sui newsgroup dedicati allo sviluppo, in quanto è + piuttosto comune trovarsi in questa situazione quando si sta sviluppando un + server che si ferma e si riavvia in continuazione dopo aver fatto + modifiche.} Inoltre se si usa il protocollo TCP questo può avvenire anche +dopo tutti i processi figlio sono terminati, dato che una connessione può +restare attiva anche dopo la chiusura del socket, mantenendosi nello stato +\texttt{TIME\_WAIT} (vedi sez.~\ref{sec:TCP_time_wait}). Usando \const{SO\_REUSEADDR} fra la chiamata a \func{socket} e quella a \func{bind} si consente a quest'ultima di avere comunque successo anche se la connessione è attiva (o nello stato \texttt{TIME\_WAIT}). È bene però -ricordare (si riveda quanto detto in sez.~\ref{sec:TCP_time_wait}) che la +ricordare (si ricordi quanto detto in sez.~\ref{sec:TCP_time_wait}) che la presenza dello stato \texttt{TIME\_WAIT} ha una ragione, ed infatti se si usa questa opzione esiste sempre una probabilità, anche se estremamente remota,\footnote{perché ciò avvenga infatti non solo devono coincidere gli @@ -2379,14 +2429,71 @@ remota,\footnote{perch eventuali pacchetti rimasti intrappolati in una precedente connessione possano finire fra quelli di una nuova. -Il secondo caso in cui viene usata questa opzione è quando si ha una macchina -cui sono assegnati diversi numeri IP (o come suol dirsi \textit{multi-homed}) -e si vuole porre in ascolto sulla stessa porta un programma diverso (o una -istanza diversa dello stesso programma) per indirizzi IP diversi. Si ricordi -infatti che è sempre possibile indicare a \func{bind} di collegarsi solo su di -un indirizzo specifico; in tal caso se un altro programma cerca di -riutilizzare la stessa porta (anche specificando un indirizzo diverso) otterrà -un errore a meno di non aver preventivamente impostato \const{SO\_REUSEADDR}. +Come esempio di uso di questa connessione abbiamo predisposto una nuova +versione della funzione \func{sockbind} (vedi fig.~\ref{fig:sockbind_code}) +che consenta l'impostazione di questa opzione. La nuova funzione è +\func{sockbindopt}, e le principali differenze rispetto alla precedente sono +illustrate in fig.~\ref{fig:sockbindopt_code} dove si sono riportate le +sezioni di codice modificate rispetto ad essa. Il codice completo della +funzione si trova, insieme alle altre funzioni di servizio dei socket, +all'interno del file \texttt{SockUtils.c} dei sorgenti allegati alla guida. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/sockbindopt.c} + \end{minipage} + \normalsize + \caption{Le sezioni della funzione \func{sockbindopt} modificate rispetto al + codice della precedente \func{sockbind}.} + \label{fig:sockbindopt_code} +\end{figure} + +In realtà tutto quello che si è fatto è stato introdurre (\texttt{\small 1}) +un nuovo argomento intero \param{reuse} nella nuova funzione che conterrà il +valore logico da usare nella successiva chiamata (\texttt{\small 14}) a +\func{setsockopt}. Si è poi aggiunta la sezione (\texttt{\small 13-17}) che +esegue l'impostazione dell'opzione fra la chiamata a \func{socket} e quella a +\func{bind}. + + +A questo punto basterà modificare il server per utilizzare la nuova +funzione; in fig.~\ref{fig:TCP_echod_fifth} abbiamo riportato le sezioni +modificate rispetto alla precedente versione di +fig.~\ref{fig:TCP_echod_third}. Al solito il codice completo è coi sorgenti +allegati alla guida, nel file \texttt{TCP\_echod\_fifth.c}. + +Anche in questo caso si è introdotta (\texttt{\small 8}) una nuova variabile +\var{reuse} che consente di controllare l'uso dell'opzione e che poi sarà +usata (\texttt{\small 14}) come ultimo argomento di \func{setsockopt}. Il +valore di default di questa variabile è nullo, ma usando l'opzione \texttt{-r} +nell'invocazione del server (al solito la gestione delle opzioni non è +riportata in fig.~\ref{fig:TCP_echod_fifth}) se ne potrà impostare ad 1 il +valore, per cui in tal caso la successiva chiamata (\texttt{\small 13-17}) a +\func{setsockopt} attiverà l'opzione \const{SO\_REUSEADDR}. + + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echod_fifth.c} + \end{minipage} + \normalsize + \caption{Il nuovo codice per l'apertura passiva del server \textit{echo} che + usa la nuova funzione \func{sockbindopt}.} + \label{fig:TCP_echod_fifth} +\end{figure} + +Il secondo caso in cui viene usata \const{SO\_REUSEADDR} è quando si ha una +macchina cui sono assegnati diversi numeri IP (o come suol dirsi +\textit{multi-homed}) e si vuole porre in ascolto sulla stessa porta un +programma diverso (o una istanza diversa dello stesso programma) per indirizzi +IP diversi. Si ricordi infatti che è sempre possibile indicare a \func{bind} +di collegarsi solo su di un indirizzo specifico; in tal caso se un altro +programma cerca di riutilizzare la stessa porta (anche specificando un +indirizzo diverso) otterrà un errore, a meno di non aver preventivamente +impostato \const{SO\_REUSEADDR}. + Usando questa opzione diventa anche possibile eseguire \func{bind} sull'indirizzo generico, e questo permetterà il collegamento per tutti gli indirizzi (di quelli presenti) per i quali la porta non risulti occupata da @@ -2403,9 +2510,9 @@ il sistema non supporta l'opzione \const{IP\_RECVDSTADDR};\footnote{nel caso di Linux questa opzione è stata supportata per in certo periodo nello sviluppo del kernel 2.1.x, ma è in seguito stata soppiantata dall'uso di \const{IP\_PKTINFO} (vedi sez.~\ref{sec:sock_ipv4_options}).} in tale modo -si può sapere a quale socket corrisponde un certo indirizzo. Non ha senso per -socket TCP dato che su di essi si può sempre invocare \func{getsockname} una -volta che si è completata la connessione. +si può sapere a quale socket corrisponde un certo indirizzo. Non ha senso +fare questa operazionie per socket TCP dato che su di essi si può sempre +invocare \func{getsockname} una volta che si è completata la connessione. Infine il quarto caso è quello in si vuole effettivamente ottenere un \textit{completely duplicate binding}, quando cioè si vuole eseguire @@ -2451,13 +2558,15 @@ questa opzione.\footnote{Questa restrizione permette di evitare il cosiddetto \index{\texttt{SO\_REUSEADDR} (costante)|)} -\index{\texttt{SO\_LINGER} (costante)|(} La terza opzione da approfondire è -\const{SO\_LINGER}; essa, come il nome suggerisce, consente di -\textsl{indugiare} nella chiusura di un socket. Il comportamento standard sia -di \func{close} che \func{shutdown} è quello di terminare immediatamente dopo -la chiamata, mentre il procedimento di chiusura della connessione e l'invio -sulla rete di tutti i dati ancora presenti nei buffer viene gestito in -sottofondo dal kernel. +\index{\texttt{SO\_LINGER} (costante)|(} +\subsubsection{L'opzione \const{SO\_LINGER}} + +La terza opzione da approfondire è \const{SO\_LINGER}; essa, come il nome +suggerisce, consente di ``\textsl{indugiare}'' nella chiusura di un socket. Il +comportamento standard sia di \func{close} che \func{shutdown} è quello di +terminare immediatamente dopo la chiamata, mentre il procedimento di chiusura +della connessione e l'invio sulla rete di tutti i dati ancora presenti nei +buffer viene gestito in sottofondo dal kernel. \begin{figure}[!htb] \footnotesize \centering @@ -2487,6 +2596,8 @@ da zero sia \func{close} che \func{shutdown} si bloccano e non ritornano fintanto che non si sia concluso il procedimento di chiusura della connessione, o non siano passati il numero di secondi specificati da \var{l\_linger}. + + \index{\texttt{SO\_LINGER} (costante)|)} diff --git a/sources/Gapil.h b/sources/Gapil.h index e82e825..68fbc24 100644 --- a/sources/Gapil.h +++ b/sources/Gapil.h @@ -132,7 +132,7 @@ int RemoveShm(char * shm_name); */ int sockconn(char *host, char *serv, int prot, int type); int sockbind(char *host, char *serv, int prot, int type); -int sockbind2(char *host, char *serv, int prot, int type); +int sockbindopt(char *host, char *serv, int prot, int type, int reuse); /* * General purpose functions. See corresponding .c diff --git a/sources/Makefile b/sources/Makefile index f612cf2..edd6119 100644 --- a/sources/Makefile +++ b/sources/Makefile @@ -70,7 +70,7 @@ forktest: ForkTest.c errcode: ErrCode.c $(CC) $(CFLAGJ) $^ -o $@ -echo: UDP_echo.c +uecho: UDP_echo.c $(CC) $(CFLAGJ) $(CFLAGS) $^ -o $@ techo: TCP_echo.c diff --git a/sources/SockUtil.c b/sources/SockUtil.c index 28b22de..8b3fef2 100644 --- a/sources/SockUtil.c +++ b/sources/SockUtil.c @@ -205,12 +205,11 @@ int sockbind(char *host, char *serv, int prot, int type) * $Id$ * ****************************************************************/ -int sockbind2(char *host, char *serv, int prot, int type) +int sockbindopt(char *host, char *serv, int prot, int type, int reuse) { struct addrinfo hint, *addr, *save; int res; int sock; - int opt=1; char buf[INET6_ADDRSTRLEN]; /* initialize hint structure */ memset(&hint, 0, sizeof(struct addrinfo)); @@ -240,7 +239,8 @@ int sockbind2(char *host, char *serv, int prot, int type) } } /* connect the socket */ - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + &reuse, sizeof(reuse))) { printf("error on socket options\n"); return -1; } diff --git a/sources/TCP_echo.c b/sources/TCP_echo.c index f60c518..6b806ac 100644 --- a/sources/TCP_echo.c +++ b/sources/TCP_echo.c @@ -39,6 +39,7 @@ #include /* include standard I/O library */ #include /* include error codes */ #include /* include erroro strings definitions */ +#include #include "Gapil.h" #include "macros.h" diff --git a/sources/TCP_echod.c b/sources/TCP_echod.c index cca4933..ff866ef 100644 --- a/sources/TCP_echod.c +++ b/sources/TCP_echod.c @@ -62,6 +62,8 @@ int main(int argc, char *argv[]) */ int list_fd, conn_fd; int waiting = 0; + int keepalive = 0; + int reuse = 0; int compat = 0; pid_t pid; struct sockaddr_in cli_add; @@ -73,7 +75,7 @@ int main(int argc, char *argv[]) */ int i; opterr = 0; /* don't want writing to stderr */ - while ( (i = getopt(argc, argv, "hdicw:")) != -1) { + while ( (i = getopt(argc, argv, "hkrdicw:")) != -1) { switch (i) { /* * Handling options @@ -86,6 +88,12 @@ int main(int argc, char *argv[]) case 'i': demonize = 0; break; + case 'k': + keepalive = 1; + break; + case 'r': + reuse = 1; + break; case 'c': compat = 1; break; @@ -116,7 +124,8 @@ int main(int argc, char *argv[]) SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */ } /* create and bind socket */ - if ( (list_fd = sockbind(argv[optind], "echo", 6, SOCK_STREAM)) < 0) { + if ( (list_fd = sockbindopt(argv[optind], "echo", 6, + SOCK_STREAM, reuse)) < 0) { return 1; } /* release privileges and go daemon */ @@ -167,6 +176,10 @@ int main(int argc, char *argv[]) } if (pid == 0) { /* child */ close(list_fd); /* close listening socket */ + if (keepalive) { /* enable keepalive ? */ + setsockopt(conn_fd, SOL_SOCKET, SO_KEEPALIVE, + &keepalive, sizeof(keepalive)); + } ServEcho(conn_fd); /* handle echo */ if (debugging) { snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr); @@ -193,6 +206,8 @@ void usage(void) { printf(" echod [-h] \n"); printf(" -h print this help\n"); printf(" -d write debug info\n"); + printf(" -k enable SO_KEEPALIVE\n"); + printf(" -r enable SO_REUSEADDR\n"); printf(" -i use interactively\n"); printf(" -c disable BSD semantics\n"); printf(" -w N wait N sec. before calling accept\n"); diff --git a/sources/TCP_echod_fifth.c b/sources/TCP_echod_fifth.c new file mode 100644 index 0000000..ff866ef --- /dev/null +++ b/sources/TCP_echod_fifth.c @@ -0,0 +1,256 @@ +/* TCP_echod.c + * + * Copyright (C) 2001-2004 Simone Piccardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/**************************************************************** + * + * Program echod + * Elementary TCP server for echo service (port 7) + * + * Author: Simone Piccardi + * Jun. 2001 + * + * Usage: echod -h give all info + * + * $Id$ + * + ****************************************************************/ +/* + * Include needed headers + */ +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ +#include +#include /* syslog system functions */ +#include /* signal functions */ +#include /* error code */ +#include /* error strings */ +#include + +#include "Gapil.h" + +#define BACKLOG 10 +#define MAXLINE 256 +int demonize = 1; /* daemon use option: default is daemon */ +int debugging = 0; /* debug info printing option: default is no debug */ +/* Subroutines declaration */ +void usage(void); +void ServEcho(int sockfd); +void PrintErr(char * error); +/* Program beginning */ +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int waiting = 0; + int keepalive = 0; + int reuse = 0; + int compat = 0; + pid_t pid; + struct sockaddr_in cli_add; + socklen_t len; + char debug[MAXLINE], ipaddr[20]; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + int i; + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "hkrdicw:")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': + printf("Wrong -h option use\n"); + usage(); + return(0); + break; + case 'i': + demonize = 0; + break; + case 'k': + keepalive = 1; + break; + case 'r': + reuse = 1; + break; + case 'c': + compat = 1; + break; + case 'd': + debugging = 1; + break; + case 'w': + waiting = strtol(optarg, NULL, 10); + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + /* Main code begin here */ + if (compat) { /* install signal handler */ + Signal(SIGCHLD, HandSigCHLD); /* non restarting handler */ + } else { + SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */ + } + /* create and bind socket */ + if ( (list_fd = sockbindopt(argv[optind], "echo", 6, + SOCK_STREAM, reuse)) < 0) { + return 1; + } + /* release privileges and go daemon */ + if (setgid(65534) !=0) { /* first give away group privileges */ + perror("cannot give away group privileges"); + exit(1); + } + if (setuid(65534) !=0) { /* and only after user ... */ + perror("cannot give away user privileges"); + exit(1); + } + if (demonize) { /* go daemon */ + openlog(argv[0], 0, LOG_DAEMON); /* open logging */ + if (daemon(0, 0) != 0) { + perror("cannot start as daemon"); + exit(1); + } + } + /* main body */ + if (listen(list_fd, BACKLOG) < 0 ) { + PrintErr("listen error"); + exit(1); + } + if (waiting) sleep(waiting); + /* handle echo to client */ + while (1) { + /* accept connection */ + len = sizeof(cli_add); + while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) + < 0) && (errno == EINTR)); + if (conn_fd < 0) { + PrintErr("accept error"); + exit(1); + } + if (debugging) { + inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr)); + snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr); + if (demonize) { + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + /* fork to handle connection */ + if ( (pid = fork()) < 0 ){ + PrintErr("fork error"); + exit(1); + } + if (pid == 0) { /* child */ + close(list_fd); /* close listening socket */ + if (keepalive) { /* enable keepalive ? */ + setsockopt(conn_fd, SOL_SOCKET, SO_KEEPALIVE, + &keepalive, sizeof(keepalive)); + } + ServEcho(conn_fd); /* handle echo */ + if (debugging) { + snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr); + if (demonize) { + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + exit(0); + } else { /* parent */ + close(conn_fd); /* close connected socket */ + } + } + /* normal exit, never reached */ + exit(0); +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Elementary echo server\n"); + printf("Usage:\n"); + printf(" echod [-h] \n"); + printf(" -h print this help\n"); + printf(" -d write debug info\n"); + printf(" -k enable SO_KEEPALIVE\n"); + printf(" -r enable SO_REUSEADDR\n"); + printf(" -i use interactively\n"); + printf(" -c disable BSD semantics\n"); + printf(" -w N wait N sec. before calling accept\n"); + exit(1); +} +/* + * routine to handle echo for connection + */ +void ServEcho(int sockfd) { + char buffer[MAXLINE]; + int nread, nwrite; + char debug[MAXLINE+20]; + /* main loop, reading 0 char means client close connection */ + while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { + if (nread < 0) { + PrintErr("Errore in lettura"); + return; + } + nwrite = FullWrite(sockfd, buffer, nread); + if (nwrite) { + PrintErr("Errore in scrittura"); + return; + } + if (debugging) { + buffer[nread] = 0; + snprintf(debug, MAXLINE+20, "Letti %d byte, %s", nread, buffer); + if (demonize) { /* daemon mode */ + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + } + return; +} +/* + * routine to print error on stout or syslog + */ +void PrintErr(char * error) { + if (demonize) { /* daemon mode */ + syslog(LOG_ERR, "%s: %m", error); /* log string and error message */ + } else { + perror(error); + } + return; +} diff --git a/sources/TCP_echod_fourth.c b/sources/TCP_echod_fourth.c new file mode 100644 index 0000000..48d564c --- /dev/null +++ b/sources/TCP_echod_fourth.c @@ -0,0 +1,250 @@ +/* TCP_echod.c + * + * Copyright (C) 2001-2004 Simone Piccardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/**************************************************************** + * + * Program echod + * Elementary TCP server for echo service (port 7) + * + * Author: Simone Piccardi + * Jun. 2001 + * + * Usage: echod -h give all info + * + * $Id$ + * + ****************************************************************/ +/* + * Include needed headers + */ +#include /* predefined types */ +#include /* include unix standard library */ +#include /* IP addresses conversion utiliites */ +#include /* socket library */ +#include /* include standard I/O library */ +#include +#include /* syslog system functions */ +#include /* signal functions */ +#include /* error code */ +#include /* error strings */ +#include + +#include "Gapil.h" + +#define BACKLOG 10 +#define MAXLINE 256 +int demonize = 1; /* daemon use option: default is daemon */ +int debugging = 0; /* debug info printing option: default is no debug */ +/* Subroutines declaration */ +void usage(void); +void ServEcho(int sockfd); +void PrintErr(char * error); +/* Program beginning */ +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int waiting = 0; + int keepalive = 0; + int compat = 0; + pid_t pid; + struct sockaddr_in cli_add; + socklen_t len; + char debug[MAXLINE], ipaddr[20]; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + int i; + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "hkdicw:")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': + printf("Wrong -h option use\n"); + usage(); + return(0); + break; + case 'i': + demonize = 0; + break; + case 'k': + keepalive = 1; + break; + case 'c': + compat = 1; + break; + case 'd': + debugging = 1; + break; + case 'w': + waiting = strtol(optarg, NULL, 10); + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + /* Main code begin here */ + if (compat) { /* install signal handler */ + Signal(SIGCHLD, HandSigCHLD); /* non restarting handler */ + } else { + SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */ + } + /* create and bind socket */ + if ( (list_fd = sockbind(argv[optind], "echo", 6, SOCK_STREAM)) < 0) { + return 1; + } + /* release privileges and go daemon */ + if (setgid(65534) !=0) { /* first give away group privileges */ + perror("cannot give away group privileges"); + exit(1); + } + if (setuid(65534) !=0) { /* and only after user ... */ + perror("cannot give away user privileges"); + exit(1); + } + if (demonize) { /* go daemon */ + openlog(argv[0], 0, LOG_DAEMON); /* open logging */ + if (daemon(0, 0) != 0) { + perror("cannot start as daemon"); + exit(1); + } + } + /* main body */ + if (listen(list_fd, BACKLOG) < 0 ) { + PrintErr("listen error"); + exit(1); + } + if (waiting) sleep(waiting); + /* handle echo to client */ + while (1) { + /* accept connection */ + len = sizeof(cli_add); + while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) + < 0) && (errno == EINTR)); + if (conn_fd < 0) { + PrintErr("accept error"); + exit(1); + } + if (debugging) { + inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr)); + snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr); + if (demonize) { + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + /* fork to handle connection */ + if ( (pid = fork()) < 0 ){ + PrintErr("fork error"); + exit(1); + } + if (pid == 0) { /* child */ + close(list_fd); /* close listening socket */ + if (keepalive) { /* enable keepalive ? */ + setsockopt(conn_fd, SOL_SOCKET, SO_KEEPALIVE, + &keepalive, sizeof(keepalive)); + } + ServEcho(conn_fd); /* handle echo */ + if (debugging) { + snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr); + if (demonize) { + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + exit(0); + } else { /* parent */ + close(conn_fd); /* close connected socket */ + } + } + /* normal exit, never reached */ + exit(0); +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Elementary echo server\n"); + printf("Usage:\n"); + printf(" echod [-h] \n"); + printf(" -h print this help\n"); + printf(" -d write debug info\n"); + printf(" -k enable keepalive on connection\n"); + printf(" -i use interactively\n"); + printf(" -c disable BSD semantics\n"); + printf(" -w N wait N sec. before calling accept\n"); + exit(1); +} +/* + * routine to handle echo for connection + */ +void ServEcho(int sockfd) { + char buffer[MAXLINE]; + int nread, nwrite; + char debug[MAXLINE+20]; + /* main loop, reading 0 char means client close connection */ + while ( (nread = read(sockfd, buffer, MAXLINE)) != 0) { + if (nread < 0) { + PrintErr("Errore in lettura"); + return; + } + nwrite = FullWrite(sockfd, buffer, nread); + if (nwrite) { + PrintErr("Errore in scrittura"); + return; + } + if (debugging) { + buffer[nread] = 0; + snprintf(debug, MAXLINE+20, "Letti %d byte, %s", nread, buffer); + if (demonize) { /* daemon mode */ + syslog(LOG_DEBUG, debug); + } else { + printf("%s", debug); + } + } + } + return; +} +/* + * routine to print error on stout or syslog + */ +void PrintErr(char * error) { + if (demonize) { /* daemon mode */ + syslog(LOG_ERR, "%s: %m", error); /* log string and error message */ + } else { + perror(error); + } + return; +} diff --git a/sources/UDP_echo.c b/sources/UDP_echo.c index cc9e3ae..238effb 100644 --- a/sources/UDP_echo.c +++ b/sources/UDP_echo.c @@ -39,6 +39,7 @@ #include /* include standard I/O library */ #include /* include error codes */ #include /* include erroro strings definitions */ +#include #include "macros.h" diff --git a/sources/wwwd.c b/sources/wwwd.c index cb77526..1683888 100644 --- a/sources/wwwd.c +++ b/sources/wwwd.c @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) int list_fd, conn_fd; int compat = 0; int reroot = 0; + int reuse = 1; char * rootdir; pid_t pid; struct sockaddr_in cli_add; @@ -85,7 +86,7 @@ int main(int argc, char *argv[]) */ int i; opterr = 0; /* don't want writing to stderr */ - while ( (i = getopt(argc, argv, "hdicr:")) != -1) { + while ( (i = getopt(argc, argv, "hwdicr:")) != -1) { switch (i) { /* * Handling options @@ -104,6 +105,9 @@ int main(int argc, char *argv[]) case 'd': debugging = 1; break; + case 'w': + reuse = 0; + break; case 'r': reroot = 1; rootdir = optarg; @@ -129,7 +133,8 @@ int main(int argc, char *argv[]) SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */ } /* create and bind socket */ - if ( (list_fd = sockbind2(argv[optind], "www", 6, SOCK_STREAM)) < 0) { + if ( (list_fd = sockbindopt(argv[optind], "www", 6, + SOCK_STREAM, reuse)) < 0) { return 1; } /* chroot if requested */ diff --git a/system.tex b/system.tex index 1d66211..200aa94 100644 --- a/system.tex +++ b/system.tex @@ -2065,23 +2065,23 @@ l'ora locale o il tempo universale, a quelle per trasformare il valore di un tempo in una stringa contenente data ed ora, i loro prototipi sono: \begin{functions} \headdecl{time.h} - \funcdecl{char *asctime(const struct tm *tm)} + \funcdecl{char *\funcd{asctime}(const struct tm *tm)} Produce una stringa con data e ora partendo da un valore espresso in \textit{broken-down time}. - \funcdecl{char *ctime(const time\_t *timep)} + \funcdecl{char *\funcd{ctime}(const time\_t *timep)} Produce una stringa con data e ora partendo da un valore espresso in in formato \type{time\_t}. - \funcdecl{struct tm *gmtime(const time\_t *timep)} + \funcdecl{struct tm *\funcd{gmtime}(const time\_t *timep)} Converte il \textit{calendar time} dato in formato \type{time\_t} in un \textit{broken-down time} espresso in UTC. - \funcdecl{struct tm *localtime(const time\_t *timep)} + \funcdecl{struct tm *\funcd{localtime}(const time\_t *timep)} Converte il \textit{calendar time} dato in formato \type{time\_t} in un \textit{broken-down time} espresso nell'ora locale. - \funcdecl{time\_t mktime(struct tm *tm)} + \funcdecl{time\_t \funcd{mktime}(struct tm *tm)} Converte il \textit{broken-down time} in formato \type{time\_t}. \bodydesc{Tutte le funzioni restituiscono un puntatore al risultato in caso diff --git a/tcpsock.tex b/tcpsock.tex index 03fb502..78a6ce4 100644 --- a/tcpsock.tex +++ b/tcpsock.tex @@ -1600,7 +1600,7 @@ Inoltre nel caso sia stato abilitato il \textit{logging} delle connessioni, si provvede anche (\texttt{\small 40--43}) a stampare sullo standard output l'indirizzo e la porta da cui il client ha effettuato la connessione, usando i valori contenuti nelle strutture restituite da \func{accept}, eseguendo le -opportune conversioni con \func{inet\_ntop} e \func{atohs}. +opportune conversioni con \func{inet\_ntop} e \func{ntohs}. Ancora una volta l'esempio è estremamente semplificato, si noti come di nuovo non si sia gestita né la terminazione del processo né il suo uso come demone, @@ -2292,9 +2292,10 @@ attraverso la sequenza vista in sez.~\ref{sec:TCP_conn_term}, per cui la \func{accept} ritornerà senza errori, e si avrà semplicemente un end-of-file al primo accesso al socket. Nel caso di Linux inoltre, anche qualora si modifichi il client per fargli gestire l'invio di un segmento di RST alla -chiusura dal socket (come suggerito da Stevens in \cite{UNP1}), non si ha -nessun errore al ritorno di \funcd{accept}, quanto un errore di -\errcode{ECONNRESET} al primo tentativo di accesso al socket. +chiusura dal socket (usando l'opzione \const{SO\_LINGER}, vedi +sez.~\ref{sec:sock_options_main}), non si ha nessun errore al ritorno di +\func{accept}, quanto un errore di \errcode{ECONNRESET} al primo tentativo di +accesso al socket.