File: | lwan.c |
Warning: | line 199, column 17 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 <fcntl.h> | |||
27 | #include <libproc.h> | |||
28 | #include <limits.h> | |||
29 | #include <signal.h> | |||
30 | #include <stdlib.h> | |||
31 | #include <string.h> | |||
32 | #include <sys/resource.h> | |||
33 | #include <sys/socket.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() */ | |||
47 | clockid_t monotonic_clock_id = CLOCK_MONOTONIC1; | |||
48 | ||||
49 | static 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 | ||||
62 | LWAN_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)) | |||
71 | static void *find_handler(const char *name) | |||
72 | { | |||
73 | const struct lwan_handler_info *handler; | |||
74 | ||||
75 | LWAN_SECTION_FOREACH(lwan_handler, handler)for (handler = ({ extern const typeof(*handler) __start_lwan_handler []; __start_lwan_handler; }); handler < ({ extern const typeof (*handler) __stop_lwan_handler[]; __stop_lwan_handler; }); (handler )++) { | |||
76 | if (streq(handler->name, name)) | |||
77 | return handler->handler; | |||
78 | } | |||
79 | ||||
80 | return NULL((void*)0); | |||
81 | } | |||
82 | ||||
83 | __attribute__((no_sanitize_address)) | |||
84 | static const struct lwan_module *find_module(const char *name) | |||
85 | { | |||
86 | const struct lwan_module_info *module; | |||
87 | ||||
88 | LWAN_SECTION_FOREACH(lwan_module, module)for (module = ({ extern const typeof(*module) __start_lwan_module []; __start_lwan_module; }); module < ({ extern const typeof (*module) __stop_lwan_module[]; __stop_lwan_module; }); (module )++) { | |||
89 | if (streq(module->name, name)) | |||
90 | return module->module; | |||
91 | } | |||
92 | ||||
93 | return NULL((void*)0); | |||
94 | } | |||
95 | ||||
96 | static void destroy_urlmap(void *data) | |||
97 | { | |||
98 | struct lwan_url_map *url_map = data; | |||
99 | ||||
100 | if (url_map->module) { | |||
101 | const struct lwan_module *module = url_map->module; | |||
102 | ||||
103 | if (module->destroy) | |||
104 | module->destroy(url_map->data); | |||
105 | } else if (url_map->data && url_map->flags & HANDLER_DATA_IS_HASH_TABLE) { | |||
106 | hash_free(url_map->data); | |||
107 | } | |||
108 | ||||
109 | free(url_map->authorization.realm); | |||
110 | free(url_map->authorization.password_file); | |||
111 | free((char *)url_map->prefix); | |||
112 | free(url_map); | |||
113 | } | |||
114 | ||||
115 | static struct lwan_url_map *add_url_map(struct lwan_trie *t, const char *prefix, | |||
116 | const struct lwan_url_map *map) | |||
117 | { | |||
118 | struct lwan_url_map *copy = malloc(sizeof(*copy)); | |||
119 | ||||
120 | if (!copy) | |||
121 | 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" , 121, __FUNCTION__, "Could not copy URL map"); | |||
122 | ||||
123 | memcpy(copy, map, sizeof(*copy)); | |||
124 | ||||
125 | copy->prefix = strdup(prefix ? prefix : copy->prefix); | |||
126 | if (!copy->prefix) | |||
127 | 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" , 127, __FUNCTION__, "Could not copy URL prefix"); | |||
128 | ||||
129 | copy->prefix_len = strlen(copy->prefix); | |||
130 | lwan_trie_add(t, copy->prefix, copy); | |||
131 | ||||
132 | return copy; | |||
133 | } | |||
134 | ||||
135 | static void parse_listener_prefix_authorization(struct config *c, | |||
136 | const struct config_line *l, | |||
137 | struct lwan_url_map *url_map) | |||
138 | { | |||
139 | if (!streq(l->value, "basic")) { | |||
140 | config_error(c, "Only basic authorization supported"); | |||
141 | return; | |||
142 | } | |||
143 | ||||
144 | memset(&url_map->authorization, 0, sizeof(url_map->authorization)); | |||
145 | ||||
146 | while ((l = config_read_line(c))) { | |||
147 | switch (l->type) { | |||
148 | case CONFIG_LINE_TYPE_LINE: | |||
149 | if (streq(l->key, "realm")) { | |||
150 | free(url_map->authorization.realm); | |||
151 | url_map->authorization.realm = strdup(l->value); | |||
152 | } else if (streq(l->key, "password_file")) { | |||
153 | free(url_map->authorization.password_file); | |||
154 | url_map->authorization.password_file = realpath(l->value, NULL((void*)0)); | |||
155 | if (!url_map->authorization.password_file) | |||
156 | config_error(c, "Could not determine full path for password file: %s", l->value); | |||
157 | } | |||
158 | break; | |||
159 | ||||
160 | case CONFIG_LINE_TYPE_SECTION: | |||
161 | config_error(c, "Unexpected section: %s", l->key); | |||
162 | goto error; | |||
163 | ||||
164 | case CONFIG_LINE_TYPE_SECTION_END: | |||
165 | if (!url_map->authorization.realm) | |||
166 | url_map->authorization.realm = strdup("Lwan"); | |||
167 | if (!url_map->authorization.password_file) | |||
168 | url_map->authorization.password_file = strdup("htpasswd"); | |||
169 | ||||
170 | url_map->flags |= HANDLER_MUST_AUTHORIZE; | |||
171 | return; | |||
172 | } | |||
173 | } | |||
174 | ||||
175 | config_error(c, "Could not find end of authorization section"); | |||
176 | ||||
177 | error: | |||
178 | free(url_map->authorization.realm); | |||
179 | free(url_map->authorization.password_file); | |||
180 | } | |||
181 | ||||
182 | static void parse_listener_prefix(struct config *c, | |||
183 | const struct config_line *l, | |||
184 | struct lwan *lwan, | |||
185 | const struct lwan_module *module, | |||
186 | void *handler) | |||
187 | { | |||
188 | struct lwan_url_map url_map = {}; | |||
189 | struct hash *hash = hash_str_new(free, free); | |||
190 | 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); })); | |||
191 | struct config *isolated; | |||
192 | ||||
193 | isolated = config_isolate_section(c, l); | |||
194 | if (!isolated) { | |||
195 | config_error(c, "Could not isolate configuration file"); | |||
196 | goto out; | |||
197 | } | |||
198 | ||||
199 | while ((l = config_read_line(c))) { | |||
| ||||
200 | switch (l->type) { | |||
201 | case CONFIG_LINE_TYPE_LINE: | |||
202 | hash_add(hash, strdup(l->key), strdup(l->value)); | |||
203 | break; | |||
204 | ||||
205 | case CONFIG_LINE_TYPE_SECTION: | |||
206 | if (streq(l->key, "authorization")) { | |||
207 | parse_listener_prefix_authorization(c, l, &url_map); | |||
208 | } else if (!config_skip_section(c, l)) { | |||
209 | config_error(c, "Could not skip section"); | |||
210 | goto out; | |||
211 | } | |||
212 | break; | |||
213 | ||||
214 | case CONFIG_LINE_TYPE_SECTION_END: | |||
215 | goto add_map; | |||
216 | } | |||
217 | } | |||
218 | ||||
219 | config_error(c, "Expecting section end while parsing prefix"); | |||
220 | goto out; | |||
221 | ||||
222 | add_map: | |||
223 | 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" , 223, __extension__ __PRETTY_FUNCTION__); })); | |||
224 | ||||
225 | if (handler) { | |||
226 | url_map.handler = handler; | |||
227 | url_map.flags |= HANDLER_PARSE_MASK | HANDLER_DATA_IS_HASH_TABLE; | |||
228 | url_map.data = hash; | |||
229 | url_map.module = NULL((void*)0); | |||
230 | ||||
231 | hash = NULL((void*)0); | |||
232 | } else if (module->create_from_hash && module->handle_request) { | |||
233 | url_map.data = module->create_from_hash(prefix, hash); | |||
234 | if (!url_map.data) { | |||
235 | config_error(c, "Could not create module instance"); | |||
236 | goto out; | |||
237 | } | |||
238 | ||||
239 | if (module->parse_conf && !module->parse_conf(url_map.data, isolated)) { | |||
240 | const char *msg = config_last_error(isolated); | |||
241 | ||||
242 | config_error(c, "Error from module: %s", msg ? msg : "Unknown"); | |||
243 | goto out; | |||
244 | } | |||
245 | ||||
246 | url_map.handler = module->handle_request; | |||
247 | url_map.flags |= module->flags; | |||
248 | url_map.module = module; | |||
249 | } else if (UNLIKELY(!module->create_from_hash)__builtin_expect(((!module->create_from_hash)), (0))) { | |||
250 | config_error(c, "Module isn't prepared to load settings from a file; " | |||
251 | "create_from_hash() method isn't present"); | |||
252 | goto out; | |||
253 | } else if (UNLIKELY(!module->handle_request)__builtin_expect(((!module->handle_request)), (0))) { | |||
254 | config_error(c, "Module does not have handle_request() method"); | |||
255 | goto out; | |||
256 | } | |||
257 | ||||
258 | add_url_map(&lwan->url_map_trie, prefix, &url_map); | |||
259 | ||||
260 | out: | |||
261 | hash_free(hash); | |||
262 | config_close(isolated); | |||
263 | } | |||
264 | ||||
265 | void lwan_set_url_map(struct lwan *l, const struct lwan_url_map *map) | |||
266 | { | |||
267 | lwan_trie_destroy(&l->url_map_trie); | |||
268 | if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap))__builtin_expect(((!lwan_trie_init(&l->url_map_trie, destroy_urlmap ))), (0))) | |||
269 | lwan_status_critical_perror("Could not initialize trie")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 269, __FUNCTION__, "Could not initialize trie"); | |||
270 | ||||
271 | for (; map->prefix; map++) { | |||
272 | struct lwan_url_map *copy = add_url_map(&l->url_map_trie, NULL((void*)0), map); | |||
273 | ||||
274 | if (copy->module && copy->module->create) { | |||
275 | copy->data = copy->module->create (map->prefix, copy->args); | |||
276 | copy->flags = copy->module->flags; | |||
277 | copy->handler = copy->module->handle_request; | |||
278 | } else { | |||
279 | copy->flags = HANDLER_PARSE_MASK; | |||
280 | } | |||
281 | } | |||
282 | } | |||
283 | ||||
284 | static void parse_listener(struct config *c, | |||
285 | const struct config_line *l, | |||
286 | struct lwan *lwan) | |||
287 | { | |||
288 | free(lwan->config.listener); | |||
289 | lwan->config.listener = strdup(l->value); | |||
290 | ||||
291 | while ((l = config_read_line(c))) { | |||
292 | switch (l->type) { | |||
293 | case CONFIG_LINE_TYPE_LINE: | |||
294 | config_error(c, "Expecting prefix section"); | |||
295 | return; | |||
296 | case CONFIG_LINE_TYPE_SECTION: | |||
297 | if (l->key[0] == '&') { | |||
298 | void *handler = find_handler(l->key + 1); | |||
299 | if (handler) { | |||
300 | parse_listener_prefix(c, l, lwan, NULL((void*)0), handler); | |||
301 | continue; | |||
302 | } | |||
303 | ||||
304 | config_error(c, "Could not find handler name: %s", l->key + 1); | |||
305 | return; | |||
306 | } | |||
307 | ||||
308 | const struct lwan_module *module = find_module(l->key); | |||
309 | if (module) { | |||
310 | parse_listener_prefix(c, l, lwan, module, NULL((void*)0)); | |||
311 | continue; | |||
312 | } | |||
313 | ||||
314 | config_error(c, "Invalid section or module not found: %s", l->key); | |||
315 | return; | |||
316 | case CONFIG_LINE_TYPE_SECTION_END: | |||
317 | return; | |||
318 | } | |||
319 | } | |||
320 | ||||
321 | config_error(c, "Expecting section end while parsing listener"); | |||
322 | } | |||
323 | ||||
324 | const char *lwan_get_config_path(char *path_buf, size_t path_buf_len) | |||
325 | { | |||
326 | char buffer[PATH_MAX4096]; | |||
327 | ||||
328 | if (proc_pidpath(getpid(), buffer, sizeof(buffer)) < 0) | |||
329 | goto out; | |||
330 | ||||
331 | char *path = strrchr(buffer, '/'); | |||
332 | if (!path) | |||
333 | goto out; | |||
334 | int ret = snprintf(path_buf, path_buf_len, "%s.conf", path + 1); | |||
335 | if (ret < 0 || ret >= (int)path_buf_len) | |||
336 | goto out; | |||
337 | ||||
338 | return path_buf; | |||
339 | ||||
340 | out: | |||
341 | return "lwan.conf"; | |||
342 | } | |||
343 | ||||
344 | static bool_Bool setup_from_config(struct lwan *lwan, const char *path) | |||
345 | { | |||
346 | const struct config_line *line; | |||
347 | struct config *conf; | |||
348 | bool_Bool has_listener = false0; | |||
349 | char path_buf[PATH_MAX4096]; | |||
350 | ||||
351 | if (!path
| |||
352 | path = lwan_get_config_path(path_buf, sizeof(path_buf)); | |||
353 | lwan_status_info("Loading configuration file: %s", path)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 353, __FUNCTION__, "Loading configuration file: %s", path); | |||
354 | ||||
355 | conf = config_open(path); | |||
356 | if (!conf) | |||
357 | return false0; | |||
358 | ||||
359 | if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap)) | |||
360 | return false0; | |||
361 | ||||
362 | while ((line = config_read_line(conf))) { | |||
363 | switch (line->type) { | |||
364 | case CONFIG_LINE_TYPE_LINE: | |||
365 | if (streq(line->key, "keep_alive_timeout")) { | |||
366 | lwan->config.keep_alive_timeout = (unsigned int)parse_long( | |||
367 | line->value, default_config.keep_alive_timeout); | |||
368 | } else if (streq(line->key, "quiet")) { | |||
369 | lwan->config.quiet = | |||
370 | parse_bool(line->value, default_config.quiet); | |||
371 | } else if (streq(line->key, "reuse_port")) { | |||
372 | lwan->config.reuse_port = | |||
373 | parse_bool(line->value, default_config.reuse_port); | |||
374 | } else if (streq(line->key, "proxy_protocol")) { | |||
375 | lwan->config.proxy_protocol = | |||
376 | parse_bool(line->value, default_config.proxy_protocol); | |||
377 | } else if (streq(line->key, "allow_cors")) { | |||
378 | lwan->config.allow_cors = | |||
379 | parse_bool(line->value, default_config.allow_cors); | |||
380 | } else if (streq(line->key, "expires")) { | |||
381 | lwan->config.expires = | |||
382 | parse_time_period(line->value, default_config.expires); | |||
383 | } else if (streq(line->key, "error_template")) { | |||
384 | free(lwan->config.error_template); | |||
385 | lwan->config.error_template = strdup(line->value); | |||
386 | } else if (streq(line->key, "threads")) { | |||
387 | long n_threads = | |||
388 | parse_long(line->value, default_config.n_threads); | |||
389 | if (n_threads < 0) | |||
390 | config_error(conf, "Invalid number of threads: %ld", | |||
391 | n_threads); | |||
392 | lwan->config.n_threads = (unsigned int)n_threads; | |||
393 | } else if (streq(line->key, "max_post_data_size")) { | |||
394 | long max_post_data_size = parse_long( | |||
395 | line->value, (long)default_config.max_post_data_size); | |||
396 | if (max_post_data_size < 0) | |||
397 | config_error(conf, "Negative maximum post data size"); | |||
398 | else if (max_post_data_size > 128 * (1 << 20)) | |||
399 | config_error(conf, | |||
400 | "Maximum post data can't be over 128MiB"); | |||
401 | lwan->config.max_post_data_size = (size_t)max_post_data_size; | |||
402 | } else if (streq(line->key, "allow_temp_files")) { | |||
403 | lwan->config.allow_post_temp_file = | |||
404 | !!strstr(line->value, "post"); | |||
405 | } else { | |||
406 | config_error(conf, "Unknown config key: %s", line->key); | |||
407 | } | |||
408 | break; | |||
409 | case CONFIG_LINE_TYPE_SECTION: | |||
410 | if (streq(line->key, "listener")) { | |||
411 | if (!has_listener
| |||
412 | parse_listener(conf, line, lwan); | |||
413 | has_listener = true1; | |||
414 | } else { | |||
415 | config_error(conf, "Only one listener supported"); | |||
416 | } | |||
417 | } else if (streq(line->key, "straitjacket")) { | |||
418 | lwan_straitjacket_enforce_from_config(conf); | |||
419 | } else { | |||
420 | config_error(conf, "Unknown section type: %s", line->key); | |||
421 | } | |||
422 | break; | |||
423 | case CONFIG_LINE_TYPE_SECTION_END: | |||
424 | config_error(conf, "Unexpected section end"); | |||
425 | } | |||
426 | } | |||
427 | ||||
428 | if (config_last_error(conf)) { | |||
429 | 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" , 430, __FUNCTION__, "Error on config file \"%s\", line %d: %s" , path, config_cur_line(conf), config_last_error(conf)) | |||
430 | config_cur_line(conf), config_last_error(conf))lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 430, __FUNCTION__, "Error on config file \"%s\", line %d: %s" , path, config_cur_line(conf), config_last_error(conf)); | |||
431 | } | |||
432 | ||||
433 | config_close(conf); | |||
434 | ||||
435 | return true1; | |||
436 | } | |||
437 | ||||
438 | static void try_setup_from_config(struct lwan *l, | |||
439 | const struct lwan_config *config) | |||
440 | { | |||
441 | if (!setup_from_config(l, config->config_file_path)) { | |||
442 | if (config->config_file_path) { | |||
443 | lwan_status_critical("Could not read config file: %s",lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 444, __FUNCTION__, "Could not read config file: %s", config ->config_file_path) | |||
444 | config->config_file_path)lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 444, __FUNCTION__, "Could not read config file: %s", config ->config_file_path); | |||
445 | } | |||
446 | } | |||
447 | ||||
448 | lwan_status_init(l); /* `quiet` key might have changed value. */ | |||
449 | ||||
450 | l->config.request_flags = | |||
451 | (l->config.proxy_protocol ? REQUEST_ALLOW_PROXY_REQS : 0) | | |||
452 | (l->config.allow_cors ? REQUEST_ALLOW_CORS : 0); | |||
453 | } | |||
454 | ||||
455 | static rlim_t setup_open_file_count_limits(void) | |||
456 | { | |||
457 | struct rlimit r; | |||
458 | ||||
459 | if (getrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) { | |||
460 | 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" , 462, __FUNCTION__, "Could not obtain maximum number of file " "descriptors. Assuming %d", 256) | |||
461 | "descriptors. Assuming %d",lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 462, __FUNCTION__, "Could not obtain maximum number of file " "descriptors. Assuming %d", 256) | |||
462 | OPEN_MAX)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 462, __FUNCTION__, "Could not obtain maximum number of file " "descriptors. Assuming %d", 256); | |||
463 | return OPEN_MAX256; | |||
464 | } | |||
465 | ||||
466 | if (r.rlim_max != r.rlim_cur) { | |||
467 | const rlim_t current = r.rlim_cur; | |||
468 | ||||
469 | if (r.rlim_max == RLIM_INFINITY0xffffffffffffffffuLL && r.rlim_cur < OPEN_MAX256) { | |||
470 | r.rlim_cur = OPEN_MAX256; | |||
471 | } else if (r.rlim_cur < r.rlim_max) { | |||
472 | r.rlim_cur = r.rlim_max; | |||
473 | } else { | |||
474 | /* Shouldn't happen, so just return the current value. */ | |||
475 | goto out; | |||
476 | } | |||
477 | ||||
478 | if (setrlimit(RLIMIT_NOFILERLIMIT_NOFILE, &r) < 0) { | |||
479 | 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" , 481, __FUNCTION__, "Could not raise maximum number of file " "descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max , current) | |||
480 | "descriptors to %" PRIu64 ". Leaving at "lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 481, __FUNCTION__, "Could not raise maximum number of file " "descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max , current) | |||
481 | "%" PRIu64, r.rlim_max, current)lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 481, __FUNCTION__, "Could not raise maximum number of file " "descriptors to %" "l" "u" ". Leaving at " "%" "l" "u", r.rlim_max , current); | |||
482 | r.rlim_cur = current; | |||
483 | } | |||
484 | } | |||
485 | ||||
486 | out: | |||
487 | return r.rlim_cur; | |||
488 | } | |||
489 | ||||
490 | static void allocate_connections(struct lwan *l, size_t max_open_files) | |||
491 | { | |||
492 | const size_t sz = max_open_files * sizeof(struct lwan_connection); | |||
493 | ||||
494 | l->conns = lwan_aligned_alloc(sz, 64); | |||
495 | if (UNLIKELY(!l->conns)__builtin_expect(((!l->conns)), (0))) | |||
496 | lwan_status_critical_perror("lwan_alloc_aligned")lwan_status_critical_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 496, __FUNCTION__, "lwan_alloc_aligned"); | |||
497 | ||||
498 | memset(l->conns, 0, sz); | |||
499 | } | |||
500 | ||||
501 | static unsigned int get_number_of_cpus(void) | |||
502 | { | |||
503 | long n_online_cpus = sysconf(_SC_NPROCESSORS_ONLN_SC_NPROCESSORS_ONLN); | |||
504 | ||||
505 | if (UNLIKELY(n_online_cpus < 0)__builtin_expect(((n_online_cpus < 0)), (0))) { | |||
506 | lwan_status_warning(lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 507, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU" ) | |||
507 | "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" , 507, __FUNCTION__, "Could not get number of online CPUs, assuming 1 CPU" ); | |||
508 | return 1; | |||
509 | } | |||
510 | ||||
511 | return (unsigned int)n_online_cpus; | |||
512 | } | |||
513 | ||||
514 | void lwan_init(struct lwan *l) { lwan_init_with_config(l, &default_config); } | |||
| ||||
515 | ||||
516 | const struct lwan_config *lwan_get_default_config(void) | |||
517 | { | |||
518 | return &default_config; | |||
519 | } | |||
520 | ||||
521 | static char *dup_or_null(const char *s) | |||
522 | { | |||
523 | return s ? strdup(s) : NULL((void*)0); | |||
524 | } | |||
525 | ||||
526 | void lwan_init_with_config(struct lwan *l, const struct lwan_config *config) | |||
527 | { | |||
528 | /* Load defaults */ | |||
529 | memset(l, 0, sizeof(*l)); | |||
530 | memcpy(&l->config, config, sizeof(*config)); | |||
531 | l->config.listener = dup_or_null(l->config.listener); | |||
532 | l->config.config_file_path = dup_or_null(l->config.config_file_path); | |||
533 | ||||
534 | /* Initialize status first, as it is used by other things during | |||
535 | * their initialization. */ | |||
536 | lwan_status_init(l); | |||
537 | ||||
538 | /* These will only print debugging messages. Debug messages are always | |||
539 | * printed if we're on a debug build, so the quiet setting will be | |||
540 | * respected. */ | |||
541 | lwan_job_thread_init(); | |||
542 | lwan_tables_init(); | |||
543 | ||||
544 | try_setup_from_config(l, config); | |||
545 | ||||
546 | lwan_response_init(l); | |||
547 | ||||
548 | /* Continue initialization as normal. */ | |||
549 | lwan_status_debug("Initializing lwan web server")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 549, __FUNCTION__, "Initializing lwan web server"); | |||
550 | ||||
551 | l->n_cpus = get_number_of_cpus(); | |||
552 | if (!l->config.n_threads) { | |||
553 | l->thread.count = l->n_cpus; | |||
554 | if (l->thread.count == 1) | |||
555 | l->thread.count = 2; | |||
556 | } else if (l->config.n_threads > 3 * l->n_cpus) { | |||
557 | l->thread.count = l->n_cpus * 3; | |||
558 | ||||
559 | 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" , 561, __FUNCTION__, "%d threads requested, but only %d online CPUs; " "capping to %d threads", l->config.n_threads, l->n_cpus , 3 * l->n_cpus) | |||
560 | "capping to %d threads",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 561, __FUNCTION__, "%d threads requested, but only %d online CPUs; " "capping to %d threads", l->config.n_threads, l->n_cpus , 3 * l->n_cpus) | |||
561 | 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" , 561, __FUNCTION__, "%d threads requested, but only %d online CPUs; " "capping to %d threads", l->config.n_threads, l->n_cpus , 3 * l->n_cpus); | |||
562 | } else if (l->config.n_threads > 255) { | |||
563 | l->thread.count = 256; | |||
564 | ||||
565 | lwan_status_warning("%d threads requested, but max 256 supported",lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 566, __FUNCTION__, "%d threads requested, but max 256 supported" , l->config.n_threads) | |||
566 | l->config.n_threads)lwan_status_warning_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 566, __FUNCTION__, "%d threads requested, but max 256 supported" , l->config.n_threads); | |||
567 | } else { | |||
568 | l->thread.count = l->config.n_threads; | |||
569 | } | |||
570 | ||||
571 | rlim_t max_open_files = setup_open_file_count_limits(); | |||
572 | allocate_connections(l, (size_t)max_open_files); | |||
573 | ||||
574 | l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count; | |||
575 | 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" , 576, __FUNCTION__, "Using %d threads, maximum %d sockets per thread" , l->thread.count, l->thread.max_fd) | |||
576 | l->thread.count, l->thread.max_fd)lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 576, __FUNCTION__, "Using %d threads, maximum %d sockets per thread" , l->thread.count, l->thread.max_fd); | |||
577 | ||||
578 | signal(SIGPIPE13, SIG_IGN((__sighandler_t) 1)); | |||
579 | ||||
580 | lwan_readahead_init(); | |||
581 | lwan_thread_init(l); | |||
582 | lwan_socket_init(l); | |||
583 | lwan_http_authorize_init(); | |||
584 | } | |||
585 | ||||
586 | void lwan_shutdown(struct lwan *l) | |||
587 | { | |||
588 | lwan_status_info("Shutting down")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 588, __FUNCTION__, "Shutting down"); | |||
589 | ||||
590 | free(l->config.listener); | |||
591 | free(l->config.error_template); | |||
592 | free(l->config.config_file_path); | |||
593 | ||||
594 | lwan_job_thread_shutdown(); | |||
595 | lwan_thread_shutdown(l); | |||
596 | ||||
597 | lwan_status_debug("Shutting down URL handlers")lwan_status_debug_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 597, __FUNCTION__, "Shutting down URL handlers"); | |||
598 | lwan_trie_destroy(&l->url_map_trie); | |||
599 | ||||
600 | free(l->conns); | |||
601 | ||||
602 | lwan_response_shutdown(l); | |||
603 | lwan_tables_shutdown(); | |||
604 | lwan_status_shutdown(l); | |||
605 | lwan_http_authorize_shutdown(); | |||
606 | lwan_readahead_shutdown(); | |||
607 | } | |||
608 | ||||
609 | static ALWAYS_INLINEinline __attribute__((always_inline)) int schedule_client(struct lwan *l, int fd) | |||
610 | { | |||
611 | struct lwan_thread *thread = l->conns[fd].thread; | |||
612 | ||||
613 | lwan_thread_add_client(thread, fd); | |||
614 | ||||
615 | return (int)(thread - l->thread.threads); | |||
616 | } | |||
617 | ||||
618 | static volatile sig_atomic_t main_socket = -1; | |||
619 | ||||
620 | static_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; })] | |||
621 | "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; })]; | |||
622 | ||||
623 | static void sigint_handler(int signal_number __attribute__((unused))) | |||
624 | { | |||
625 | if (main_socket < 0) | |||
626 | return; | |||
627 | ||||
628 | shutdown((int)main_socket, SHUT_RDWRSHUT_RDWR); | |||
629 | close((int)main_socket); | |||
630 | ||||
631 | main_socket = -1; | |||
632 | } | |||
633 | ||||
634 | enum herd_accept { HERD_MORE = 0, HERD_GONE = -1, HERD_SHUTDOWN = 1 }; | |||
635 | ||||
636 | struct core_bitmap { | |||
637 | uint64_t bitmap[4]; | |||
638 | }; | |||
639 | ||||
640 | static ALWAYS_INLINEinline __attribute__((always_inline)) enum herd_accept | |||
641 | accept_one(struct lwan *l, struct core_bitmap *cores) | |||
642 | { | |||
643 | int fd = accept4((int)main_socket, NULL((void*)0), NULL((void*)0), SOCK_NONBLOCKSOCK_NONBLOCK | SOCK_CLOEXECSOCK_CLOEXEC); | |||
644 | ||||
645 | if (LIKELY(fd >= 0)__builtin_expect((!!(fd >= 0)), (1))) { | |||
646 | int core = schedule_client(l, fd); | |||
647 | ||||
648 | cores->bitmap[core / 64] |= UINT64_C(1)1UL<<(core % 64); | |||
649 | ||||
650 | return HERD_MORE; | |||
651 | } | |||
652 | ||||
653 | switch (errno(*__errno_location ())) { | |||
654 | case EAGAIN11: | |||
655 | return HERD_GONE; | |||
656 | ||||
657 | case EBADF9: | |||
658 | case ECONNABORTED103: | |||
659 | case EINVAL22: | |||
660 | if (main_socket < 0) { | |||
661 | lwan_status_info("Signal 2 (Interrupt) received")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 661, __FUNCTION__, "Signal 2 (Interrupt) received"); | |||
662 | } else { | |||
663 | lwan_status_info("Main socket closed for unknown reasons")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 663, __FUNCTION__, "Main socket closed for unknown reasons" ); | |||
664 | } | |||
665 | return HERD_SHUTDOWN; | |||
666 | ||||
667 | default: | |||
668 | lwan_status_perror("accept")lwan_status_perror_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 668, __FUNCTION__, "accept"); | |||
669 | return HERD_MORE; | |||
670 | } | |||
671 | } | |||
672 | ||||
673 | void lwan_main_loop(struct lwan *l) | |||
674 | { | |||
675 | struct core_bitmap cores = {}; | |||
676 | ||||
677 | 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" , 677, __extension__ __PRETTY_FUNCTION__); })); | |||
678 | main_socket = l->main_socket; | |||
679 | ||||
680 | if (signal(SIGINT2, sigint_handler) == SIG_ERR((__sighandler_t) -1)) | |||
681 | lwan_status_critical("Could not set signal handler")lwan_status_critical_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 681, __FUNCTION__, "Could not set signal handler"); | |||
682 | ||||
683 | lwan_status_info("Ready to serve")lwan_status_info_debug("/home/buildbot/lwan-worker/clang-analyze/build/src/lib/lwan.c" , 683, __FUNCTION__, "Ready to serve"); | |||
684 | ||||
685 | while (true1) { | |||
686 | enum herd_accept ha; | |||
687 | ||||
688 | fcntl(l->main_socket, F_SETFL4, 0); | |||
689 | ha = accept_one(l, &cores); | |||
690 | if (ha == HERD_MORE) { | |||
691 | fcntl(l->main_socket, F_SETFL4, O_NONBLOCK04000); | |||
692 | ||||
693 | do { | |||
694 | ha = accept_one(l, &cores); | |||
695 | } while (ha == HERD_MORE); | |||
696 | } | |||
697 | ||||
698 | if (UNLIKELY(ha > HERD_MORE)__builtin_expect(((ha > HERD_MORE)), (0))) | |||
699 | break; | |||
700 | ||||
701 | for (size_t i = 0; i < N_ELEMENTS(cores.bitmap)((!sizeof(char[1 - 2 * __builtin_types_compatible_p( __typeof__ (cores.bitmap), __typeof__(&(cores.bitmap)[0]))])) | sizeof (cores.bitmap) / sizeof(cores.bitmap[0])); i++) { | |||
702 | for (uint64_t c = cores.bitmap[i]; c; c ^= c & -c) { | |||
703 | size_t core = (size_t)__builtin_ctzl(c); | |||
704 | lwan_thread_nudge(&l->thread.threads[i * 64 + core]); | |||
705 | } | |||
706 | } | |||
707 | cores = (struct core_bitmap){}; | |||
708 | } | |||
709 | } | |||
710 | ||||
711 | #ifdef CLOCK_MONOTONIC_COARSE6 | |||
712 | __attribute__((constructor)) static void detect_fastest_monotonic_clock(void) | |||
713 | { | |||
714 | struct timespec ts; | |||
715 | ||||
716 | if (!clock_gettime(CLOCK_MONOTONIC_COARSE6, &ts)) | |||
717 | monotonic_clock_id = CLOCK_MONOTONIC_COARSE6; | |||
718 | } | |||
719 | #endif | |||
720 | ||||
721 | void lwan_set_thread_name(const char *name) | |||
722 | { | |||
723 | char thread_name[16]; | |||
724 | char process_name[PATH_MAX4096]; | |||
725 | char *tmp; | |||
726 | int ret; | |||
727 | ||||
728 | if (proc_pidpath(getpid(), process_name, sizeof(process_name)) < 0) | |||
729 | return; | |||
730 | ||||
731 | tmp = strrchr(process_name, '/'); | |||
732 | if (!tmp) | |||
733 | return; | |||
734 | ||||
735 | ret = snprintf(thread_name, sizeof(thread_name), "%s %s", tmp + 1, name); | |||
736 | if (ret < 0) | |||
737 | return; | |||
738 | ||||
739 | pthread_set_name_np(pthread_self(), thread_name); | |||
740 | } |