]> code.citadel.org Git - citadel.git/blob - webcit/webserver.c
140c2ee23de14eacaf9b20acf78b028a6b6ebdfa
[citadel.git] / webcit / webserver.c
1 /*
2  * webserver.c
3  *
4  * This contains a simple multithreaded TCP server manager.  It sits around
5  * waiting on the specified port for incoming HTTP connections.  When a
6  * connection is established, it calls context_loop() from context_loop.c.
7  *
8  */
9
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <sys/socket.h>
18 #include <sys/time.h>
19 #include <limits.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #include <string.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <pthread.h>
27 #include "webcit.h"
28
29 int msock;                                      /* master listening socket */
30 extern void *context_loop(int);
31
32 /*
33  * This is a generic function to set up a master socket for listening on
34  * a TCP port.  The server shuts down if the bind fails.
35  */
36 int ig_tcp_server(int port_number, int queue_len)
37 {
38         struct sockaddr_in sin;
39         int s, i;
40
41         memset(&sin, 0, sizeof(sin));
42         sin.sin_family = AF_INET;
43         sin.sin_addr.s_addr = INADDR_ANY;
44
45         if (port_number == 0) {
46                 printf("webcit: Cannot start: no port number specified.\n");
47                 exit(1);
48                 }
49         
50         sin.sin_port = htons((u_short)port_number);
51
52         s = socket(PF_INET, SOCK_STREAM, (getprotobyname("tcp")->p_proto));
53         if (s < 0) {
54                 printf("webcit: Can't create a socket: %s\n",
55                         strerror(errno));
56                 exit(errno);
57                 }
58
59         /* Set the SO_REUSEADDR socket option, because it makes sense. */
60         i = 1;
61         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
62
63         if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
64                 printf("webcit: Can't bind: %s\n", strerror(errno));
65                 exit(errno);
66                 }
67
68         if (listen(s, queue_len) < 0) {
69                 printf("webcit: Can't listen: %s\n", strerror(errno));
70                 exit(errno);
71                 }
72
73         return(s);
74         }
75
76
77 /*
78  * client_write()   ...    Send binary data to the client.
79  */
80 void client_write(int sock, char *buf, int nbytes)
81 {
82         int bytes_written = 0;
83         int retval;
84         while (bytes_written < nbytes) {
85                 retval = write(sock, &buf[bytes_written],
86                         nbytes - bytes_written);
87                 if (retval < 1) {
88                         printf("client_write() failed: %s\n",
89                                 strerror(errno));
90                         pthread_exit(NULL);
91                         }
92                 bytes_written = bytes_written + retval;
93                 }
94         }
95
96
97 /*
98  * cprintf()  ...   Send formatted printable data to the client.   It is
99  *                  implemented in terms of client_write() but remains in
100  *                  sysdep.c in case we port to somewhere without va_args...
101  */
102 void cprintf(int sock, const char *format, ...) {   
103         va_list arg_ptr;   
104         char buf[256];   
105    
106         va_start(arg_ptr, format);   
107         if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
108                 buf[sizeof buf - 2] = '\n';
109         client_write(sock, buf, strlen(buf)); 
110         va_end(arg_ptr);
111         }   
112
113
114 /*
115  * Read data from the client socket.
116  * Return values are:
117  *      1       Requested number of bytes has been read.
118  *      0       Request timed out.
119  * If the socket breaks, the session is immediately terminated.
120  */
121 int client_read_to(int sock, char *buf, int bytes, int timeout)
122 {
123         int len,rlen;
124         fd_set rfds;
125         struct timeval tv;
126         int retval;
127
128         len = 0;
129         while(len<bytes) {
130                 FD_ZERO(&rfds);
131                 FD_SET(sock, &rfds);
132                 tv.tv_sec = timeout;
133                 tv.tv_usec = 0;
134
135                 retval = select( (sock)+1, 
136                                         &rfds, NULL, NULL, &tv);
137                 if (FD_ISSET(sock, &rfds) == 0) {
138                         return(0);
139                         }
140
141                 rlen = read(sock, &buf[len], bytes-len);
142                 if (rlen<1) {
143                         printf("client_read() failed: %s\n",
144                                 strerror(errno));
145                         pthread_exit(NULL);
146                         }
147                 len = len + rlen;
148                 }
149         return(1);
150         }
151
152 /*
153  * Read data from the client socket with default timeout.
154  * (This is implemented in terms of client_read_to() and could be
155  * justifiably moved out of sysdep.c)
156  */
157 int client_read(int sock, char *buf, int bytes)
158 {
159         return(client_read_to(sock, buf, bytes, SLEEPING));
160         }
161
162
163 /*
164  * client_gets()   ...   Get a LF-terminated line of text from the client.
165  * (This is implemented in terms of client_read() and could be
166  * justifiably moved out of sysdep.c)
167  */
168 int client_gets(int sock, char *buf)
169 {
170         int i, retval;
171
172         /* Read one character at a time.
173          */
174         for (i = 0;;i++) {
175                 retval = client_read(sock, &buf[i], 1);
176                 if (retval != 1 || buf[i] == '\n' || i == 255)
177                         break;
178                 }
179
180         /* If we got a long line, discard characters until the newline.
181          */
182         if (i == 255)
183                 while (buf[i] != '\n' && retval == 1)
184                         retval = client_read(sock, &buf[i], 1);
185
186         /*
187          * Strip any trailing not-printable characters.
188          */
189         buf[i] = 0;
190         while ((strlen(buf)>0)&&(!isprint(buf[strlen(buf)-1]))) {
191                 buf[strlen(buf)-1] = 0;
192                 }
193         return(retval);
194         }
195
196
197 /*
198  * Start running as a daemon.  Only close stdio if do_close_stdio is set.
199  */
200 void start_daemon(int do_close_stdio) {
201         if (do_close_stdio) {
202                 /* close(0); */
203                 close(1);
204                 close(2);
205                 }
206         signal(SIGHUP,SIG_IGN);
207         signal(SIGINT,SIG_IGN);
208         signal(SIGQUIT,SIG_IGN);
209         if (fork()!=0) exit(0);
210         }
211
212
213
214 /*
215  * Here's where it all begins.
216  */
217 int main(int argc, char **argv)
218 {
219         struct sockaddr_in fsin;        /* Data for master socket */
220         int alen;                       /* Data for master socket */
221         int ssock;                      /* Descriptor for master socket */
222         pthread_t SessThread;           /* Thread descriptor */
223         pthread_attr_t attr;            /* Thread attributes */
224         int a, i;                       /* General-purpose variables */
225         char convbuf[128];
226         
227         /* Tell 'em who's in da house */
228         printf("WebCit v2 experimental\n");
229         printf("Copyright (C) 1996-1998 by Art Cancro.  ");
230         printf("All rights reserved.\n\n");
231
232         /*
233          * Bind the server to our favourite port.
234          * There is no need to check for errors, because ig_tcp_server()
235          * exits if it doesn't succeed.
236          */
237         printf("Attempting to bind to port %d...\n", PORT_NUM);
238         msock = ig_tcp_server(PORT_NUM, 5);
239         printf("Listening on socket %d\n", msock);
240
241         /* 
242          * Endless loop.  Listen on the master socket.  When a connection
243          * comes in, create a socket, a context, and a thread.
244          */     
245         while (1) {
246                 ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
247                 if (ssock < 0) {
248                         printf("webcit: accept() failed: %s\n",
249                                 strerror(errno));
250                         }
251                 else {
252                         /* Set the SO_REUSEADDR socket option */
253                         i = 1;
254                         setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR,
255                                 &i, sizeof(i));
256
257                         /* set attributes for the new thread */
258                         pthread_attr_init(&attr);
259                         pthread_attr_setdetachstate(&attr,
260                                 PTHREAD_CREATE_DETACHED);
261
262                         /* now create the thread */
263                         if (pthread_create(&SessThread, &attr,
264                                            (void* (*)(void*)) context_loop,
265                                            &ssock)
266                             != 0) {
267                                 printf("webcit: can't create thread: %s\n",
268                                         strerror(errno));
269                                 }
270
271                         }
272                 }
273         }
274