indent -kr -i8 -l132 on everything in webcit-ng
[citadel.git] / webcit-ng / webserver.c
1 /*
2  * webserver.c
3  *
4  * This module handles the task of setting up a listening socket, accepting
5  * connections, and dispatching active connections onto a pool of worker
6  * threads.
7  *
8  * Copyright (c) 1996-2018 by the citadel.org team
9  *
10  * This program is open source software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 3.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18
19 #include "webcit.h"                     // All other headers are included from this header.
20
21 int num_threads_executing = 1;          // Number of worker threads currently bound to a connected client
22 int num_threads_existing = 1;           // Total number of worker threads that exist right now
23 int is_https = 0;                       // Set to nonzero if we are running as an HTTPS server today.
24 static void *original_brk = NULL;       // Remember the original program break so we can test for leaks
25
26
27 /*
28  * Spawn an additional worker thread into the pool.
29  */
30 void spawn_another_worker_thread(int *pointer_to_master_socket)
31 {
32         pthread_t th;           // Thread descriptor
33         pthread_attr_t attr;    // Thread attributes
34
35         /* set attributes for the new thread */
36         pthread_attr_init(&attr);
37         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
38         pthread_attr_setstacksize(&attr, 1048576);      // Large stacks to prevent MIME parser crash on FreeBSD
39
40         /* now create the thread */
41         if (pthread_create(&th, &attr, (void *(*)(void *)) worker_entry, (void *) pointer_to_master_socket) != 0) {
42                 syslog(LOG_WARNING, "Can't create thread: %s", strerror(errno));
43         } else {
44                 ++num_threads_existing;
45                 ++num_threads_executing;
46         }
47
48         /* free up the attributes */
49         pthread_attr_destroy(&attr);
50 }
51
52
53 /*
54  * Entry point for worker threads
55  */
56 void worker_entry(int *pointer_to_master_socket)
57 {
58         int master_socket = *pointer_to_master_socket;
59         int i = 0;
60         int fail_this_transaction = 0;
61         struct client_handle ch;
62
63         while (1) {
64                 /* Each worker thread blocks on accept() while waiting for something to do. */
65                 memset(&ch, 0, sizeof ch);
66                 ch.sock = -1;
67                 errno = EAGAIN;
68                 do {
69                         --num_threads_executing;
70                         syslog(LOG_DEBUG, "Additional memory allocated since startup: %d bytes", (int) (sbrk(0) - original_brk));
71                         syslog(LOG_DEBUG, "Thread 0x%x calling accept() on master socket %d", (unsigned int) pthread_self(),
72                                master_socket);
73                         ch.sock = accept(master_socket, NULL, 0);
74                         if (ch.sock < 0) {
75                                 syslog(LOG_DEBUG, "accept() : %s", strerror(errno));
76                         }
77                         ++num_threads_executing;
78                         syslog(LOG_DEBUG, "socket %d is awake , threads executing: %d , threads total: %d", ch.sock,
79                                num_threads_executing, num_threads_existing);
80                 } while ((master_socket > 0) && (ch.sock < 0));
81
82                 /* If all threads are executing, spawn more, up to the maximum */
83                 if ((num_threads_executing >= num_threads_existing) && (num_threads_existing <= MAX_WORKER_THREADS)) {
84                         spawn_another_worker_thread(pointer_to_master_socket);
85                 }
86
87                 /* We have a client.  Do some work. */
88
89                 /* Set the SO_REUSEADDR socket option */
90                 i = 1;
91                 setsockopt(ch.sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
92
93                 /* If we are an HTTPS server, go crypto now. */
94                 if (is_https) {
95                         starttls(&ch);
96                         if (ch.ssl_handle == NULL) {
97                                 fail_this_transaction = 1;
98                         }
99                 } else {
100                         int fdflags;
101                         fdflags = fcntl(ch.sock, F_GETFL);
102                         if (fdflags < 0) {
103                                 syslog(LOG_WARNING, "unable to get server socket flags! %s", strerror(errno));
104                         }
105                 }
106
107                 /* Perform an HTTP transaction... */
108                 if (fail_this_transaction == 0) {
109                         perform_one_http_transaction(&ch);
110                 }
111
112                 /* Shut down SSL/TLS if required... */
113                 if (is_https) {
114                         endtls(&ch);
115                 }
116                 /* ...and close the socket. */
117                 //syslog(LOG_DEBUG, "Closing socket %d...", ch.sock);
118                 //lingering_close(ch.sock);
119                 close(ch.sock);
120                 syslog(LOG_DEBUG, "Closed socket %d.", ch.sock);
121         }
122 }
123
124
125 /*
126  * Start up a TCP HTTP[S] server on the requested port
127  */
128 int webserver(char *webserver_interface, int webserver_port, int webserver_protocol)
129 {
130         int master_socket = (-1);
131         original_brk = sbrk(0);
132
133         switch (webserver_protocol) {
134         case WEBSERVER_HTTP:
135                 syslog(LOG_DEBUG, "Starting HTTP server on %s:%d", webserver_interface, webserver_port);
136                 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
137                 break;
138         case WEBSERVER_HTTPS:
139                 syslog(LOG_DEBUG, "Starting HTTPS server on %s:%d", webserver_interface, webserver_port);
140                 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
141                 init_ssl();
142                 is_https = 1;
143                 break;
144         default:
145                 syslog(LOG_ERR, "unknown protocol");
146                 ;;
147         }
148
149         if (master_socket < 1) {
150                 syslog(LOG_ERR, "Unable to bind the web server listening socket");
151                 return (1);
152         }
153
154         syslog(LOG_INFO, "Listening on socket %d", master_socket);
155         signal(SIGPIPE, SIG_IGN);
156
157         worker_entry(&master_socket);   // this thread becomes a worker
158         return (0);
159 }