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}).
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.
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
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}
\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}
\begin{table}[htb]
\centering
\footnotesize
- \begin{tabular}[c]{|l|p{10cm}|}
+ \begin{tabular}[c]{|l|p{11cm}|}
\hline
\textbf{Valore} & \textbf{Significato} \\
\hline
\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
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}
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}
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.
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
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
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
--- /dev/null
+/* 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 <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 <sys/mman.h>
+#include <semaphore.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;
+}
+
+
+#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);
+}
{
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");