Bug Summary

File:lwan.c
Warning:line 203, column 12
Potential memory leak

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name lwan.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -fno-plt -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/clang/8.0.0 -include /home/buildbot/lwan-worker/clang-analyze/build/lwan-build-config.h -D _FILE_OFFSET_BITS=64 -D _TIME_BITS=64 -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib/missing -I /usr/include/luajit-2.0 -I /usr/include/valgrind -I /home/buildbot/lwan-worker/clang-analyze/build/src/lib -I /home/buildbot/lwan-worker/clang-analyze/build -internal-isystem /usr/local/include -internal-isystem /usr/lib/clang/8.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -std=gnu99 -fdebug-compilation-dir /home/buildbot/lwan-worker/clang-analyze/build/src/lib -ferror-limit 19 -fmessage-length 0 -stack-protector 2 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /home/buildbot/lwan-worker/clang-analyze/CLANG/2019-04-17-045736-12850-1 -x c /home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c -faddrsig
1/*
2 * lwan - simple web server
3 * Copyright (c) 2012, 2013 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
18 * USA.
19 */
20
21#define _GNU_SOURCE
22#include <assert.h>
23#include <ctype.h>
24#include <dlfcn.h>
25#include <errno(*__errno_location ()).h>
26#include <libproc.h>
27#include <limits.h>
28#include <signal.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/resource.h>
32#include <sys/socket.h>
33#include <sys/epoll.h>
34#include <sys/types.h>
35#include <unistd.h>
36
37#include "lwan-private.h"
38
39#include "lwan-config.h"
40#include "lwan-http-authorize.h"
41
42#if defined(HAVE_LUA)
43#include "lwan-lua.h"
44#endif
45
46/* See detect_fastest_monotonic_clock() */
47clockid_t monotonic_clock_id = CLOCK_MONOTONIC1;
48
49static const struct lwan_config default_config = {
50 .listener = "localhost:8080",
51 .keep_alive_timeout = 15,
52 .quiet = false0,
53 .reuse_port = false0,
54 .proxy_protocol = false0,
55 .allow_cors = false0,
56 .expires = 1 * ONE_WEEK(((60 * 60) * 24) * 7),
57 .n_threads = 0,
58 .max_post_data_size = 10 * DEFAULT_BUFFER_SIZE4096,
59 .allow_post_temp_file = false0,
60};
61
62LWAN_HANDLER(brew_coffee)static enum lwan_http_status lwan_handler_brew_coffee( struct
lwan_request *, struct lwan_response *, void *); static const
struct lwan_handler_info __attribute__((used, section("lwan_handler"
))) lwan_handler_info_brew_coffee = {.name = "brew_coffee", .
handler = lwan_handler_brew_coffee}; static enum lwan_http_status
lwan_handler_brew_coffee( struct lwan_request *request __attribute__
((unused)), struct lwan_response *response __attribute__((unused
)), void *data __attribute__((unused)))
63{
64 /* Placeholder handler so that __start_lwan_handler and __stop_lwan_handler
65 * symbols will get defined.
66 */
67 return HTTP_I_AM_A_TEAPOT;
68}
69
70__attribute__((no_sanitize_address))
71static void *find_handler(const char *name)
72{
73 extern const struct lwan_handler_info SECTION_START(lwan_handler)__start_lwan_handler[];
74 extern const struct lwan_handler_info SECTION_END(lwan_handler)__stop_lwan_handler[];
75 const struct lwan_handler_info *handler;
76
77 for (handler = __start_lwan_handler; handler < __stop_lwan_handler;
78 handler++) {
79 if (streq(handler->name, name))
80 return handler->handler;
81 }
82
83 return NULL((void*)0);
84}
85
86__attribute__((no_sanitize_address))
87static const struct lwan_module *find_module(const char *name)
88{
89 extern const struct lwan_module_info SECTION_START(lwan_module)__start_lwan_module[];
90 extern const struct lwan_module_info SECTION_END(lwan_module)__stop_lwan_module[];
91 const struct lwan_module_info *module;
92
93 for (module = __start_lwan_module; module < __stop_lwan_module; module++) {
94 if (streq(module->name, name))
95 return module->module;
96 }
97
98 return NULL((void*)0);
99}
100
101static void destroy_urlmap(void *data)
102{
103 struct lwan_url_map *url_map = data;
104
105 if (url_map->module) {
106 const struct lwan_module *module = url_map->module;
107
108 if (module->destroy)
109 module->destroy(url_map->data);
110 } else if (url_map->data && url_map->flags & HANDLER_DATA_IS_HASH_TABLE) {
111 hash_free(url_map->data);
112 }
113
114 free(url_map->authorization.realm);
115 free(url_map->authorization.password_file);
116 free((char *)url_map->prefix);
117 free(url_map);
118}
119
120static struct lwan_url_map *add_url_map(struct lwan_trie *t, const char *prefix,
121 const struct lwan_url_map *map)
122{
123 struct lwan_url_map *copy = malloc(sizeof(*copy));
124
125 if (!copy)
126 lwan_status_critical_perror("Could not copy URL map")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 126, __FUNCTION__, "Could not copy URL map")
;
127
128 memcpy(copy, map, sizeof(*copy));
129
130 copy->prefix = strdup(prefix ? prefix : copy->prefix);
131 if (!copy->prefix)
132 lwan_status_critical_perror("Could not copy URL prefix")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 132, __FUNCTION__, "Could not copy URL prefix")
;
133
134 copy->prefix_len = strlen(copy->prefix);
135 lwan_trie_add(t, copy->prefix, copy);
136
137 return copy;
138}
139
140static void parse_listener_prefix_authorization(struct config *c,
141 struct config_line *l,
142 struct lwan_url_map *url_map)
143{
144 if (!streq(l->value, "basic")) {
145 config_error(c, "Only basic authorization supported");
146 return;
147 }
148
149 memset(&url_map->authorization, 0, sizeof(url_map->authorization));
150
151 while (config_read_line(c, l)) {
152 switch (l->type) {
153 case CONFIG_LINE_TYPE_LINE:
154 if (streq(l->key, "realm")) {
155 free(url_map->authorization.realm);
156 url_map->authorization.realm = strdup(l->value);
157 } else if (streq(l->key, "password_file")) {
158 free(url_map->authorization.password_file);
159 url_map->authorization.password_file = strdup(l->value);
160 }
161 break;
162
163 case CONFIG_LINE_TYPE_SECTION:
164 config_error(c, "Unexpected section: %s", l->key);
165 goto error;
166
167 case CONFIG_LINE_TYPE_SECTION_END:
168 if (!url_map->authorization.realm)
169 url_map->authorization.realm = strdup("Lwan");
170 if (!url_map->authorization.password_file)
171 url_map->authorization.password_file = strdup("htpasswd");
172
173 url_map->flags |= HANDLER_MUST_AUTHORIZE;
174 goto out;
175 }
176 }
177
178out:
179 return;
180
181error:
182 free(url_map->authorization.realm);
183 free(url_map->authorization.password_file);
184}
185
186static void parse_listener_prefix(struct config *c,
187 struct config_line *l,
188 struct lwan *lwan,
189 const struct lwan_module *module,
190 void *handler)
191{
192 struct lwan_url_map url_map = {};
193 struct hash *hash = hash_str_new(free, free);
194 char *prefix = strdupa(l->value)(__extension__ ({ const char *__old = (l->value); size_t __len
= strlen (__old) + 1; char *__new = (char *) __builtin_alloca
(__len); (char *) memcpy (__new, __old, __len); }))
;
195 struct config *isolated;
196
197 isolated = config_isolate_section(c, l);
198 if (!isolated) {
21
Assuming 'isolated' is non-null
22
Taking false branch
199 config_error(c, "Could not isolate configuration file");
200 goto out;
201 }
202
203 while (config_read_line(c, l)) {
23
Loop condition is true. Entering loop body
27
Potential memory leak
204 switch (l->type) {
24
Control jumps to 'case CONFIG_LINE_TYPE_LINE:' at line 205
205 case CONFIG_LINE_TYPE_LINE:
206 hash_add(hash, strdup(l->key), strdup(l->value));
25
Memory is allocated
207 break;
26
Execution continues on line 203
208
209 case CONFIG_LINE_TYPE_SECTION:
210 if (streq(l->key, "authorization")) {
211 parse_listener_prefix_authorization(c, l, &url_map);
212 } else if (!config_skip_section(c, l)) {
213 config_error(c, "Could not skip section");
214 goto out;
215 }
216 break;
217
218 case CONFIG_LINE_TYPE_SECTION_END:
219 goto add_map;
220 }
221 }
222
223 config_error(c, "Expecting section end while parsing prefix");
224 goto out;
225
226add_map:
227 assert((handler && !module) || (!handler && module))((void) sizeof (((handler && !module) || (!handler &&
module)) ? 1 : 0), __extension__ ({ if ((handler && !
module) || (!handler && module)) ; else __assert_fail
("(handler && !module) || (!handler && module)"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 227, __extension__ __PRETTY_FUNCTION__); }))
;
228
229 if (handler) {
230 url_map.handler = handler;
231 url_map.flags |= HANDLER_PARSE_MASK | HANDLER_DATA_IS_HASH_TABLE;
232 url_map.data = hash;
233 url_map.module = NULL((void*)0);
234
235 hash = NULL((void*)0);
236 } else if (module->create_from_hash && module->handle_request) {
237 url_map.data = module->create_from_hash(prefix, hash);
238 if (!url_map.data) {
239 config_error(c, "Could not create module instance");
240 goto out;
241 }
242
243 if (module->parse_conf && !module->parse_conf(url_map.data, isolated)) {
244 const char *msg = config_last_error(isolated);
245
246 config_error(c, "Error from module: %s", msg ? msg : "Unknown");
247 goto out;
248 }
249
250 url_map.handler = module->handle_request;
251 url_map.flags |= module->flags;
252 url_map.module = module;
253 } else if (UNLIKELY(!module->create_from_hash)__builtin_expect(((!module->create_from_hash)), (0))) {
254 config_error(c, "Module isn't prepared to load settings from a file; "
255 "create_from_hash() method isn't present");
256 goto out;
257 } else if (UNLIKELY(!module->handle_request)__builtin_expect(((!module->handle_request)), (0))) {
258 config_error(c, "Module does not have handle_request() method");
259 goto out;
260 }
261
262 add_url_map(&lwan->url_map_trie, prefix, &url_map);
263
264out:
265 hash_free(hash);
266 config_close(isolated);
267}
268
269void lwan_set_url_map(struct lwan *l, const struct lwan_url_map *map)
270{
271 lwan_trie_destroy(&l->url_map_trie);
272 if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap))__builtin_expect(((!lwan_trie_init(&l->url_map_trie, destroy_urlmap
))), (0))
)
273 lwan_status_critical_perror("Could not initialize trie")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 273, __FUNCTION__, "Could not initialize trie")
;
274
275 for (; map->prefix; map++) {
276 struct lwan_url_map *copy = add_url_map(&l->url_map_trie, NULL((void*)0), map);
277
278 if (copy->module && copy->module->create) {
279 copy->data = copy->module->create (map->prefix, copy->args);
280 copy->flags = copy->module->flags;
281 copy->handler = copy->module->handle_request;
282 } else {
283 copy->flags = HANDLER_PARSE_MASK;
284 }
285 }
286}
287
288static void parse_listener(struct config *c, struct config_line *l,
289 struct lwan *lwan)
290{
291 free(lwan->config.listener);
292 lwan->config.listener = strdup(l->value);
293
294 while (config_read_line(c, l)) {
14
Loop condition is true. Entering loop body
295 switch (l->type) {
15
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 299
296 case CONFIG_LINE_TYPE_LINE:
297 config_error(c, "Expecting prefix section");
298 return;
299 case CONFIG_LINE_TYPE_SECTION:
300 if (l->key[0] == '&') {
16
Assuming the condition is false
17
Taking false branch
301 l->key++;
302
303 void *handler = find_handler(l->key);
304 if (handler) {
305 parse_listener_prefix(c, l, lwan, NULL((void*)0), handler);
306 continue;
307 }
308
309 config_error(c, "Could not find handler name: %s", l->key);
310 return;
311 }
312
313 const struct lwan_module *module = find_module(l->key);
314 if (module) {
18
Assuming 'module' is non-null
19
Taking true branch
315 parse_listener_prefix(c, l, lwan, module, NULL((void*)0));
20
Calling 'parse_listener_prefix'
316 continue;
317 }
318
319 config_error(c, "Invalid section or module not found: %s", l->key);
320 return;
321 case CONFIG_LINE_TYPE_SECTION_END:
322 return;
323 }
324 }
325
326 config_error(c, "Expecting section end while parsing listener");
327}
328
329const char *lwan_get_config_path(char *path_buf, size_t path_buf_len)
330{
331 char buffer[PATH_MAX4096];
332
333 if (proc_pidpath(getpid(), buffer, sizeof(buffer)) < 0)
334 goto out;
335
336 char *path = strrchr(buffer, '/');
337 if (!path)
338 goto out;
339 int ret = snprintf(path_buf, path_buf_len, "%s.conf", path + 1);
340 if (ret < 0 || ret >= (int)path_buf_len)
341 goto out;
342
343 return path_buf;
344
345out:
346 return "lwan.conf";
347}
348
349static bool_Bool setup_from_config(struct lwan *lwan, const char *path)
350{
351 struct config *conf;
352 struct config_line line;
353 bool_Bool has_listener = false0;
354 char path_buf[PATH_MAX4096];
355
356 if (!path)
4
Taking true branch
357 path = lwan_get_config_path(path_buf, sizeof(path_buf));
358 lwan_status_info("Loading configuration file: %s", path)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 358, __FUNCTION__, "Loading configuration file: %s", path)
;
359
360 conf = config_open(path);
361 if (!conf)
5
Assuming 'conf' is non-null
6
Taking false branch
362 return false0;
363
364 if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap))
7
Assuming the condition is false
8
Taking false branch
365 return false0;
366
367 while (config_read_line(conf, &line)) {
9
Loop condition is true. Entering loop body
368 switch (line.type) {
10
Control jumps to 'case CONFIG_LINE_TYPE_SECTION:' at line 414
369 case CONFIG_LINE_TYPE_LINE:
370 if (streq(line.key, "keep_alive_timeout")) {
371 lwan->config.keep_alive_timeout = (unsigned short)parse_long(
372 line.value, default_config.keep_alive_timeout);
373 } else if (streq(line.key, "quiet")) {
374 lwan->config.quiet =
375 parse_bool(line.value, default_config.quiet);
376 } else if (streq(line.key, "reuse_port")) {
377 lwan->config.reuse_port =
378 parse_bool(line.value, default_config.reuse_port);
379 } else if (streq(line.key, "proxy_protocol")) {
380 lwan->config.proxy_protocol =
381 parse_bool(line.value, default_config.proxy_protocol);
382 } else if (streq(line.key, "allow_cors")) {
383 lwan->config.allow_cors =
384 parse_bool(line.value, default_config.allow_cors);
385 } else if (streq(line.key, "expires")) {
386 lwan->config.expires =
387 parse_time_period(line.value, default_config.expires);
388 } else if (streq(line.key, "error_template")) {
389 free(lwan->config.error_template);
390 lwan->config.error_template = strdup(line.value);
391 } else if (streq(line.key, "threads")) {
392 long n_threads =
393 parse_long(line.value, default_config.n_threads);
394 if (n_threads < 0)
395 config_error(conf, "Invalid number of threads: %ld",
396 n_threads);
397 lwan->config.n_threads = (unsigned short int)n_threads;
398 } else if (streq(line.key, "max_post_data_size")) {
399 long max_post_data_size = parse_long(
400 line.value, (long)default_config.max_post_data_size);
401 if (max_post_data_size < 0)
402 config_error(conf, "Negative maximum post data size");
403 else if (max_post_data_size > 128 * (1 << 20))
404 config_error(conf,
405 "Maximum post data can't be over 128MiB");
406 lwan->config.max_post_data_size = (size_t)max_post_data_size;
407 } else if (streq(line.key, "allow_temp_files")) {
408 lwan->config.allow_post_temp_file =
409 !!strstr(line.value, "post");
410 } else {
411 config_error(conf, "Unknown config key: %s", line.key);
412 }
413 break;
414 case CONFIG_LINE_TYPE_SECTION:
415 if (streq(line.key, "listener")) {
11
Taking true branch
416 if (!has_listener) {
12
Taking true branch
417 parse_listener(conf, &line, lwan);
13
Calling 'parse_listener'
418 has_listener = true1;
419 } else {
420 config_error(conf, "Only one listener supported");
421 }
422 } else if (streq(line.key, "straitjacket")) {
423 lwan_straitjacket_enforce_from_config(conf);
424 } else {
425 config_error(conf, "Unknown section type: %s", line.key);
426 }
427 break;
428 case CONFIG_LINE_TYPE_SECTION_END:
429 config_error(conf, "Unexpected section end");
430 }
431 }
432
433 if (config_last_error(conf)) {
434 lwan_status_critical("Error on config file \"%s\", line %d: %s", path,lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 435, __FUNCTION__, "Error on config file \"%s\", line %d: %s"
, path, config_cur_line(conf), config_last_error(conf))
435 config_cur_line(conf), config_last_error(conf))lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 435, __FUNCTION__, "Error on config file \"%s\", line %d: %s"
, path, config_cur_line(conf), config_last_error(conf))
;
436 }
437
438 config_close(conf);
439
440 return true1;
441}
442
443static void try_setup_from_config(struct lwan *l, const struct lwan_config *config)
444{
445 if (!setup_from_config(l, config->config_file_path)) {
3
Calling 'setup_from_config'
446 if (config->config_file_path) {
447 lwan_status_critical("Could not read config file: %s",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 448, __FUNCTION__, "Could not read config file: %s", config
->config_file_path)
448 config->config_file_path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 448, __FUNCTION__, "Could not read config file: %s", config
->config_file_path)
;
449 }
450 }
451
452 /* `quiet` key might have changed value. */
453 lwan_status_init(l);
454}
455
456static rlim_t setup_open_file_count_limits(void)
457{
458 struct rlimit r;
459
460 if (getrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) {
461 lwan_status_perror("Could not obtain maximum number of file "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 463, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
462 "descriptors. Assuming %d",lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 463, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
463 OPEN_MAX)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 463, __FUNCTION__, "Could not obtain maximum number of file "
"descriptors. Assuming %d", 256)
;
464 return OPEN_MAX256;
465 }
466
467 if (r.rlim_max != r.rlim_cur) {
468 const rlim_t current = r.rlim_cur;
469
470 if (r.rlim_max == RLIM_INFINITY0xffffffffffffffffuLL) {
471 r.rlim_cur = OPEN_MAX256;
472 } else if (r.rlim_cur < r.rlim_max) {
473 r.rlim_cur = r.rlim_max;
474 } else {
475 /* Shouldn't happen, so just return the current value. */
476 goto out;
477 }
478
479 if (setrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) {
480 lwan_status_perror("Could not raise maximum number of file "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 482, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
481 "descriptors to %" PRIu64 ". Leaving at "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 482, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
482 "%" PRIu64, r.rlim_max, current)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 482, __FUNCTION__, "Could not raise maximum number of file "
"descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max
, current)
;
483 r.rlim_cur = current;
484 }
485 }
486
487out:
488 return r.rlim_cur;
489}
490
491static inline size_t align_to_size(size_t value, size_t alignment)
492{
493 return (value + alignment - 1) & ~(alignment - 1);
494}
495
496static void allocate_connections(struct lwan *l, size_t max_open_files)
497{
498 const size_t sz = max_open_files * sizeof(struct lwan_connection);
499
500 if (posix_memalign((void **)&l->conns, 64, align_to_size(sz, 64)))
501 lwan_status_critical_perror("aligned_alloc")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 501, __FUNCTION__, "aligned_alloc")
;
502
503 memset(l->conns, 0, sz);
504}
505
506static unsigned short int get_number_of_cpus(void)
507{
508 long n_online_cpus = sysconf(_SC_NPROCESSORS_ONLN_SC_NPROCESSORS_ONLN);
509 if (UNLIKELY(n_online_cpus < 0)__builtin_expect(((n_online_cpus < 0)), (0))) {
510 lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 511, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU"
)
511 "Could not get number of online CPUs, assuming 1 CPU")lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 511, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU"
)
;
512 return 1;
513 }
514 return (unsigned short int)n_online_cpus;
515}
516
517void lwan_init(struct lwan *l) { lwan_init_with_config(l, &default_config); }
1
Calling 'lwan_init_with_config'
518
519const struct lwan_config *lwan_get_default_config(void)
520{
521 return &default_config;
522}
523
524static char *dup_or_null(const char *s)
525{
526 return s ? strdup(s) : NULL((void*)0);
527}
528
529static void lwan_fd_watch_init(struct lwan *l)
530{
531 l->epfd = epoll_create1(EPOLL_CLOEXECEPOLL_CLOEXEC);
532 if (l->epfd < 0)
533 lwan_status_critical_perror("epoll_create1")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 533, __FUNCTION__, "epoll_create1")
;
534}
535
536static void lwan_fd_watch_shutdown(struct lwan *l)
537{
538 close(l->epfd);
539}
540
541void lwan_init_with_config(struct lwan *l, const struct lwan_config *config)
542{
543 /* Load defaults */
544 memset(l, 0, sizeof(*l));
545 memcpy(&l->config, config, sizeof(*config));
546 l->config.listener = dup_or_null(l->config.listener);
547 l->config.config_file_path = dup_or_null(l->config.config_file_path);
548
549 /* Initialize status first, as it is used by other things during
550 * their initialization. */
551 lwan_status_init(l);
552
553 /* These will only print debugging messages. Debug messages are always
554 * printed if we're on a debug build, so the quiet setting will be
555 * respected. */
556 lwan_job_thread_init();
557 lwan_tables_init();
558
559 try_setup_from_config(l, config);
2
Calling 'try_setup_from_config'
560
561 lwan_response_init(l);
562
563 /* Continue initialization as normal. */
564 lwan_status_debug("Initializing lwan web server")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 564, __FUNCTION__, "Initializing lwan web server")
;
565
566 l->n_cpus = get_number_of_cpus();
567 if (!l->config.n_threads) {
568 l->thread.count = l->n_cpus;
569 if (l->thread.count == 1)
570 l->thread.count = 2;
571 } else if (l->config.n_threads > 3 * l->n_cpus) {
572 l->thread.count = (short unsigned int)(l->n_cpus * 3);
573
574 lwan_status_warning("%d threads requested, but only %d online CPUs; "lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 576, __FUNCTION__, "%d threads requested, but only %d online CPUs; "
"capping to %d threads", l->config.n_threads, l->n_cpus
, 3 * l->n_cpus)
575 "capping to %d threads",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 576, __FUNCTION__, "%d threads requested, but only %d online CPUs; "
"capping to %d threads", l->config.n_threads, l->n_cpus
, 3 * l->n_cpus)
576 l->config.n_threads, l->n_cpus, 3 * l->n_cpus)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 576, __FUNCTION__, "%d threads requested, but only %d online CPUs; "
"capping to %d threads", l->config.n_threads, l->n_cpus
, 3 * l->n_cpus)
;
577 } else if (l->config.n_threads > 63) {
578 l->thread.count = 64;
579
580 lwan_status_warning("%d threads requested, but max 64 supported",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 581, __FUNCTION__, "%d threads requested, but max 64 supported"
, l->config.n_threads)
581 l->config.n_threads)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 581, __FUNCTION__, "%d threads requested, but max 64 supported"
, l->config.n_threads)
;
582 } else {
583 l->thread.count = l->config.n_threads;
584 }
585
586 rlim_t max_open_files = setup_open_file_count_limits();
587 allocate_connections(l, (size_t)max_open_files);
588
589 l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count;
590 lwan_status_info("Using %d threads, maximum %d sockets per thread",lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 591, __FUNCTION__, "Using %d threads, maximum %d sockets per thread"
, l->thread.count, l->thread.max_fd)
591 l->thread.count, l->thread.max_fd)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 591, __FUNCTION__, "Using %d threads, maximum %d sockets per thread"
, l->thread.count, l->thread.max_fd)
;
592
593 signal(SIGPIPE13, SIG_IGN((__sighandler_t) 1));
594
595 lwan_readahead_init();
596 lwan_thread_init(l);
597 lwan_socket_init(l);
598 lwan_http_authorize_init();
599 lwan_fd_watch_init(l);
600}
601
602void lwan_shutdown(struct lwan *l)
603{
604 lwan_status_info("Shutting down")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 604, __FUNCTION__, "Shutting down")
;
605
606 free(l->config.listener);
607 free(l->config.error_template);
608 free(l->config.config_file_path);
609
610 lwan_job_thread_shutdown();
611 lwan_thread_shutdown(l);
612
613 lwan_status_debug("Shutting down URL handlers")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 613, __FUNCTION__, "Shutting down URL handlers")
;
614 lwan_trie_destroy(&l->url_map_trie);
615
616 free(l->conns);
617
618 lwan_response_shutdown(l);
619 lwan_tables_shutdown();
620 lwan_status_shutdown(l);
621 lwan_http_authorize_shutdown();
622 lwan_readahead_shutdown();
623 lwan_fd_watch_shutdown(l);
624}
625
626static ALWAYS_INLINEinline __attribute__((always_inline)) unsigned int schedule_client(struct lwan *l, int fd)
627{
628 struct lwan_thread *thread = l->conns[fd].thread;
629
630 lwan_thread_add_client(thread, fd);
631
632 return (unsigned int)(thread - l->thread.threads);
633}
634
635static volatile sig_atomic_t main_socket = -1;
636
637static_assert(sizeof(main_socket) >= sizeof(int),extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(main_socket) >= sizeof
(int)) ? 2 : -1; })]
638 "size of sig_atomic_t > size of int")extern int (*__Static_assert_function (void)) [!!sizeof (struct
{ int __error_if_negative: (sizeof(main_socket) >= sizeof
(int)) ? 2 : -1; })]
;
639
640static void sigint_handler(int signal_number __attribute__((unused)))
641{
642 if (main_socket < 0)
643 return;
644
645 shutdown((int)main_socket, SHUT_RDWRSHUT_RDWR);
646 close((int)main_socket);
647
648 main_socket = -1;
649}
650
651enum herd_accept { HERD_MORE = 0, HERD_GONE = -1, HERD_SHUTDOWN = 1 };
652
653static ALWAYS_INLINEinline __attribute__((always_inline)) enum herd_accept
654accept_one(struct lwan *l, uint64_t *cores)
655{
656 int fd = accept4((int)main_socket, NULL((void*)0), NULL((void*)0), SOCK_NONBLOCKSOCK_NONBLOCK | SOCK_CLOEXECSOCK_CLOEXEC);
657
658 if (LIKELY(fd >= 0)__builtin_expect((!!(fd >= 0)), (1))) {
659 *cores |= UINT64_C(1)1UL<<schedule_client(l, fd);
660
661 return HERD_MORE;
662 }
663
664 switch (errno(*__errno_location ())) {
665 case EAGAIN11:
666 return HERD_GONE;
667
668 case EBADF9:
669 case ECONNABORTED103:
670 case EINVAL22:
671 if (main_socket < 0) {
672 lwan_status_info("Signal 2 (Interrupt) received")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 672, __FUNCTION__, "Signal 2 (Interrupt) received")
;
673 } else {
674 lwan_status_info("Main socket closed for unknown reasons")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 674, __FUNCTION__, "Main socket closed for unknown reasons"
)
;
675 }
676 return HERD_SHUTDOWN;
677
678 default:
679 lwan_status_perror("accept")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 679, __FUNCTION__, "accept")
;
680 return HERD_MORE;
681 }
682}
683
684static int
685accept_connection_coro(struct coro *coro, void *data)
686{
687 struct lwan *l = data;
688 uint64_t cores = 0;
689
690 while (coro_yield(coro, 1) & ~(EPOLLHUPEPOLLHUP | EPOLLRDHUPEPOLLRDHUP | EPOLLERREPOLLERR)) {
691 enum herd_accept ha;
692
693 do {
694 ha = accept_one(l, &cores);
695 } while (ha == HERD_MORE);
696
697 if (UNLIKELY(ha > HERD_MORE)__builtin_expect(((ha > HERD_MORE)), (0)))
698 break;
699
700 if (LIKELY(cores)__builtin_expect((!!(cores)), (1))) {
701 for (unsigned short t = 0; t < l->thread.count; t++) {
702 if (cores & UINT64_C(1)1UL<<t)
703 lwan_thread_nudge(&l->thread.threads[t]);
704 }
705
706 cores = 0;
707 }
708 }
709
710 return 0;
711}
712
713struct lwan_fd_watch *lwan_watch_fd(struct lwan *l,
714 int fd,
715 uint32_t events,
716 coro_function_t coro_fn,
717 void *data)
718{
719 struct lwan_fd_watch *watch;
720
721 watch = malloc(sizeof(*watch));
722 if (!watch)
723 return NULL((void*)0);
724
725 watch->coro = coro_new(&l->switcher, coro_fn, data);
726 if (!watch->coro)
727 goto out;
728
729 struct epoll_event ev = {.events = events, .data.ptr = watch->coro};
730 if (epoll_ctl(l->epfd, EPOLL_CTL_ADD1, fd, &ev) < 0) {
731 coro_free(watch->coro);
732 goto out;
733 }
734
735 watch->fd = fd;
736 return watch;
737
738out:
739 free(watch);
740 return NULL((void*)0);
741}
742
743void lwan_unwatch_fd(struct lwan *l, struct lwan_fd_watch *w)
744{
745 if (l->main_socket != w->fd) {
746 if (epoll_ctl(l->epfd, EPOLL_CTL_DEL2, w->fd, NULL((void*)0)) < 0)
747 lwan_status_perror("Could not unwatch fd %d", w->fd)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 747, __FUNCTION__, "Could not unwatch fd %d", w->fd)
;
748 }
749
750 coro_free(w->coro);
751 free(w);
752}
753
754void lwan_main_loop(struct lwan *l)
755{
756 struct epoll_event evs[16];
757 struct lwan_fd_watch *watch;
758
759 assert(main_socket == -1)((void) sizeof ((main_socket == -1) ? 1 : 0), __extension__ (
{ if (main_socket == -1) ; else __assert_fail ("main_socket == -1"
, "/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 759, __extension__ __PRETTY_FUNCTION__); }))
;
760 main_socket = l->main_socket;
761
762 if (signal(SIGINT2, sigint_handler) == SIG_ERR((__sighandler_t) -1))
763 lwan_status_critical("Could not set signal handler")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 763, __FUNCTION__, "Could not set signal handler")
;
764
765 watch = lwan_watch_fd(l, l->main_socket, EPOLLINEPOLLIN | EPOLLHUPEPOLLHUP | EPOLLRDHUPEPOLLRDHUP,
766 accept_connection_coro, l);
767 if (!watch)
768 lwan_status_critical("Could not watch main socket")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 768, __FUNCTION__, "Could not watch main socket")
;
769
770 lwan_status_info("Ready to serve")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c"
, 770, __FUNCTION__, "Ready to serve")
;
771
772 while (true1) {
773 int n_evs = epoll_wait(l->epfd, evs, N_ELEMENTS(evs)(sizeof(evs) / sizeof(evs[0])), -1);
774
775 if (UNLIKELY(n_evs < 0)__builtin_expect(((n_evs < 0)), (0))) {
776 if (main_socket < 0)
777 break;
778 if (errno(*__errno_location ()) == EINTR4 || errno(*__errno_location ()) == EAGAIN11)
779 continue;
780 break;
781 }
782
783 for (int i = 0; i < n_evs; i++) {
784 if (!coro_resume_value(evs[i].data.ptr, (int)evs[i].events))
785 break;
786 }
787 }
788
789 lwan_unwatch_fd(l, watch);
790}
791
792#ifdef CLOCK_MONOTONIC_COARSE6
793__attribute__((constructor)) static void detect_fastest_monotonic_clock(void)
794{
795 struct timespec ts;
796
797 if (!clock_gettime(CLOCK_MONOTONIC_COARSE6, &ts))
798 monotonic_clock_id = CLOCK_MONOTONIC_COARSE6;
799}
800#endif
801
802void lwan_set_thread_name(const char *name)
803{
804 char thread_name[16];
805 char process_name[PATH_MAX4096];
806 char *tmp;
807 int ret;
808
809 if (proc_pidpath(getpid(), process_name, sizeof(process_name)) < 0)
810 return;
811
812 tmp = strrchr(process_name, '/');
813 if (!tmp)
814 return;
815
816 ret = snprintf(thread_name, sizeof(thread_name), "%s %s", tmp + 1, name);
817 if (ret < 0)
818 return;
819
820 pthread_set_name_np(pthread_self(), thread_name);
821}