Varie correzioni, completata revisione capitolo sull'I/O su file
[gapil.git] / sources / poll_echod.c
1 /* poll_echod.c
2  * 
3  * Copyright (C) 2003 Simone Piccardi
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /****************************************************************
20  *
21  * Program poll_echod 
22  * Elementary TCP server for echo service (port 7) using poll
23  *
24  * Author: Simone Piccardi
25  * Dec. 2003
26  *
27  * Usage: echod -h give all info
28  *
29  ****************************************************************/
30 /* 
31  * Include needed headers
32  */
33 #define _XOPEN_SOURCE
34 #include <limits.h>      /* system limits constants, types and functions */
35 #include <sys/types.h>   /* primitive system data types */
36 #include <unistd.h>      /* unix standard library */
37 #include <arpa/inet.h>   /* IP addresses conversion utilities */
38 #include <sys/socket.h>  /* socket constants, types and functions */
39 #include <stdio.h>       /* standard I/O library */
40 #include <time.h>        /* date and time constants, types and functions */
41 #include <syslog.h>      /* syslog system functions */
42 #include <signal.h>      /* signal constants, types and functions */
43 #include <errno.h>       /* error definitions and routines */
44 #include <string.h>      /* C strings library */
45 #include <stdlib.h>      /* C standard library */
46 #include <sys/poll.h>    /* poll syscall */
47
48 #include "macros.h"
49 #include "Gapil.h"
50
51 #define BACKLOG 10
52 #define MAXLINE 256
53 int demonize  = 1;  /* daemon use option: default is daemon */
54 int debugging = 0;  /* debug info printing option: default is no debug */
55 /* Subroutines declaration */
56 void usage(void);
57 void PrintErr(char * error);
58 /* Program beginning */
59 int main(int argc, char *argv[])
60 {
61 /* 
62  * Variables definition  
63  */
64     int waiting = 0;
65     int compat = 0;
66     struct sockaddr_in s_addr, c_addr;
67     socklen_t len;
68     char buffer[MAXLINE];
69     struct pollfd *poll_set;
70     int list_fd, fd;
71     int max_fd, nread, nwrite;
72     int i, n = 256;
73     /*
74      * Input section: decode parameters passed in the calling 
75      * Use getopt function
76      */
77     opterr = 0;  /* don't want writing to stderr */
78     while ( (i = getopt(argc, argv, "hdicw:n:")) != -1) {
79         switch (i) {
80         /* 
81          * Handling options 
82          */ 
83         case 'h':  
84             printf("Wrong -h option use\n");
85             usage();
86             return(0);
87             break;
88         case 'i':
89             demonize = 0;
90             break;
91         case 'c':
92             compat = 1;
93             break;
94         case 'd':
95             debugging = 1;
96             break;
97         case 'w':
98             waiting = strtol(optarg, NULL, 10);
99             break;
100         case 'n':
101             n = strtol(optarg, NULL, 10);
102             break;
103         case '?':   /* unrecognized options */
104             printf("Unrecognized options -%c\n",optopt);
105             usage();
106         default:    /* should not reached */
107             usage();
108         }
109     }
110     /* ***********************************************************
111      * 
112      *           Options processing completed
113      *
114      *                Main code beginning
115      * 
116      * ***********************************************************/
117     /* Main code begin here */
118     if (compat) {                             /* install signal handler */
119         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
120     } else {
121         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
122     }
123     /* create socket */
124     if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
125         perror("Socket creation error");
126         exit(1);
127     }
128     /* initialize address */
129     memset((void *)&s_addr, 0, sizeof(s_addr));   /* clear server address */
130     s_addr.sin_family = AF_INET;                  /* address type is INET */
131     s_addr.sin_port = htons(7);                   /* echo port is 7 */
132     s_addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* connect from anywhere */
133     /* bind socket */
134     if (bind(list_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
135         perror("bind error");
136         exit(1);
137     }
138     /* release privileges and go daemon */
139     if (setgid(65534) !=0) { /* first give away group privileges */
140         perror("cannot give away group privileges");
141         exit(1);
142     }
143     if (setuid(65534) !=0) { /* and only after user ... */
144         perror("cannot give away user privileges");
145         exit(1);
146     }
147     if (demonize) {          /* go daemon */
148         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
149         if (daemon(0, 0) != 0) {
150             perror("cannot start as daemon");
151             exit(1);
152         }
153     }
154     /* main body */
155     if (listen(list_fd, BACKLOG) < 0 ) {
156         PrintErr("listen error");
157         exit(1);
158     }
159     if (waiting) sleep(waiting);
160     /* initialize all needed variables */
161     poll_set = (struct pollfd *) malloc(n * sizeof(struct pollfd));
162     max_fd = list_fd;                 /* maximum now is listening socket */
163     for (i=0; i<n; i++) {
164         poll_set[i].fd = -1;
165         poll_set[i].events = POLLRDNORM;
166     }
167     poll_set[max_fd].fd = list_fd;
168     /* main loop, wait for connection and data inside a select */
169     while (1) {    
170         while ( ((n = poll(poll_set, max_fd + 1, -1)) < 0) 
171                 && (errno == EINTR));         /* wait for data or connection */
172         if (n < 0) {                          /* on real error exit */
173             PrintErr("poll error");
174             exit(1);
175         }
176         /* on activity */
177         debug("Trovati %d socket attivi\n", n);
178         if (poll_set[list_fd].revents & POLLRDNORM) {  /* if new connection */
179             n--;                              /* decrement active */
180             len = sizeof(c_addr);             /* and call accept */
181             if ((fd = accept(list_fd, (struct sockaddr *)&c_addr, &len)) < 0) {
182                 PrintErr("accept error");
183                 exit(1);
184             }
185             debug("Connessione su fd %d restano %d socket attivi\n", fd, n);
186             poll_set[fd].fd = fd;             /* set new connection socket */
187             if (max_fd < fd) max_fd = fd;     /* if needed set new maximum */
188             debug("max_fd=%d\n", max_fd);
189         }
190         /* loop on open connections */
191         i = list_fd;                  /* first socket to look */
192         while (n != 0) {              /* loop until active */
193             i++;                      /* start after listening socket */
194             debug("restano %d socket, fd %d\n", n, fd);
195             if (poll_set[i].fd == -1) continue;   /* closed, go next */
196             if (poll_set[i].revents & (POLLRDNORM|POLLERR)) { /* if active process it*/
197                 n--;                         /* decrease active */
198                 debug("dati su fd %d\n", i);
199                 nread = read(i, buffer, MAXLINE);     /* read operations */
200                 if (nread < 0) {
201                     PrintErr("Errore in lettura");
202                     exit(1);
203                 }
204                 if (nread == 0) {            /* if closed connection */
205                     debug("fd %d chiuso\n", i);
206                     close(i);                /* close file */
207                     poll_set[i].fd = -1;          /* mark as closed in table */
208                     if (max_fd == i) {       /* if was the maximum */
209                         while (poll_set[--i].fd == -1);    /* loop down */
210                         max_fd = i;          /* set new maximum */
211                         debug("nuovo max_fd %d\n", max_fd);
212                         break;               /* and go back to select */
213                     }
214                     continue;                /* continue loop on open */
215                 }
216                 nwrite = FullWrite(i, buffer, nread); /* write data */
217                 if (nwrite) {
218                     PrintErr("Errore in scrittura");
219                     exit(1);
220                 }
221             }
222         }
223     }
224     /* normal exit, never reached */
225     exit(0);
226 }
227 /*
228  * routine to print usage info and exit
229  */
230 void usage(void) {
231     printf("Elementary echo server\n");
232     printf("Usage:\n");
233     printf("  echod [-h] \n");
234     printf("  -h           print this help\n");
235     printf("  -d           write debug info\n");
236     printf("  -i           use interactively\n");
237     printf("  -c           disable BSD signal semantics\n");
238     printf("  -n N         set max contemporary connection\n");
239     printf("  -w N         wait N sec. before calling poll\n");
240     exit(1);
241 }
242 /*
243  * routine to print error on stout or syslog
244  */
245 void PrintErr(char * error) {
246     if (demonize) {                       /* daemon mode */
247         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
248     } else {
249         perror(error);
250     }
251     return;
252 }