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