ALcune correzioni sparse, risistemati i commenti interni delle varie
[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  * $Id: poll_echod.c,v 1.3 2004/02/17 23:48:46 piccardi Exp $
30  *
31  ****************************************************************/
32 /* 
33  * Include needed headers
34  */
35 #define _XOPEN_SOURCE
36 #include <limits.h>      /* system limits */
37 #include <sys/types.h>   /* predefined types */
38 #include <unistd.h>      /* include unix standard library */
39 #include <arpa/inet.h>   /* IP addresses conversion utiliites */
40 #include <sys/socket.h>  /* socket library */
41 #include <stdio.h>       /* include standard I/O library */
42 #include <time.h>
43 #include <syslog.h>      /* syslog system functions */
44 #include <signal.h>      /* signal functions */
45 #include <errno.h>       /* error code */
46 #include <string.h>      /* error strings */
47 #include <stdlib.h>
48 #include <sys/poll.h>    /* poll function definition */
49
50 #include "macros.h"
51 #include "Gapil.h"
52
53 #define BACKLOG 10
54 #define MAXLINE 256
55 int demonize  = 1;  /* daemon use option: default is daemon */
56 int debugging = 0;  /* debug info printing option: default is no debug */
57 /* Subroutines declaration */
58 void usage(void);
59 void PrintErr(char * error);
60 /* Program beginning */
61 int main(int argc, char *argv[])
62 {
63 /* 
64  * Variables definition  
65  */
66     int waiting = 0;
67     int compat = 0;
68     struct sockaddr_in s_addr, c_addr;
69     socklen_t len;
70     char buffer[MAXLINE];
71     struct pollfd *poll_set;
72     int list_fd, fd;
73     int max_fd, nread, nwrite;
74     int i, n = 256;
75     /*
76      * Input section: decode parameters passed in the calling 
77      * Use getopt function
78      */
79     opterr = 0;  /* don't want writing to stderr */
80     while ( (i = getopt(argc, argv, "hdicw:n:")) != -1) {
81         switch (i) {
82         /* 
83          * Handling options 
84          */ 
85         case 'h':  
86             printf("Wrong -h option use\n");
87             usage();
88             return(0);
89             break;
90         case 'i':
91             demonize = 0;
92             break;
93         case 'c':
94             compat = 1;
95             break;
96         case 'd':
97             debugging = 1;
98             break;
99         case 'w':
100             waiting = strtol(optarg, NULL, 10);
101             break;
102         case 'n':
103             n = strtol(optarg, NULL, 10);
104             break;
105         case '?':   /* unrecognized options */
106             printf("Unrecognized options -%c\n",optopt);
107             usage();
108         default:    /* should not reached */
109             usage();
110         }
111     }
112     /* ***********************************************************
113      * 
114      *           Options processing completed
115      *
116      *                Main code beginning
117      * 
118      * ***********************************************************/
119     /* Main code begin here */
120     if (compat) {                             /* install signal handler */
121         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
122     } else {
123         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
124     }
125     /* create socket */
126     if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
127         perror("Socket creation error");
128         exit(1);
129     }
130     /* initialize address */
131     memset((void *)&s_addr, 0, sizeof(s_addr));   /* clear server address */
132     s_addr.sin_family = AF_INET;                  /* address type is INET */
133     s_addr.sin_port = htons(7);                   /* echo port is 7 */
134     s_addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* connect from anywhere */
135     /* bind socket */
136     if (bind(list_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
137         perror("bind error");
138         exit(1);
139     }
140     /* release privileges and go daemon */
141     if (setgid(65534) !=0) { /* first give away group privileges */
142         perror("cannot give away group privileges");
143         exit(1);
144     }
145     if (setuid(65534) !=0) { /* and only after user ... */
146         perror("cannot give away user privileges");
147         exit(1);
148     }
149     if (demonize) {          /* go daemon */
150         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
151         if (daemon(0, 0) != 0) {
152             perror("cannot start as daemon");
153             exit(1);
154         }
155     }
156     /* main body */
157     if (listen(list_fd, BACKLOG) < 0 ) {
158         PrintErr("listen error");
159         exit(1);
160     }
161     if (waiting) sleep(waiting);
162     /* initialize all needed variables */
163     poll_set = (struct pollfd *) malloc(n * sizeof(struct pollfd));
164     max_fd = list_fd;                 /* maximum now is listening socket */
165     for (i=0; i<n; i++) {
166         poll_set[i].fd = -1;
167         poll_set[i].events = POLLRDNORM;
168     }
169     poll_set[max_fd].fd = list_fd;
170     /* main loop, wait for connection and data inside a select */
171     while (1) {    
172         while ( ((n = poll(poll_set, max_fd + 1, -1)) < 0) 
173                 && (errno == EINTR));         /* wait for data or connection */
174         if (n < 0) {                          /* on real error exit */
175             PrintErr("poll error");
176             exit(1);
177         }
178         /* on activity */
179         debug("Trovati %d socket attivi\n", n);
180         if (poll_set[list_fd].revents & POLLRDNORM) {  /* if new connection */
181             n--;                              /* decrement active */
182             len = sizeof(c_addr);             /* and call accept */
183             if ((fd = accept(list_fd, (struct sockaddr *)&c_addr, &len)) < 0) {
184                 PrintErr("accept error");
185                 exit(1);
186             }
187             debug("Connessione su fd %d restano %d socket attivi\n", fd, n);
188             poll_set[fd].fd = fd;             /* set new connection socket */
189             if (max_fd < fd) max_fd = fd;     /* if needed set new maximum */
190             debug("max_fd=%d\n", max_fd);
191         }
192         /* loop on open connections */
193         i = list_fd;                  /* first socket to look */
194         while (n != 0) {              /* loop until active */
195             i++;                      /* start after listening socket */
196             debug("restano %d socket, fd %d\n", n, fd);
197             if (poll_set[i].fd == -1) continue;   /* closed, go next */
198             if (poll_set[i].revents & (POLLRDNORM|POLLERR)) { /* if active process it*/
199                 n--;                         /* decrease active */
200                 debug("dati su fd %d\n", i);
201                 nread = read(i, buffer, MAXLINE);     /* read operations */
202                 if (nread < 0) {
203                     PrintErr("Errore in lettura");
204                     exit(1);
205                 }
206                 if (nread == 0) {            /* if closed connection */
207                     debug("fd %d chiuso\n", i);
208                     close(i);                /* close file */
209                     poll_set[i].fd = -1;          /* mark as closed in table */
210                     if (max_fd == i) {       /* if was the maximum */
211                         while (poll_set[--i].fd == -1);    /* loop down */
212                         max_fd = i;          /* set new maximum */
213                         debug("nuovo max_fd %d\n", max_fd);
214                         break;               /* and go back to select */
215                     }
216                     continue;                /* continue loop on open */
217                 }
218                 nwrite = FullWrite(i, buffer, nread); /* write data */
219                 if (nwrite) {
220                     PrintErr("Errore in scrittura");
221                     exit(1);
222                 }
223             }
224         }
225     }
226     /* normal exit, never reached */
227     exit(0);
228 }
229 /*
230  * routine to print usage info and exit
231  */
232 void usage(void) {
233     printf("Elementary echo server\n");
234     printf("Usage:\n");
235     printf("  echod [-h] \n");
236     printf("  -h           print this help\n");
237     printf("  -d           write debug info\n");
238     printf("  -i           use interactively\n");
239     printf("  -c           disable BSD signal semantics\n");
240     printf("  -n N         set max contemporary connection\n");
241     printf("  -w N         wait N sec. before calling poll\n");
242     exit(1);
243 }
244 /*
245  * routine to print error on stout or syslog
246  */
247 void PrintErr(char * error) {
248     if (demonize) {                       /* daemon mode */
249         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
250     } else {
251         perror(error);
252     }
253     return;
254 }