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