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}
\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}
--- /dev/null
+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);
+}
--- /dev/null
+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 */
+ ...
+}
--- /dev/null
+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;
+}
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
\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}
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:
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
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
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
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
\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
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)|)}
*/
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
errcode: ErrCode.c
$(CC) $(CFLAGJ) $^ -o $@
-echo: UDP_echo.c
+uecho: UDP_echo.c
$(CC) $(CFLAGJ) $(CFLAGS) $^ -o $@
techo: TCP_echo.c
* $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));
}
}
/* 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;
}
#include <stdio.h> /* include standard I/O library */
#include <errno.h> /* include error codes */
#include <string.h> /* include erroro strings definitions */
+#include <stdlib.h>
#include "Gapil.h"
#include "macros.h"
*/
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;
*/
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
case 'i':
demonize = 0;
break;
+ case 'k':
+ keepalive = 1;
+ break;
+ case 'r':
+ reuse = 1;
+ break;
case 'c':
compat = 1;
break;
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 */
}
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);
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");
--- /dev/null
+/* 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 <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <time.h>
+#include <syslog.h> /* syslog system functions */
+#include <signal.h> /* signal functions */
+#include <errno.h> /* error code */
+#include <string.h> /* error strings */
+#include <stdlib.h>
+
+#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;
+}
--- /dev/null
+/* 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 <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <time.h>
+#include <syslog.h> /* syslog system functions */
+#include <signal.h> /* signal functions */
+#include <errno.h> /* error code */
+#include <string.h> /* error strings */
+#include <stdlib.h>
+
+#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;
+}
#include <stdio.h> /* include standard I/O library */
#include <errno.h> /* include error codes */
#include <string.h> /* include erroro strings definitions */
+#include <stdlib.h>
#include "macros.h"
int list_fd, conn_fd;
int compat = 0;
int reroot = 0;
+ int reuse = 1;
char * rootdir;
pid_t pid;
struct sockaddr_in cli_add;
*/
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
case 'd':
debugging = 1;
break;
+ case 'w':
+ reuse = 0;
+ break;
case 'r':
reroot = 1;
rootdir = optarg;
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 */
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
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,
\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.