Tolta opzione inutile dal web server e messa opzione per far andare bene
[gapil.git] / sources / wwwd.c
1 /* wwwd.c
2  * 
3  * Copyright (C) 2005 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 wwwd 
22  * Elementary WWW server (port 80)
23  *
24  * Author: Simone Piccardi
25  * Mar. 2005
26  *
27  * Usage: wwwd -h give all info
28  *
29  * $Id$ 
30  *
31  ****************************************************************/
32 /* 
33  * Include needed headers
34  */
35 #define _GNU_SOURCE
36 #include <string.h>      /* error strings */
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 <stdlib.h>
47
48 #include "Gapil.h"
49
50 #define BACKLOG 10
51 #define MAXLINE 256
52 int demonize  = 1;  /* daemon use option: default is daemon */
53 int debugging = 0;  /* debug info printing option: default is no debug */
54
55 /* Subroutines declaration */
56 void usage(void);
57 void ServPage(int sockfd);
58 void PrintErr(char * error);
59 void print_headers(FILE *file);
60 void print_err404(FILE *file, char *filename);
61 void print_err400(FILE *file, char *string);
62 void print_err500(FILE *file, char *string);
63
64 /* Program beginning */
65 int main(int argc, char *argv[])
66 {
67 /* 
68  * Variables definition  
69  */
70     int list_fd, conn_fd;
71     int compat = 0;
72     int reroot = 0;
73     char * rootdir;
74     pid_t pid;
75     struct sockaddr_in cli_add;
76     socklen_t len;
77     char debug[MAXLINE], ipaddr[20];
78     /*
79      * Input section: decode parameters passed in the calling 
80      * Use getopt function
81      */
82     int i;
83     opterr = 0;  /* don't want writing to stderr */
84     while ( (i = getopt(argc, argv, "hdicw:r:")) != -1) {
85         switch (i) {
86         /* 
87          * Handling options 
88          */ 
89         case 'h':  
90             printf("Wrong -h option use\n");
91             usage();
92             return(0);
93             break;
94         case 'i':
95             demonize = 0;
96             break;
97         case 'c':
98             compat = 1;
99             break;
100         case 'd':
101             debugging = 1;
102             break;
103         case 'r':
104             reroot = 1;
105             rootdir = optarg;
106             break;
107         case '?':   /* unrecognized options */
108             printf("Unrecognized options -%c\n",optopt);
109             usage();
110         default:    /* should not reached */
111             usage();
112         }
113     }
114     /* ***********************************************************
115      * 
116      *           Options processing completed
117      *
118      *                Main code beginning
119      * 
120      * ***********************************************************/
121     /* Main code begin here */
122     if (compat) {                             /* install signal handler */
123         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
124     } else {
125         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
126     }
127     /* create and bind socket */
128     if ( (list_fd = sockbind2(argv[optind], "www", 6, SOCK_STREAM)) < 0) {
129         return 1;
130     }   
131     /* chroot if requested */
132     if (reroot) {
133         if (chdir(rootdir)) {
134             perror("Cannot find directory to chroot");
135             exit(1);
136         }
137         if (chroot(rootdir)) {
138             perror("Cannot chroot");
139             exit(1);
140         }
141     }
142     /* release privileges and go daemon */
143     if (setgid(65534) !=0) { /* first give away group privileges */
144         perror("cannot give away group privileges");
145         exit(1);
146     }
147     if (setuid(65534) !=0) { /* and only after user ... */
148         perror("cannot give away user privileges");
149         exit(1);
150     }
151     if (demonize) {          /* go daemon */
152         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
153         if (daemon(0, 0) != 0) {
154             perror("cannot start as daemon");
155             exit(1);
156         }
157     }
158     /* main body */
159     if (listen(list_fd, BACKLOG) < 0 ) {
160         PrintErr("listen error");
161         exit(1);
162     }
163     /* handle echo to client */
164     while (1) {
165         /* accept connection */
166         len = sizeof(cli_add);
167         while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) 
168                 < 0) && (errno == EINTR)); 
169         if (conn_fd < 0) {
170             PrintErr("accept error");
171             exit(1);
172         }
173         if (debugging) {
174             inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr));
175             snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr);
176             if (demonize) {
177                 syslog(LOG_DEBUG, debug);
178             } else {
179                 printf("%s", debug);
180             }
181         }
182         /* fork to handle connection */
183         if ( (pid = fork()) < 0 ){
184             PrintErr("fork error");
185             exit(1);
186         }
187         if (pid == 0) {      /* child */
188             close(list_fd);          /* close listening socket */   
189             ServPage(conn_fd);       /* handle echo */
190             if (debugging) {
191                 snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr);
192                 if (demonize) {
193                     syslog(LOG_DEBUG, debug);
194                 } else {
195                     printf("%s", debug);
196                 }
197             }
198             exit(0);
199         } else {             /* parent */
200             close(conn_fd);          /* close connected socket */
201         }
202     }
203     /* normal exit, never reached */
204     exit(0);
205 }
206 /*
207  * routine to print usage info and exit
208  */
209 void usage(void) 
210 {
211     printf("Elementary echo server\n");
212     printf("Usage:\n");
213     printf("  echod [-h] \n");
214     printf("  -h           print this help\n");
215     printf("  -d           write debug info\n");
216     printf("  -i           use interactively\n");
217     printf("  -c           disable BSD semantics\n");
218     printf("  -w N         wait N sec. before calling accept\n");
219     exit(1);
220 }
221 /*
222  * routine to handle echo for connection
223  */
224 void ServPage(int sockfd) 
225 {
226     char buffer[MAXLINE];
227     char outbuf[1024];
228     FILE *sock, *file;
229     char *line, *copy, *method, *ptr, *filename, *version;
230     char *methods[] = { "GET", "PUT", NULL };
231     char *codes[] = {
232         "200 OK",
233         "400 Bad Request",
234         "404 Not Found",
235         "500 Internal Server Error",
236         NULL
237     };
238     int nleft;
239     int i, j;
240
241     sock = fdopen(sockfd, "w+");
242     /* main loop, reading 0 char means client close connection */
243     line = fgets(buffer, MAXLINE, sock);
244     if (line == NULL) {
245         PrintErr("Errore in lettura");
246         return;
247     }
248     /* parsing first line, getting method and filename */
249     copy = strndupa(line, MAXLINE);
250     if ((method = strtok_r(copy, " ", &ptr)) == NULL) {
251         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
252         print_headers(sock);
253         print_err400(sock, line);
254         return;
255     }
256     if ((filename = strtok_r(NULL, " ", &ptr)) == NULL) {
257         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
258         print_headers(sock);
259         print_err400(sock, line);
260         return;
261     }
262     if ((version = strtok_r(NULL, " ", &ptr)) == NULL) {
263         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
264         print_headers(sock);
265         print_err400(sock, line);
266         return;
267     }
268     i = 0;
269     while ( (ptr = methods[i]) != NULL) {
270         if ( (strncmp(ptr, method, strlen(ptr)) == 0)) {
271             break;
272         }
273         i++;
274     }
275     if (i>=2) {
276         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
277         print_headers(sock);
278         print_err400(sock, line);
279         return;
280     }
281
282     while (strcmp(line,"\r\n")) {
283         line = fgets(buffer, MAXLINE, sock);
284     }
285
286     if ( (file = fopen(filename, "r")) == NULL) {
287         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
288         print_headers(sock);
289         print_err404(sock, filename);
290         return;
291     }
292     fprintf(sock, "HTTP/1.0 %s\n", codes[0]);
293     print_headers(sock);
294
295     j = 0;
296     while (!feof(file)) {
297         if ( (nleft = full_fread(file, outbuf, 1024)) != 0) {
298             if (ferror(file)) {
299                 fprintf(sock, "HTTP/1.0 %s\n", codes[3]);
300                 print_headers(sock);
301                 snprintf(buffer, MAXLINE, "reading %s", filename);
302                 print_err500(sock, buffer);
303                 return;
304             }
305         }
306         if (full_fwrite(sock, outbuf, 1024-nleft) != 0) {
307             if (ferror(file)) {
308                 fprintf(sock, "HTTP/1.0 %s\n", codes[3]);
309                 print_headers(sock);
310                 snprintf(buffer, MAXLINE, "writing");
311                 print_err500(sock, buffer);
312                 return;
313             }   
314         }
315     }
316     fclose(file);
317     fclose(sock);
318     return;
319 }
320 /*
321  * routine to print error on stout or syslog
322  */
323 void PrintErr(char * error) 
324 {
325     if (demonize) {                       /* daemon mode */
326         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
327     } else {
328         perror(error);
329     }
330     return;
331 }
332
333 void print_headers(FILE *file)
334 {
335     time_t tempo;
336
337     time(&tempo);
338     fprintf(file, "Date: %s", ctime(&tempo));
339     fprintf(file, "Server: WWWd test server\n");
340     fprintf(file, "Connection: close\n");
341     fprintf(file, "Content-Type: text/html; charset=iso-8859-1\n");
342     fprintf(file, "\n");
343     return;
344 }
345
346 void print_err404(FILE *file, char *filename)
347 {
348     fprintf(file, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
349     fprintf(file, "<BODY><H1>Not Found</H1>\nThe requested ");
350     fprintf(file, "URL %s was not found on this server.<P><HR>", filename);
351     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi</ADDRESS></BODY></HTML>");
352     return;
353 }
354
355 void print_err400(FILE *file, char *string)
356 {
357     fprintf(file, "<HTML><HEAD><TITLE>404 Bad Request</TITLE></HEAD>\n");
358     fprintf(file, "<BODY><H1>Bad Request</H1>\n ");
359     fprintf(file, "Your browser sent a request that this server could not ");
360     fprintf(file, "understand.<P>The request line<P> %s <P>", string);
361     fprintf(file, "is invalid following the protocol<p><HR>");
362     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi </ADDRESS></BODY></HTML>");
363     return;
364 }
365
366
367 void print_err500(FILE *file, char *string)
368 {
369     fprintf(file, "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE>\n");
370     fprintf(file, "</HEAD><BODY><H1>Internal Server Error</H1>\n ");
371     fprintf(file, "We got an error processing your request.<P>");
372     fprintf(file, "Error is: %s <P><HR>\n", string);
373     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi </ADDRESS></BODY></HTML>");
374     return;
375 }