3 * Copyright (C) 2003 Simone Piccardi
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.
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.
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.
19 /****************************************************************
22 * Elementary TCP server for echo service (port 7) using poll
24 * Author: Simone Piccardi
27 * Usage: echod -h give all info
29 ****************************************************************/
31 * Include needed headers
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 */
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 */
57 void PrintErr(char * error);
58 /* Program beginning */
59 int main(int argc, char *argv[])
62 * Variables definition
66 struct sockaddr_in s_addr, c_addr;
69 struct pollfd *poll_set;
71 int max_fd, nread, nwrite;
74 * Input section: decode parameters passed in the calling
77 opterr = 0; /* don't want writing to stderr */
78 while ( (i = getopt(argc, argv, "hdicw:n:")) != -1) {
84 printf("Wrong -h option use\n");
98 waiting = strtol(optarg, NULL, 10);
101 n = strtol(optarg, NULL, 10);
103 case '?': /* unrecognized options */
104 printf("Unrecognized options -%c\n",optopt);
106 default: /* should not reached */
110 /* ***********************************************************
112 * Options processing completed
114 * Main code beginning
116 * ***********************************************************/
117 /* Main code begin here */
118 if (compat) { /* install signal handler */
119 Signal(SIGCHLD, HandSigCHLD); /* non restarting handler */
121 SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */
124 if ( (list_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
125 perror("Socket creation error");
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 */
134 if (bind(list_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
135 perror("bind error");
138 /* release privileges and go daemon */
139 if (setgid(65534) !=0) { /* first give away group privileges */
140 perror("cannot give away group privileges");
143 if (setuid(65534) !=0) { /* and only after user ... */
144 perror("cannot give away user privileges");
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");
155 if (listen(list_fd, BACKLOG) < 0 ) {
156 PrintErr("listen error");
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++) {
165 poll_set[i].events = POLLRDNORM;
167 poll_set[max_fd].fd = list_fd;
168 /* main loop, wait for connection and data inside a select */
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");
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");
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);
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 */
201 PrintErr("Errore in lettura");
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 */
214 continue; /* continue loop on open */
216 nwrite = FullWrite(i, buffer, nread); /* write data */
218 PrintErr("Errore in scrittura");
224 /* normal exit, never reached */
228 * routine to print usage info and exit
231 printf("Elementary echo server\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");
243 * routine to print error on stout or syslog
245 void PrintErr(char * error) {
246 if (demonize) { /* daemon mode */
247 syslog(LOG_ERR, "%s: %m", error); /* log string and error message */