4 // This module handles the task of setting up a listening socket, accepting
5 // connections, and dispatching active connections onto a pool of worker
8 // Copyright (c) 1996-2022 by the citadel.org team
10 // This program is open source software. It runs great on the
11 // Linux operating system (and probably elsewhere). You can use,
12 // copy, and run it under the terms of the GNU General Public
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 #include "webcit.h" // All other headers are included from this header.
22 int num_threads_executing = 1; // Number of worker threads currently bound to a connected client
23 int num_threads_existing = 1; // Total number of worker threads that exist right now
24 int is_https = 0; // Set to nonzero if we are running as an HTTPS server today.
25 static void *original_brk = NULL; // Remember the original program break so we can test for leaks
28 // Spawn an additional worker thread into the pool.
29 void spawn_another_worker_thread(int *pointer_to_master_socket) {
30 pthread_t th; // Thread descriptor
31 pthread_attr_t attr; // Thread attributes
33 // set attributes for the new thread
34 pthread_attr_init(&attr);
35 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
36 pthread_attr_setstacksize(&attr, 1048576); // Large stacks to prevent MIME parser crash on FreeBSD
38 // now create the thread
39 if (pthread_create(&th, &attr, (void *(*)(void *)) worker_entry, (void *) pointer_to_master_socket) != 0) {
40 syslog(LOG_WARNING, "Can't create thread: %s", strerror(errno));
43 ++num_threads_existing;
44 ++num_threads_executing;
47 // free up the attributes
48 pthread_attr_destroy(&attr);
52 // Entry point for worker threads
53 void worker_entry(int *pointer_to_master_socket) {
54 int master_socket = *pointer_to_master_socket;
56 int fail_this_transaction = 0;
57 struct client_handle ch;
60 // Each worker thread blocks on accept() while waiting for something to do.
61 // We don't have to worry about the "thundering herd" problem on modern kernels; for an explanation see
62 // https://stackoverflow.com/questions/2213779/does-the-thundering-herd-problem-exist-on-linux-anymore
63 memset(&ch, 0, sizeof ch);
67 --num_threads_executing;
68 syslog(LOG_DEBUG, "Additional memory allocated since startup: %d bytes", (int) (sbrk(0) - original_brk));
69 syslog(LOG_DEBUG, "Thread 0x%x calling accept() on master socket %d", (unsigned int) pthread_self(),
71 ch.sock = accept(master_socket, NULL, 0);
73 syslog(LOG_DEBUG, "accept() : %s", strerror(errno));
75 ++num_threads_executing;
76 syslog(LOG_DEBUG, "socket %d is awake , threads executing: %d , threads total: %d", ch.sock,
77 num_threads_executing, num_threads_existing);
78 } while ((master_socket > 0) && (ch.sock < 0));
80 // If all threads are executing, spawn more, up to the maximum
81 if ((num_threads_executing >= num_threads_existing) && (num_threads_existing <= MAX_WORKER_THREADS)) {
82 spawn_another_worker_thread(pointer_to_master_socket);
85 // We have a client. Do some work.
87 // Set the SO_REUSEADDR socket option
89 setsockopt(ch.sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
91 // If we are an HTTPS server, go crypto now.
94 if (ch.ssl_handle == NULL) {
95 fail_this_transaction = 1;
100 fdflags = fcntl(ch.sock, F_GETFL);
102 syslog(LOG_WARNING, "unable to get server socket flags! %s", strerror(errno));
106 // Perform an HTTP transaction...
107 if (fail_this_transaction == 0) {
108 perform_one_http_transaction(&ch);
111 // Shut down SSL/TLS if required...
115 // ...and close the socket.
116 //syslog(LOG_DEBUG, "Closing socket %d...", ch.sock);
117 //lingering_close(ch.sock);
119 syslog(LOG_DEBUG, "Closed socket %d.", ch.sock);
124 // Start up a TCP HTTP[S] server on the requested port
125 int webserver(char *webserver_interface, int webserver_port, int webserver_protocol) {
126 int master_socket = (-1);
127 original_brk = sbrk(0);
129 switch (webserver_protocol) {
131 syslog(LOG_DEBUG, "Starting HTTP server on %s:%d", webserver_interface, webserver_port);
132 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
134 case WEBSERVER_HTTPS:
135 syslog(LOG_DEBUG, "Starting HTTPS server on %s:%d", webserver_interface, webserver_port);
136 master_socket = webcit_tcp_server(webserver_interface, webserver_port, 10);
141 syslog(LOG_ERR, "unknown protocol");
145 if (master_socket < 1) {
146 syslog(LOG_ERR, "Unable to bind the web server listening socket");
150 syslog(LOG_INFO, "Listening on socket %d", master_socket);
151 signal(SIGPIPE, SIG_IGN);
153 worker_entry(&master_socket); // this thread becomes a worker