From 7444bbe1f4d1e9858693bfcb41921fa601450a89 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 2 May 2003 10:05:52 +0000 Subject: [PATCH] Modifiche al server di echo per introdurre le stampe di debugging e la gestion dei processi figli (parziale). --- elemtcp.tex | 268 ++++++++++++++------- listati/ClientEcho.c | 2 +- listati/ElemEchoTCPServer.c | 52 ---- listati/PrintErr.c | 8 + listati/ServEcho.c | 13 +- listati/{TCP_echo_client.c => TCP_echo1.c} | 6 +- listati/TCP_echod.c | 60 +++++ listati/sigchildhand.c | 2 +- 8 files changed, 262 insertions(+), 149 deletions(-) delete mode 100644 listati/ElemEchoTCPServer.c create mode 100644 listati/PrintErr.c rename listati/{TCP_echo_client.c => TCP_echo1.c} (94%) create mode 100644 listati/TCP_echod.c diff --git a/elemtcp.tex b/elemtcp.tex index b021a78..534dd39 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -233,9 +233,9 @@ comporta ad esempio che se un processo viene terminato da un segnale tutte le connessioni aperte verranno chiuse. Infine occorre sottolineare che, benché nella figura (e nell'esempio che -vedremo più avanti in \secref{sec:TCPsimp_echo}) sia stato il client ad -eseguire la chiusura attiva, nella realtà questa può essere eseguita da uno -qualunque dei due capi della comunicazione (come nell'esempio di +vedremo più avanti in \secref{sec:TCP_echo}) sia stato il client ad eseguire +la chiusura attiva, nella realtà questa può essere eseguita da uno qualunque +dei due capi della comunicazione (come nell'esempio di \figref{fig:TCP_daytime_iter_server_code}), e anche se il caso più comune resta quello del client, ci sono alcuni servizi, il principale dei quali è l'HTTP, per i quali è il server ad effettuare la chiusura attiva. @@ -445,7 +445,7 @@ Quando un client contatta un server deve poter identificare con quale dei vari possibili server attivi intende parlare. Sia TCP che UDP definiscono un gruppo di \textsl{porte conosciute} (le cosiddette \textit{well-known port}) che identificano una serie di servizi noti (ad esempio la porta 22 identifica il -servizio \texttt{ssh}) effettuati da appositi server che rispondono alle +servizio \textit{ssh}) effettuati da appositi server che rispondono alle connessioni verso tali porte. D'altra parte un client non ha necessità di usare un numero di porta @@ -503,7 +503,7 @@ tabelle. I sistemi Unix hanno inoltre il concetto di \textsl{porte riservate} (che corrispondono alle porte con numero minore di 1024 e coincidono quindi con le porte conosciute). La loro caratteristica è che possono essere assegnate a un -socket solo da un processo con i privilegi di root, per far si che solo +socket solo da un processo con i privilegi di amministratore, per far si che solo l'amministratore possa allocare queste porte per far partire i relativi servizi. @@ -1591,7 +1591,7 @@ un po' pi le direzioni. Ci limiteremo a fornire una implementazione elementare, che usi solo le -funzioni di base viste finore, ma prenderemo in esame, oltre al comportamento +funzioni di base viste finora, ma prenderemo in esame, oltre al comportamento in condizioni normali, anche tutti i possibili scenari particolari (errori, sconnessione della rete, crash del client o del server durante la connessione) che possono avere luogo durante l'impiego di un'applicazione di rete, partendo @@ -1618,11 +1618,11 @@ da parte di un server che risponde fornendo altri dati in uscita. Il servizio \textit{echo} è uno dei servizi standard solitamente provvisti direttamente dal superserver \cmd{inetd}, ed è definito dall'\href{http://www.ietf.org/rfc/rfc0862.txt}{RFC~862}. Come dice il nome il -servizio deve rimandare indietro sulla connessione i dati che gli vengono -inviati; l'RFC descrive le specifiche sia per TCP che UDP, e per il primo -stabilisce che una volta stabilita la connessione ogni dato in ingresso deve -essere rimandato in uscita, fintanto che il chiamante non ha chiude la -connessione; il servizio opera sulla porta 7. +servizio deve riscrivere indietro sul socket i dati che gli vengono inviati in +ingresso. L'RFC descrive le specifiche del servizio sia per TCP che UDP, e per +il primo stabilisce che una volta stabilita la connessione ogni dato in +ingresso deve essere rimandato in uscita fintanto che il chiamante non ha +chiude la connessione. Al servizio è assegnata la porta riservata 7. Nel nostro caso l'esempio sarà costituito da un client che legge una linea di caratteri dallo standard input e la scrive sul server. A sua volta il server @@ -1634,8 +1634,8 @@ output. \subsection{Il client: prima versione} \label{sec:TCP_echo_client} -Il codice della prima versione client per il servizio \textit{echo}, -diponibile nel file \file{TCP_echo1.c}, è riportato in +Il codice della prima versione del client per il servizio \textit{echo}, +disponibile nel file \file{TCP\_echo1.c}, è riportato in \figref{fig:TCP_echo_client_1}. Esso ricalca la struttura del precedente client per il servizio \textit{daytime} (vedi \secref{sec:TCP_daytime_client}), e la prima parte (\texttt{\small 10--27}) è @@ -1644,7 +1644,7 @@ sostanzialmente identica, a parte l'uso di una porta diversa. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15.6 cm} - \includecodesample{listati/TCP_echo_client.c} + \includecodesample{listati/TCP_echo1.c} \end{minipage} \normalsize \caption{Codice della prima versione del client \textit{echo}.} @@ -1652,22 +1652,22 @@ sostanzialmente identica, a parte l'uso di una porta diversa. \end{figure} Al solito si è tralasciata la sezione relativa alla gestione delle opzioni a -riga di comando; una volta dichiarate le variabili, si prosegue -(\texttt{\small 10--13}) con della creazione del socket, con l'usuale -controllo degli errori, la preparazione (\texttt{\small 14--17}) della -struttura dell'indirizzo, che usa la porta 7 riservata al servizio -\textit{echo}, l'indirizzo specificato a riga di comando appositamente -convertito (\texttt{\small 18--22}). Una volta inizializzato l'indirizzo si si -può eseguire (\texttt{\small 23--27}) la connessione al server secondo la -stessa modalità usata in \secref{sec:TCP_daytime_client}. - -Completata la connessione, al ritorno di \func{connect}, per gestire il -funzionamento del protocollo si usa la funzione \code{ClientEcho}, il cui -codice si è riportato a parte in \figref{fig:TCPsimpl_client_echo_sub}. Questa -si preoccupa di gestire tutta la comunicazione, leggendo una riga alla volta -dallo standard input \file{stdin}, scrivendola sul socket e ristampando su -\file{stdout} quanto ricevuto in risposta dal server. Al ritorno dalla -funzione (\texttt{\small 30--31}) anche il programma termina. +riga di comando. Una volta dichiarate le variabili, si prosegue +(\texttt{\small 10--13}) con della creazione del socket con l'usuale controllo +degli errori, alla preparazione (\texttt{\small 14--17}) della struttura +dell'indirizzo, che stavolta usa la porta 7 riservata al servizio +\textit{echo}, infine si converte (\texttt{\small 18--22}) l'indirizzo +specificato a riga di comando. A questo punto (\texttt{\small 23--27}) si può +eseguire la connessione al server secondo la stessa modalità usata in +\secref{sec:TCP_daytime_client}. + +Completata la connessione, per gestire il funzionamento del protocollo si usa +la funzione \code{ClientEcho}, il cui codice si è riportato a parte in +\figref{fig:TCP_client_echo_sub}. Questa si preoccupa di gestire tutta la +comunicazione, leggendo una riga alla volta dallo standard input \file{stdin}, +scrivendola sul socket e ristampando su \file{stdout} quanto ricevuto in +risposta dal server. Al ritorno dalla funzione (\texttt{\small 30--31}) anche +il programma termina. La funzione \code{ClientEcho} utilizza due buffer (\texttt{\small 3}) per gestire i dati inviati e letti sul socket. La comunicazione viene gestita @@ -1692,58 +1692,138 @@ funzione \func{fputs} per scriverli su \file{stdout}. \normalsize \caption{Codice della prima versione della funzione \texttt{ClientEcho} per la gestione del servizio \textit{echo}.} - \label{fig:TCPsimpl_client_echo_sub} + \label{fig:TCP_client_echo_sub} \end{figure} -Quando si conluderà l'invio di dati mandando un end-of-file sullo standard -inputo (ad esempio con la pressione di \texttt{C-d}) si avrà il ritorno di -\func{fgets} con un puntatore nullo (si riveda quanto spiegato in -\secref{sec:file_line_io}) e la conseguente uscita dal ciclo; al che la -subroutine ritorna ed il nostro programma client termina. +Quando si concluderà l'invio di dati mandando un end-of-file sullo standard +input si avrà il ritorno di \func{fgets} con un puntatore nullo (si riveda +quanto spiegato in \secref{sec:file_line_io}) e la conseguente uscita dal +ciclo; al che la subroutine ritorna ed il nostro programma client termina. + +Si può effettuare una verifica del funzionamento del client abilitando il +servizio \textit{echo} nella configurazione di \cmd{initd} sulla propria +macchina ed usandolo direttamente verso di esso in locale, vedremo in +dettaglio più avanti (in \secref{sec:TCP_echo_startup}) il funzionamento del +programma, usato però con la nostra versione del server \textit{echo}, che +illustriamo immediatamente. \subsection{Il server: prima versione} \label{sec:TCPsimp_server_main} -La prima versione del server, \file{TCP_echod.c}, si compone di un -corpo principale, costituito dalla funzione \code{main}. Questa si incarica -di creare il socket, metterlo in ascolto di connessioni in arrivo e creare un -processo figlio a cui delegare la gestione di ciascuna connessione. Questa -parte, riportata in \figref{fig:TCPsimpl_serv_code}, è analoga a quella vista -nel precedente esempio esaminato in \secref{sec:TCP_daytime_cunc_server}. +La prima versione del server, contenuta nel file \file{TCP\_echod.c}, è +riportata in \figref{fig:TCP_echo_server_code}. Come abbiamo fatto per il +client anche il server è stato diviso in un corpo principale, costituito dalla +funzione \code{main}, che è molto simile a quello visto nel precedente esempio +per il server del servizio \textit{daytime} di +\secref{sec:TCP_daytime_cunc_server}, e da una funzione ausiliaria +\code{ServEcho} che si cura della gestione del servizio. -\begin{figure}[!htb] +\begin{figure}[!htbp] \footnotesize \centering \begin{minipage}[c]{15.6cm} - \includecodesample{listati/ElemEchoTCPServer.c} + \includecodesample{listati/TCP_echod.c} \end{minipage} \normalsize - \caption{Codice della funzione \code{main} della prima versione del server + \caption{Codice del corpo principale della prima versione del server per il servizio \textit{echo}.} - \label{fig:TCPsimpl_serv_code} + \label{fig:TCP_echo_server_code} \end{figure} -La struttura di questa prima versione del server è sostanzialmente identica a -quella dell'esempio citato, ed ad esso si applicano le considerazioni fatte in -\secref{sec:TCP_daytime_cunc_server}. Le uniche differenze rispetto -all'esempio in \figref{fig:TCP_daytime_cunc_server_code} sono che in questo -caso per il socket in ascolto viene usata la porta 7 e che tutta la gestione -della comunicazione è delegata alla funzione \code{ServEcho}. -% Per ogni connessione viene creato un -% processo figlio, il quale si incarica di lanciare la funzione -% \texttt{SockEcho}. - -Il codice della funzione \code{ServEcho} è invece mostrata in -\figref{fig:TCPsimpl_server_elem_sub}, la comunicazione viene gestita -all'interno del ciclo (linee \texttt{\small 6--8}). I dati inviati dal client -vengono letti dal socket con una semplice \func{read} (che ritorna solo in -presenza di dati in arrivo), la riscrittura viene invece gestita dalla -funzione \func{FullWrite} (descritta in \figref{fig:sock_FullWrite_code}) che -si incarica di tenere conto automaticamente della possibilità che non tutti i -dati di cui è richiesta la scrittura vengano trasmessi con una singola -\func{write}. +In questo caso però, rispetto a quanto visto nell'esempio di +\figref{fig:TCP_daytime_cunc_server_code} si è preferito scrivere il server +curando maggiormente alcuni dettagli, per tenere conto anche di alcune +esigenze generali (che non riguardano direttamente la rete), come la +possibilità di lanciare il server anche in modalità interattiva e la cessione +dei privilegi di amministratore non appena questi non sono più necessari. + +La sezione iniziale del programma (\texttt{\small 8--21}) è la stessa del +server di \secref{sec:TCP_daytime_cunc_server}, ed ivi descritta in dettaglio: +crea il socket, inizializza l'indirizzo e esegue \func{bind}; dato che +quest'ultima funzione viene usata su una porta riservata, il server dovrà +essere eseguito da un processo con i privilegi di amministratore, pena il +fallimento della chiamata. + +Una volta eseguita la funzione \func{bind} però i privilegi di amministratore +non sono più necessari, per questo è sempre opportuno rilasciarli, in modo da +evitare problemi in caso di eventuali vulnerabilità del server. Per questo +prima (\texttt{\small 22--26}) si esegue \func{setgid} per assegnare il +processo ad un gruppo senza privilegi,\footnote{si è usato il valore 65534, + ovvero -1 per il formato \ctyp{short}, che di norma in tutte le + distribuzioni viene usato per identificare il gruppo \texttt{nogroup} e + l'utente \texttt{nobody}, usati appunto per eseguire programmi che non + richiedono nessun privilegio particolare.} e poi si ripete (\texttt{\small + 27--30}) l'operazione usando \func{setuid} per cambiare anche +l'utente.\footnote{si tenga presente che l'ordine in cui si eseguono queste + due operazioni è importante, infatti solo avendo i privilegi di + amministratore si può cambiare il gruppo di un processo ad un'altro di cui + non si fa parte, per cui chiamare prima \func{setuid} farebbe fallire una + successiva chiamata a \func{setgid}. Inoltre si ricordi (si riveda quanto + esposto in \secref{sec:proc_perms}) che usando queste due funzioni il + rilascio dei privilegi è irreversibile.} Infine (\texttt{\small 30--36}), +qualora sia impostata la variabile \var{demonize}, prima (\texttt{\small 31}) +si apre il sistema di logging per la stampa degli errori, e poi +(\texttt{\small 32--35}) si invoca \func{daemon} per eseguire in background il +processo come demone. + +A questo punto il programma riprende di nuovo lo schema già visto usato dal +server per il servizio \textit{daytime}, con l'unica differenza della chiamata +alla funzione \code{PrintErr}, riportata in \figref{fig:TCP_PrintErr}, al +posto di \func{perror} per la stampa degli errori. + +Si inizia con il porre (\texttt{\small 37--41}) in ascolto il socket, e poi si +esegue indefinitamente il ciclo principale (\texttt{\small 42--58}). +All'interno di questo si ricevono (\texttt{\small 43--46}) le connessioni, +creando (\texttt{\small 47--50}) un processo figlio per ciascuna di esse. +Quest'ultimo (\texttt{\small 51--55}), chiuso (\texttt{\small 52}) il +\textit{listening socket}, esegue (\texttt{\small 53}) la funzione di gestione +del servizio \code{ServEcho}, ed al ritorno di questa (\texttt{\small 54}) +esce. + +Il padre invece si limita (\texttt{\small 56}) a chiudere il \textit{connected + socket} per ricominciare da capo il ciclo in attesa di nuove connessioni. In +questo modo si ha un server concorrente. La terminazione del padre non è +gestita esplicitamente, e deve essere effettuata inviando un segnale al +processo. + +Avendo trattato direttamente la gestione del programma come demone, si è +dovuto anche provvedere alla necessità di poter stampare eventuali messaggi di +errore attraverso il sistema del \textit{syslog} trattato in +\secref{sec:sess_daemon}. Come accennato questo è stato fatto utilizzando come +\textit{wrapper} la funzione \code{PrintErr}, il cui codice è riportato in +\figref{fig:TCP_PrintErr}. + +In essa ci si limita a controllare se è stato impostato (valore attivo per +default) l'uso come demone, nel qual caso (\texttt{\small 3}) si usa +\func{syslog} per stampare il messaggio di errore fornito come argomento sui +log di sistema. Se invece si è in modalità interattiva (attivabile con +l'opzione \texttt{-i}) si usa semplicemente la funzione \func{perror} per +stampare sullo standard error. + \begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/PrintErr.c} + \end{minipage} + \normalsize + \caption{Codice della funzione \code{PrintErr} per la + generalizzazione della stampa degli errori sullo standard input o + attraverso il \texttt{syslog}.} + \label{fig:TCP_PrintErr} +\end{figure} + +La gestione del servizio \textit{echo} viene effettuata interamente nella +funzione \code{ServEcho}, il cui codice è mostrato in +\figref{fig:TCP_ServEcho}, la comunicazione viene gestita all'interno del +ciclo (\texttt{\small 6--8}). I dati inviati dal client vengono letti dal +socket con una semplice \func{read} (che ritorna solo in presenza di dati in +arrivo), la riscrittura viene invece gestita dalla funzione \func{FullWrite} +(descritta in \figref{fig:sock_FullWrite_code}) che si incarica di tenere +conto automaticamente della possibilità che non tutti i dati di cui è +richiesta la scrittura vengano trasmessi con una singola \func{write}. + +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15.6cm} \includecodesample{listati/ServEcho.c} @@ -1751,19 +1831,23 @@ dati di cui \normalsize \caption{Codice della prima versione della funzione \code{ServEcho} per la gestione del servizio \textit{echo}.} - \label{fig:TCPsimpl_server_elem_sub} + \label{fig:TCP_ServEcho} \end{figure} -Quando il client chiude la connessione il ricevimento del FIN fa ritornare la -\func{read} con un numero di byte letti pari a zero, il che causa l'uscita -dal ciclo e il ritorno della funzione, che a sua volta causa la terminazione -del processo figlio. - +Nella funzione si è anche inserita la possibilità (\texttt{\small 7--14}) di +inviare dei messaggi di debug (abilitabile con l'uso dell'opzione \texttt{-d} +che imposta opportunamente \var{debugging}), gestendo entrambi i casi in cui +la stampa deve essere effettuata tramite \func{syslog} (\texttt{\small 11}) o +direttamente sullo standard output (\texttt{\small 13}). +Quando il client chiude la connessione il ricevimento del FIN fa ritornare la +\func{read} con un numero di byte letti pari a zero, il che causa l'uscita dal +ciclo e il ritorno della funzione, che a sua volta causa la terminazione del +processo figlio. \subsection{L'avvio e il funzionamento normale} -\label{sec:TCPsimpl_startup} +\label{sec:TCP_echo_startup} Benché il codice dell'esempio precedente sia molto ridotto, esso ci permetterà di considerare in dettaglio tutte le problematiche che si possono incontrare @@ -1840,7 +1924,7 @@ l'immediatamente stampa a video. \subsection{La conclusione normale} -\label{sec:TCPsimpl_conclusion} +\label{sec:TCP_echo_conclusion} Tutto quello che scriveremo sul client sarà rimandato indietro dal server e ristampato a video fintanto che non concluderemo l'immissione dei dati; una @@ -1887,12 +1971,11 @@ quando affronteremo il comportamento in caso di conclusioni anomale: di chiusura della connessione, viene emesso un FIN dal server che riceverà un ACK dal client, a questo punto la connessione è conclusa e il client resta nello stato \texttt{TIME\_WAIT}. - \end{enumerate} \subsection{La gestione dei processi figli} -\label{sec:TCPsimpl_child_hand} +\label{sec:TCP_child_hand} Tutto questo riguarda la connessione, c'è però da tenere conto dell'effetto del procedimento di chiusura del processo figlio nel server (si veda quanto @@ -1908,23 +1991,28 @@ ripetendo il comando \cmd{ps}: 2359 pts/0 Z 0:00 [echod ] \end{verbatim} -Poiché non è possibile lasciare processi zombie\index{zombie} che pur inattivi -occupano spazio nella tabella dei processi e a lungo andare saturerebbero le -risorse del kernel, occorrerà ricevere opportunamente lo stato di terminazione -del processo (si veda \secref{sec:proc_wait}), cosa che faremo utilizzando -\const{SIGCHLD} secondo quanto illustrato in \secref{sec:sig_sigchld}. - -La prima modifica al nostro server è pertanto quella di inserire la gestione -della terminazione dei processi figli attraverso l'uso di un gestore. -Per questo useremo la funzione \code{Signal}, illustrata in -\figref{fig:sig_Signal_code}, per installare il semplice gestore che -riceve i segnali dei processi figli terminati già visto in -\figref{fig:sig_sigchld_handl}; aggiungendo il seguente codice: +Dato che non è il caso di lasciare processi zombie\index{zombie}, occorrerà +ricevere opportunamente lo stato di terminazione del processo (si veda +\secref{sec:proc_wait}), cosa che faremo utilizzando \const{SIGCHLD} secondo +quanto illustrato in \secref{sec:sig_sigchld}. Una prima modifica al nostro +server è pertanto quella di inserire la gestione della terminazione dei +processi figli attraverso l'uso di un gestore. Per questo useremo la funzione +\code{Signal} della nostra libreria personale, che abbiamo illustrato in +\figref{fig:sig_Signal_code}, per installare il gestore che riceve i segnali +dei processi figli terminati già visto in \figref{fig:sig_sigchld_handl}. +Basterà allora aggiungere il seguente codice: \includecodesnip{listati/sigchildhand.c} \noindent -all'esempio illustrato in \figref{fig:TCPsimpl_serv_code}, e linkando il tutto -alla funzione \code{sigchld\_hand}, si risolverà completamente il problema -degli zombie\index{zombie}. +all'esempio illustrato in \figref{fig:TCP_echo_server_code}. + +In questo modo però si introduce un altro problema, + + + +\subsection{I vari scenari critici} +\label{sec:TCP_echo_critical} + + diff --git a/listati/ClientEcho.c b/listati/ClientEcho.c index 0a64e94..cff81bf 100644 --- a/listati/ClientEcho.c +++ b/listati/ClientEcho.c @@ -1,6 +1,6 @@ void ClientEcho(FILE * filein, int socket) { - char sendbuff[MAXLINE], recvbuff[MAXLINE]; + char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1]; int nread; while (fgets(sendbuff, MAXLINE, filein) != NULL) { FullWrite(socket, sendbuff, strlen(sendbuff)); diff --git a/listati/ElemEchoTCPServer.c b/listati/ElemEchoTCPServer.c deleted file mode 100644 index a434957..0000000 --- a/listati/ElemEchoTCPServer.c +++ /dev/null @@ -1,52 +0,0 @@ -/* Subroutines declaration */ -void ServEcho(int sockfd); -/* Program beginning */ -int main(int argc, char *argv[]) -{ - int list_fd, conn_fd; - pid_t pid; - struct sockaddr_in serv_add; - ... - /* create socket */ - if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("Socket creation error"); - exit(-1); - } - /* initialize address */ - memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */ - serv_add.sin_family = AF_INET; /* address type is INET */ - serv_add.sin_port = htons(13); /* daytime port is 13 */ - serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ - /* bind socket */ - if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { - perror("bind error"); - exit(-1); - } - /* listen on socket */ - if (listen(list_fd, BACKLOG) < 0 ) { - perror("listen error"); - exit(-1); - } - /* handle echo to client */ - while (1) { - /* accept connection */ - if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) { - perror("accept error"); - exit(-1); - } - /* fork to handle connection */ - if ( (pid = fork()) < 0 ){ - perror("fork error"); - exit(-1); - } - if (pid == 0) { /* child */ - close(list_fd); /* close listening socket */ - SockEcho(conn_fd); /* handle echo */ - exit(0); - } else { /* parent */ - close(conn_fd); /* close connected socket */ - } - } - /* normal exit, never reached */ - exit(0); -} diff --git a/listati/PrintErr.c b/listati/PrintErr.c new file mode 100644 index 0000000..e6577d4 --- /dev/null +++ b/listati/PrintErr.c @@ -0,0 +1,8 @@ +void PrintErr(char * error) { + if (demonize) { + syslog(LOG_ERR, error); + } else { + perror(error); + } + return; +} diff --git a/listati/ServEcho.c b/listati/ServEcho.c index 917c7c4..0ea0635 100644 --- a/listati/ServEcho.c +++ b/listati/ServEcho.c @@ -1,10 +1,19 @@ 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) { - nwrite = FullWrite(sockfd, buffer, nread); + if (debugging) { + snprintf(debug, MAXLINE+20, "Letti %d bytes, %s\n", nread, buffer); + debug[strlen(debug)] = 0; + if (demonize) { /* go daemon */ + syslog(LOG_DEBUG, debug); + } else { + fputs(debug, stdout); + } + } + nwrite = FullWrite(sockfd, buffer, nread); } return; } diff --git a/listati/TCP_echo_client.c b/listati/TCP_echo1.c similarity index 94% rename from listati/TCP_echo_client.c rename to listati/TCP_echo1.c index f9a67b6..d09d047 100644 --- a/listati/TCP_echo_client.c +++ b/listati/TCP_echo1.c @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) /* create socket */ if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket creation error"); - return -1; + return 1; } /* initialize address */ memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */ @@ -18,12 +18,12 @@ int main(int argc, char *argv[]) /* build address using inet_pton */ if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) { perror("Address creation error"); - return -1; + return 1; } /* extablish connection */ if (connect(sock_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { perror("Connection error"); - return -1; + return 1; } /* read daytime from server */ ClientEcho(stdin, sock_fd); diff --git a/listati/TCP_echod.c b/listati/TCP_echod.c new file mode 100644 index 0000000..81085a7 --- /dev/null +++ b/listati/TCP_echod.c @@ -0,0 +1,60 @@ +int main(int argc, char *argv[]) +{ + int list_fd, conn_fd; + pid_t pid; + struct sockaddr_in serv_add; + ... + /* create socket */ + if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket creation error"); + exit(1); + } + /* initialize address and bind socket */ + memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */ + serv_add.sin_family = AF_INET; /* address type is INET */ + serv_add.sin_port = htons(7); /* echo port is 7 */ + serv_add.sin_addr.s_addr = htonl(INADDR_ANY); /* connect from anywhere */ + if (bind(list_fd, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) { + perror("bind error"); + exit(1); + } + /* give away 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 ) { /* listen on socket */ + PrintErr("listen error"); + exit(1); + } + while (1) { /* handle echo to client */ + if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) { + PrintErr("accept error"); + exit(1); + } + if ( (pid = fork()) < 0 ) { /* fork to handle connection */ + PrintErr("fork error"); + exit(1); + } + if (pid == 0) { /* child */ + close(list_fd); /* close listening socket */ + ServEcho(conn_fd); /* handle echo */ + exit(0); + } else { /* parent */ + close(conn_fd); /* close connected socket */ + } + } + exit(0); /* normal exit, never reached */ +} diff --git a/listati/sigchildhand.c b/listati/sigchildhand.c index 8ded584..a6695a7 100644 --- a/listati/sigchildhand.c +++ b/listati/sigchildhand.c @@ -1,5 +1,5 @@ ... /* install SIGCHLD handler */ - Signal(SIGCHLD, sigchld_hand); /* establish handler */ + Signal(SIGCHLD, HandSigCHLD); /* establish handler */ /* create socket */ ... -- 2.30.2