From e6ca5b03eaf1e9ef82eb75f722487efe97abaf88 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 3 Mar 2007 19:13:47 +0000 Subject: [PATCH] Correzioni varie, completati semafori POSIX e documentata {{{ppoll}}}. --- fileadv.tex | 105 +++++++++---- ipc.tex | 57 +++++-- sources/TCP_countd.c | 349 +++++++++++++++++++++++++++++++++++++++++++ sources/wwwd.c | 2 +- 4 files changed, 472 insertions(+), 41 deletions(-) create mode 100644 sources/TCP_countd.c diff --git a/fileadv.tex b/fileadv.tex index 6fdf67d..ea2eb4b 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -164,9 +164,14 @@ La funzione richiede di specificare tre insiemi distinti di file descriptor; il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di effettuare una lettura,\footnote{per essere precisi la funzione ritornerà in tutti i casi in cui la successiva esecuzione di \func{read} risulti non - bloccante, quindi anche in caso di \textit{end-of-file}.} il secondo, + bloccante, quindi anche in caso di \textit{end-of-file}; inoltre con Linux + possono verificarsi casi particolari, ad esempio quando arrivano dati su un + socket dalla rete che poi risultano corrotti e vengono scartati, può + accadere che \func{select} riporti il relativo file descriptor come + leggibile, ma una successiva \func{read} si blocchi.} il secondo, \param{writefds}, per verificare la possibilità effettuare una scrittura ed il -terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i dati +terzo, +\param{exceptfds}, per verificare l'esistenza di eccezioni (come i dati urgenti \itindex{out-of-band} su un socket, vedi sez.~\ref{sec:TCP_urgent_data}). @@ -263,9 +268,13 @@ precedenti, ed inoltre aggiunge a \func{select} una nuova funzione La funzione è sostanzialmente identica a \func{select}, solo che usa una struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timeval_struct}) per indicare con maggiore precisione il timeout e non ne aggiorna il valore in -caso di interruzione. Inoltre prende un argomento aggiuntivo \param{sigmask} -che è il puntatore ad una maschera di segnali (si veda -sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa +caso di interruzione.\footnote{in realtà la system call di Linux aggiorna il + valore al tempo rimanente, ma la funzione fornita dalle \acr{glibc} modifica + questo comportamento passando alla system call una variabile locale, in modo + da mantenere l'aderenza allo standard POSIX che richiede che il valore di + \param{timeout} non sia modificato.} Inoltre prende un argomento aggiuntivo +\param{sigmask} che è il puntatore ad una maschera di segnali (si veda +sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della funzione. @@ -293,28 +302,25 @@ interrotta, e la ricezione del segnale non sar Per questo è stata introdotta \func{pselect} che attraverso l'argomento \param{sigmask} permette di riabilitare la ricezione il segnale contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al - kernel 2.6.16, non è presente la relativa system call, e la funzione è + kernel 2.6.16, non era presente la relativa system call, e la funzione era implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man select\_tut}) per cui la possibilità di \itindex{race~condition} - \textit{race condition} permane; esiste però una soluzione, chiamata - \itindex{self-pipe trick} \textit{self-pipe trick}, che consiste nell'aprire - una pipe (vedi sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in - lettura della stessa, e indicare l'arrivo di un segnale scrivendo sul capo - in scrittura all'interno del gestore dello stesso; in questo modo anche se - il segnale va perso prima della chiamata di \func{select} questa lo - riconoscerà comunque dalla presenza di dati sulla pipe.} ribloccandolo non -appena essa ritorna, così che il precedente codice potrebbe essere riscritto -nel seguente modo: + \textit{race condition} permaneva; in tale situzione si può ricorrere ad una + soluzione alternativa, chiamata \itindex{self-pipe trick} \textit{self-pipe + trick}, che consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes}) + ed usare \func{select} sul capo in lettura della stessa; si può indicare + l'arrivo di un segnale scrivendo sul capo in scrittura all'interno del + gestore dello stesso; in questo modo anche se il segnale va perso prima + della chiamata di \func{select} questa lo riconoscerà comunque dalla + presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così +che il precedente codice potrebbe essere riscritto nel seguente modo: \includecodesnip{listati/pselect_norace.c} in questo caso utilizzando \var{oldmask} durante l'esecuzione di \func{pselect} la ricezione del segnale sarà abilitata, ed in caso di interruzione si potranno eseguire le relative operazioni. -% TODO pselect è stata introdotta nel kernel 2.6.16 (o 15 o 17?) insieme a -% ppoll mettere e verificare, vedi articolo LWN http://lwn.net/Articles/176750/ - -\subsection{La funzione \func{poll}} +\subsection{Le funzioni \func{poll} e \func{ppoll}} \label{sec:file_poll} Nello sviluppo di System V, invece di utilizzare l'interfaccia di @@ -438,14 +444,58 @@ valore nullo indica che si indica un errore nella chiamata, il cui codice viene riportato al solito tramite \var{errno}. +Abbiamo visto in sez.~\ref{sec:file_select} come lo standard POSIX preveda una +variante di \func{select} che consente di gestire correttamente la ricezione +dei segnali nell'attesa su un file descriptor. Con l'introduzione di una +implementazione reale di \func{pselect} nel kernel 2.6.16, è stata aggiunta +anche una analoga funzione che svolga lo stesso ruolo per \func{poll}. -% TODO accennare a ppoll +In questo caso si tratta di una estensione che è specifica di Linux e non è +prevista da nessuno standard; essa può essere utilizzata esclusivamente se si +definisce la macro \macro{\_GNU\_SOURCE} ed ovviamente non deve essere usata +se si ha a cuore la portabilità. La funzione è \funcd{ppoll}, ed il suo +prototipo è: +\begin{prototype}{sys/poll.h} + {int ppoll(struct pollfd *fds, nfds\_t nfds, const struct timespec *timeout, + const sigset\_t *sigmask)} + + La funzione attende un cambiamento di stato su un insieme di file + descriptor. + + \bodydesc{La funzione restituisce il numero di file descriptor con attività + in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore, + ed in quest'ultimo caso \var{errno} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + degli insiemi. + \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite + \macro{RLIMIT\_NOFILE}. + \end{errlist} + ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} +\end{prototype} + +La funzione ha lo stesso comportamento di \func{poll}, solo che si può +specificare, con l'argomento \param{sigmask}, il puntatore ad una maschera di +segnali; questa sarà la maschera utilizzata per tutto il tempo che la funzione +resterà in attesa, all'uscita viene ripristinata la maschera originale. L'uso +di questa funzione è cioè equivalente, come illustrato nella pagina di +manuale, all'esecuzione atomica del seguente codice: +\includecodesnip{listati/ppoll_means.c} + +Eccetto per \param{timeout}, che come per \func{pselect} deve essere un +puntatore ad una struttura \struct{timespec}, gli altri argomenti comuni con +\func{poll} hanno lo stesso significato, e la funzione restituisce gli stessi +risultati illustrati in precedenza. + + +% TODO accennare a ppoll vedi articolo LWN http://lwn.net/Articles/176750/ %\subsection{L'interfaccia di \textit{epoll}} %\label{sec:file_epoll} % placeholder ... -% TODO epoll +% TODO epoll \section{L'accesso \textsl{asincrono} ai file} \label{sec:file_asyncronous_access} @@ -1219,10 +1269,10 @@ cosiddetto \textit{memory-mapped I/O}, che, attraverso il meccanismo della \textsl{paginazione} \index{paginazione} usato dalla memoria virtuale (vedi sez.~\ref{sec:proc_mem_gen}), permette di \textsl{mappare} il contenuto di un file in una sezione dello spazio di indirizzi del processo. - + che lo ha allocato \begin{figure}[htb] \centering - \includegraphics[width=12cm]{img/mmap_layout} + \includegraphics[width=10cm]{img/mmap_layout} \caption{Disposizione della memoria di un processo quando si esegue la mappatura in memoria di un file.} \label{fig:file_mmap_layout} @@ -1355,7 +1405,7 @@ tab.~\ref{tab:file_mmap_flag}. \begin{table}[htb] \centering \footnotesize - \begin{tabular}[c]{|l|p{10cm}|} + \begin{tabular}[c]{|l|p{11cm}|} \hline \textbf{Valore} & \textbf{Significato} \\ \hline @@ -1447,8 +1497,11 @@ effettive del file o della sezione che si vuole mappare. \footnotetext[20]{Dato che tutti faranno riferimento alle stesse pagine di memoria.} -\footnotetext[21]{L'uso di questo flag con \const{MAP\_SHARED} è - stato implementato in Linux a partire dai kernel della serie 2.4.x.} + +\footnotetext[21]{L'uso di questo flag con \const{MAP\_SHARED} è stato + implementato in Linux a partire dai kernel della serie 2.4.x; esso consente + di creare segmenti di memoria condivisa e torneremo sul suo utilizzo in + sez.~\ref{sec:ipc_mmap_anonymous}.} \footnotetext{questo flag ed il precedente \const{MAP\_POPULATE} sono stati introdotti nel kernel 2.5.46 insieme alla mappatura non lineare di cui diff --git a/ipc.tex b/ipc.tex index 12cd3c0..f21ffe7 100644 --- a/ipc.tex +++ b/ipc.tex @@ -3234,6 +3234,7 @@ pi sez.~\ref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete. \itindend{memory~mapping} +% TODO fare esempio di mmap anonima \section{Il sistema di comunicazione fra processi di POSIX} \label{sec:ipc_posix} @@ -3768,16 +3769,16 @@ suoi contenuti in memoria,\footnote{il filesystem \texttt{tmpfs} per la memoria condivisa; esso infatti non ha dimensione fissa, ed usa direttamente la cache interna del kernel (che viene usata anche per la shared memory in stile SysV). In più i suoi contenuti, essendo trattati - direttamente dalla memoria virtuale\index{memoria~virtuale} possono essere + direttamente dalla memoria virtuale \index{memoria~virtuale} possono essere salvati sullo swap automaticamente.} che viene attivato abilitando l'opzione \texttt{CONFIG\_TMPFS} in fase di compilazione del kernel. -Per potere utilizzare l'interfaccia POSIX per le code di messaggi le +Per potere utilizzare l'interfaccia POSIX per la memoria condivisa le \acr{glibc}\footnote{le funzioni sono state introdotte con le glibc-2.2.} richiedono di compilare i programmi con l'opzione \code{-lrt}; inoltre è necessario che in \file{/dev/shm} sia montato un filesystem \texttt{tmpfs}; -questo di norma viene eseguita aggiungendo una riga tipo: +questo di norma viene fatto aggiungendo una riga del tipo di: \begin{verbatim} tmpfs /dev/shm tmpfs defaults 0 0 \end{verbatim} @@ -3845,7 +3846,7 @@ segmento di memoria condiviso con le stesse modalit il flag \const{FD\_CLOEXEC}. Chiamate effettuate da diversi processi usando lo stesso nome, restituiranno file descriptor associati allo stesso segmento (così come, nel caso di file di dati, essi sono associati allo stesso -\index{inode}inode). In questo modo è possibile effettuare una chiamata ad +\index{inode} inode). In questo modo è possibile effettuare una chiamata ad \func{mmap} sul file descriptor restituito da \func{shm\_open} ed i processi vedranno lo stesso segmento di memoria condivisa. @@ -3963,8 +3964,6 @@ Anche in questo caso questa interfaccia, oltre ad utilizzare gli opportuni file di definizione, occorrerà compilare i programmi con l'opzione \texttt{-lrt}. -% TODO trattare l'argomento a partire da man sem_overview. - La funzione che permette di creare un nuovo semaforo POSIX, creando il relativo file, o di accedere ad uno esistente, è \funcd{sem\_open}, questa prevede due forme diverse a seconda che sia utilizzata per aprire un semaforo @@ -4297,14 +4296,14 @@ variabile allocata dinamicamente nello \itindex{heap} heap. Qualora il semaforo debba essere condiviso fra più processi (nel qual caso si parla di \textit{process-shared semaphore}) la sola scelta possibile per -renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. In -tal caso occorrerà che tutti i processi abbiano un genitore comune che ha -allocato, con uno dei metodi possibili visti con \func{shm\_open} -(sez.~\ref{sec:ipc_posix_shm}), \func{mmap} (sez.~\ref{sec:file_memory_map}) o -\func{shmget} (sez.~\ref{sec:ipc_sysv_shm}) la memoria condivisa su cui si va -a creare il semaforo,\footnote{si ricordi che i tratti di memoria condivisa - vengono mantenuti nei processi figli attraverso la funzione \func{fork}.} a -cui essi poi potranno accedere. +renderlo visibile a tutti è di porlo in un tratto di memoria condivisa. Questo +potrà essere ottenuto direttamente sia con \func{shmget} (vedi +sez.~\ref{sec:ipc_sysv_shm}) che con \func{shm\_open} (vedi +sez.~\ref{sec:ipc_posix_shm}), oppure, nel caso che tutti i processi in gioco +abbiano un genitore comune, con una mappatura anonima con \func{mmap} (vedi +sez.~\ref{sec:file_memory_map}),\footnote{si ricordi che i tratti di memoria + condivisa vengono mantenuti nei processi figli attraverso la funzione + \func{fork}.} a cui essi poi potranno accedere. Una volta inizializzato il semaforo anonimo con \func{sem\_init} lo si potrà utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e @@ -4312,6 +4311,36 @@ utilizzare nello stesso modo dei semafori normali con \func{sem\_wait} e semaforo può dar luogo ad un comportamento indefinito. +Una volta che non si indenda più utilizzare un semaforo anonimo questo può +essere eliminato da sistema; per far questo di deve utilizzare una apposita +funzione, \funcd{sem\_destroy}, il cui prototipo è: +\begin{functions} + \headdecl{semaphore.h} + + \funcdecl{int sem\_destroy(sem\_t *sem)} + + Elimina il semaforo anonimo \param{sem}. + + \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di + errore; nel quel caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EINVAL}] il valore di \param{value} eccede + \const{SEM\_VALUE\_MAX}. + \end{errlist} +} +\end{functions} + +La funzione prende come unico argomento l'indirizzo di un semaforo che deve +essere stato inizializzato con \func{sem\_init}; non deve quindi essere +applicata a semafori creati con \func{sem\_open}. Inoltre si deve essere +sicuri che il semaforo sia effettivamente inutilizzato, la distruzione di un +semaforo su cui sono presenti processi (o thread) in attesa (cioè bloccati in +una \func{sem\_wait}) provoca un comportamento indefinito. + +Si tenga presente infine che utilizzare un semaforo che è stato distrutto con +\func{sem\_destroy} di nuovo può dare esito a comportamenti indefiniti. Nel +caso ci si trovi in una tale evenienza occorre reinizializzare il semaforo una +seconda volta con \func{sem\_init}. % LocalWords: like fifo System POSIX RPC Calls Common Object Request Brocker diff --git a/sources/TCP_countd.c b/sources/TCP_countd.c new file mode 100644 index 0000000..cf761e7 --- /dev/null +++ b/sources/TCP_countd.c @@ -0,0 +1,349 @@ +/* TCP_countd.c + * + * Copyright (C) 2007 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 countd: Elementary TCP server for teaching purpose, count + * number of request to the server itself + * + * Author: Simone Piccardi + * Feb. 2007 + * + * Usage: countdd -h give all info + * + ****************************************************************/ +/* + * 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 +#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; +} + + +#define MAXLINE 80 +#define BACKLOG 10 +/* Program begin */ +void usage(void); +int main(int argc, char *argv[]) +{ +/* + * Variables definition + */ + int list_fd, conn_fd; + int i; + struct sockaddr_in serv_add; + char buffer[MAXLINE]; + time_t timeval; + /* + * Input section: decode parameters passed in the calling + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "h")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': + printf("Wrong -h option use\n"); + usage(); + return(0); + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + /* 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); + } + /* write daytime to client */ + while (1) { + if ( (conn_fd = accept(list_fd, (struct sockaddr *) NULL, NULL)) <0 ) { + perror("accept error"); + exit(-1); + } + timeval = time(NULL); + snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&timeval)); + if ( (write(conn_fd, buffer, strlen(buffer))) < 0 ) { + perror("write error"); + exit(-1); + } + close(conn_fd); + } + + /* normal exit */ + exit(0); +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Simple daytime server\n"); + printf("Usage:\n"); + printf(" daytimed [-h] \n"); + printf(" -h print this help\n"); + exit(1); +} diff --git a/sources/wwwd.c b/sources/wwwd.c index 1683888..9524dd4 100644 --- a/sources/wwwd.c +++ b/sources/wwwd.c @@ -219,7 +219,7 @@ void usage(void) { printf("Elementary echo server\n"); printf("Usage:\n"); - printf(" echod [-h] \n"); + printf(" wwwd [-h] \n"); printf(" -h print this help\n"); printf(" -d write debug info\n"); printf(" -i use interactively\n"); -- 2.30.2