Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * lwan - web server |
3 | | * Copyright (c) 2012, 2013 L. A. F. Pereira <l@tia.mat.br> |
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.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(LWAN_HAVE_LUA) |
43 | | #include "lwan-lua.h" |
44 | | #endif |
45 | | |
46 | | /* Ideally, this would check if all items in enum lwan_request_flags, |
47 | | * when bitwise-or'd together, would not have have any bit set that |
48 | | * is also set in REQUEST_METHOD_MASK. */ |
49 | | static_assert(REQUEST_ACCEPT_DEFLATE > REQUEST_METHOD_MASK, |
50 | | "enough bits to store request methods"); |
51 | | |
52 | | /* See detect_fastest_monotonic_clock() */ |
53 | | clockid_t monotonic_clock_id = CLOCK_MONOTONIC; |
54 | | |
55 | | static const struct lwan_config default_config = { |
56 | | .listener = "localhost:8080", |
57 | | .keep_alive_timeout = 15, |
58 | | .quiet = false, |
59 | | .proxy_protocol = false, |
60 | | .allow_cors = false, |
61 | | .expires = 1 * ONE_WEEK, |
62 | | .n_threads = 0, |
63 | | .max_post_data_size = 10 * DEFAULT_BUFFER_SIZE, |
64 | | .allow_post_temp_file = false, |
65 | | .max_put_data_size = 10 * DEFAULT_BUFFER_SIZE, |
66 | | .allow_put_temp_file = false, |
67 | | .use_dynamic_buffer = false, |
68 | | }; |
69 | | |
70 | | LWAN_HANDLER_ROUTE(brew_coffee, NULL /* do not autodetect this route */) |
71 | 0 | { |
72 | | /* Placeholder handler so that __start_lwan_handler and __stop_lwan_handler |
73 | | * symbols will get defined. |
74 | | */ |
75 | 0 | return HTTP_I_AM_A_TEAPOT; |
76 | 0 | } |
77 | | |
78 | | __attribute__((no_sanitize_address)) |
79 | | static void *find_handler(const char *name) |
80 | 0 | { |
81 | 0 | const struct lwan_handler_info *handler; |
82 | |
|
83 | 0 | LWAN_SECTION_FOREACH(lwan_handler, handler) { |
84 | 0 | if (streq(handler->name, name)) |
85 | 0 | return handler->handler; |
86 | 0 | } |
87 | | |
88 | 0 | return NULL; |
89 | 0 | } |
90 | | |
91 | | __attribute__((no_sanitize_address)) |
92 | | static const struct lwan_module *find_module(const char *name) |
93 | 0 | { |
94 | 0 | const struct lwan_module_info *module; |
95 | |
|
96 | 0 | LWAN_SECTION_FOREACH(lwan_module, module) { |
97 | 0 | if (streq(module->name, name)) |
98 | 0 | return module->module; |
99 | 0 | } |
100 | | |
101 | 0 | return NULL; |
102 | 0 | } |
103 | | |
104 | | static void destroy_urlmap(void *data) |
105 | 0 | { |
106 | 0 | struct lwan_url_map *url_map = data; |
107 | |
|
108 | 0 | if (url_map->module) { |
109 | 0 | const struct lwan_module *module = url_map->module; |
110 | |
|
111 | 0 | if (module->destroy) |
112 | 0 | module->destroy(url_map->data); |
113 | 0 | } else if (url_map->data && url_map->flags & HANDLER_DATA_IS_HASH_TABLE) { |
114 | 0 | hash_free(url_map->data); |
115 | 0 | } |
116 | |
|
117 | 0 | free(url_map->authorization.realm); |
118 | 0 | free(url_map->authorization.password_file); |
119 | 0 | free((char *)url_map->prefix); |
120 | 0 | free(url_map); |
121 | 0 | } |
122 | | |
123 | | static struct lwan_url_map *add_url_map(struct lwan_trie *t, const char *prefix, |
124 | | const struct lwan_url_map *map) |
125 | 0 | { |
126 | 0 | struct lwan_url_map *copy = malloc(sizeof(*copy)); |
127 | |
|
128 | 0 | if (!copy) |
129 | 0 | lwan_status_critical_perror("Could not copy URL map"); |
130 | | |
131 | 0 | memcpy(copy, map, sizeof(*copy)); |
132 | |
|
133 | 0 | copy->prefix = strdup(prefix ? prefix : copy->prefix); |
134 | 0 | if (!copy->prefix) |
135 | 0 | lwan_status_critical_perror("Could not copy URL prefix"); |
136 | | |
137 | 0 | copy->prefix_len = strlen(copy->prefix); |
138 | 0 | lwan_trie_add(t, copy->prefix, copy); |
139 | |
|
140 | 0 | return copy; |
141 | 0 | } |
142 | | |
143 | | static bool can_override_header(const char *name) |
144 | 0 | { |
145 | | /* NOTE: Update lwan_prepare_response_header_full() in lwan-response.c |
146 | | * if new headers are added here. */ |
147 | |
|
148 | 0 | if (strcaseequal_neutral(name, "Date")) |
149 | 0 | return false; |
150 | 0 | if (strcaseequal_neutral(name, "Expires")) |
151 | 0 | return false; |
152 | 0 | if (strcaseequal_neutral(name, "WWW-Authenticate")) |
153 | 0 | return false; |
154 | 0 | if (strcaseequal_neutral(name, "Connection")) |
155 | 0 | return false; |
156 | 0 | if (strcaseequal_neutral(name, "Content-Type")) |
157 | 0 | return false; |
158 | 0 | if (strcaseequal_neutral(name, "Transfer-Encoding")) |
159 | 0 | return false; |
160 | 0 | if (strcaseequal_neutral_len(name, "Access-Control-Allow-", |
161 | 0 | sizeof("Access-Control-Allow-") - 1)) |
162 | 0 | return false; |
163 | | |
164 | 0 | return true; |
165 | 0 | } |
166 | | |
167 | | static void build_response_headers(struct lwan *l, |
168 | | const struct lwan_key_value *kv) |
169 | 0 | { |
170 | 0 | struct lwan_strbuf strbuf; |
171 | 0 | bool set_server = false; |
172 | |
|
173 | 0 | assert(l); |
174 | | |
175 | 0 | lwan_strbuf_init(&strbuf); |
176 | |
|
177 | 0 | for (; kv && kv->key; kv++) { |
178 | 0 | if (!can_override_header(kv->key)) { |
179 | 0 | lwan_status_warning("Cannot override header '%s'", kv->key); |
180 | 0 | } else { |
181 | 0 | if (strcaseequal_neutral(kv->key, "Server")) |
182 | 0 | set_server = true; |
183 | |
|
184 | 0 | lwan_strbuf_append_printf(&strbuf, "\r\n%s: %s", kv->key, |
185 | 0 | kv->value); |
186 | 0 | } |
187 | 0 | } |
188 | |
|
189 | 0 | if (!set_server) |
190 | 0 | lwan_strbuf_append_strz(&strbuf, "\r\nServer: lwan"); |
191 | |
|
192 | 0 | lwan_strbuf_append_strz(&strbuf, "\r\n\r\n"); |
193 | |
|
194 | 0 | l->headers = (struct lwan_value){.value = lwan_strbuf_get_buffer(&strbuf), |
195 | 0 | .len = lwan_strbuf_get_length(&strbuf)}; |
196 | 0 | } |
197 | | |
198 | | static void parse_global_headers(struct config *c, |
199 | | struct lwan *lwan) |
200 | 0 | { |
201 | 0 | struct lwan_key_value_array hdrs; |
202 | 0 | const struct config_line *l; |
203 | 0 | struct lwan_key_value *kv; |
204 | |
|
205 | 0 | lwan_key_value_array_init(&hdrs); |
206 | |
|
207 | 0 | while ((l = config_read_line(c))) { |
208 | 0 | switch (l->type) { |
209 | 0 | case CONFIG_LINE_TYPE_SECTION: |
210 | 0 | config_error( |
211 | 0 | c, "No sections are supported under the 'headers' section"); |
212 | 0 | goto cleanup; |
213 | | |
214 | 0 | case CONFIG_LINE_TYPE_LINE: |
215 | 0 | kv = lwan_key_value_array_append(&hdrs); |
216 | 0 | if (!kv) { |
217 | 0 | lwan_status_critical_perror( |
218 | 0 | "Could not allocate memory for custom response header"); |
219 | 0 | } |
220 | | |
221 | 0 | kv->key = strdup(l->key); |
222 | 0 | if (!kv->key) { |
223 | 0 | lwan_status_critical_perror( |
224 | 0 | "Could not allocate memory for custom response header"); |
225 | 0 | } |
226 | | |
227 | 0 | kv->value = strdup(l->value); |
228 | 0 | if (!kv->value) { |
229 | 0 | lwan_status_critical_perror( |
230 | 0 | "Could not allocate memory for custom response header"); |
231 | 0 | } |
232 | 0 | break; |
233 | | |
234 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
235 | 0 | kv = lwan_key_value_array_append(&hdrs); |
236 | 0 | if (!kv) { |
237 | 0 | lwan_status_critical_perror( |
238 | 0 | "Could not allocate memory for custom response header"); |
239 | 0 | } |
240 | | |
241 | 0 | kv->key = NULL; |
242 | 0 | kv->value = NULL; |
243 | |
|
244 | 0 | build_response_headers(lwan, lwan_key_value_array_get_array(&hdrs)); |
245 | 0 | goto cleanup; |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | 0 | config_error(c, "EOF while looking for end of 'headers' section"); |
250 | |
|
251 | 0 | cleanup: |
252 | 0 | LWAN_ARRAY_FOREACH (&hdrs, kv) { |
253 | 0 | free(kv->key); |
254 | 0 | free(kv->value); |
255 | 0 | } |
256 | 0 | lwan_key_value_array_reset(&hdrs); |
257 | 0 | } |
258 | | |
259 | | static void parse_listener_prefix_authorization(struct config *c, |
260 | | const struct config_line *l, |
261 | | struct lwan_url_map *url_map) |
262 | 0 | { |
263 | 0 | if (!streq(l->value, "basic")) { |
264 | 0 | config_error(c, "Only basic authorization supported"); |
265 | 0 | return; |
266 | 0 | } |
267 | | |
268 | 0 | memset(&url_map->authorization, 0, sizeof(url_map->authorization)); |
269 | |
|
270 | 0 | while ((l = config_read_line(c))) { |
271 | 0 | switch (l->type) { |
272 | 0 | case CONFIG_LINE_TYPE_LINE: |
273 | 0 | if (streq(l->key, "realm")) { |
274 | 0 | free(url_map->authorization.realm); |
275 | 0 | url_map->authorization.realm = strdup(l->value); |
276 | 0 | } else if (streq(l->key, "password_file")) { |
277 | 0 | free(url_map->authorization.password_file); |
278 | 0 | url_map->authorization.password_file = realpath(l->value, NULL); |
279 | 0 | if (!url_map->authorization.password_file) |
280 | 0 | config_error(c, "Could not determine full path for password file: %s", l->value); |
281 | 0 | } |
282 | 0 | break; |
283 | | |
284 | 0 | case CONFIG_LINE_TYPE_SECTION: |
285 | 0 | config_error(c, "Unexpected section: %s", l->key); |
286 | 0 | goto error; |
287 | | |
288 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
289 | 0 | if (!url_map->authorization.realm) |
290 | 0 | url_map->authorization.realm = strdup("Lwan"); |
291 | 0 | if (!url_map->authorization.password_file) |
292 | 0 | url_map->authorization.password_file = strdup("htpasswd"); |
293 | |
|
294 | 0 | url_map->flags |= HANDLER_MUST_AUTHORIZE; |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | 0 | config_error(c, "Could not find end of authorization section"); |
300 | |
|
301 | 0 | error: |
302 | 0 | free(url_map->authorization.realm); |
303 | 0 | free(url_map->authorization.password_file); |
304 | 0 | } |
305 | | |
306 | | __attribute__((no_sanitize_address)) |
307 | | static const char *get_module_name(const struct lwan_module *module) |
308 | 0 | { |
309 | 0 | const struct lwan_module_info *iter; |
310 | |
|
311 | 0 | LWAN_SECTION_FOREACH(lwan_module, iter) { |
312 | 0 | if (iter->module == module) |
313 | 0 | return iter->name; |
314 | 0 | } |
315 | | |
316 | 0 | return "<unknown>"; |
317 | 0 | } |
318 | | |
319 | | static void parse_listener_prefix(struct config *c, |
320 | | const struct config_line *l, |
321 | | struct lwan *lwan, |
322 | | const struct lwan_module *module, |
323 | | void *handler) |
324 | 0 | { |
325 | 0 | struct lwan_url_map url_map = {}; |
326 | 0 | struct hash *hash = hash_str_new(free, free); |
327 | 0 | char *prefix = strdupa(l->value); |
328 | 0 | struct config *isolated; |
329 | |
|
330 | 0 | if (!hash) |
331 | 0 | lwan_status_critical("Could not allocate hash table"); |
332 | | |
333 | 0 | isolated = config_isolate_section(c, l); |
334 | 0 | if (!isolated) { |
335 | 0 | config_error(c, "Could not isolate configuration file"); |
336 | 0 | goto out; |
337 | 0 | } |
338 | | |
339 | 0 | while ((l = config_read_line(c))) { |
340 | 0 | switch (l->type) { |
341 | 0 | case CONFIG_LINE_TYPE_LINE: { |
342 | 0 | char *key_copy = strdup(l->key); |
343 | 0 | char *value_copy = strdup(l->value); |
344 | |
|
345 | 0 | if (!key_copy) |
346 | 0 | lwan_status_critical("Could not copy key from config file"); |
347 | 0 | if (!value_copy) |
348 | 0 | lwan_status_critical("Could not copy value from config file"); |
349 | | |
350 | 0 | hash_add(hash, key_copy, value_copy); |
351 | 0 | break; |
352 | 0 | } |
353 | | |
354 | 0 | case CONFIG_LINE_TYPE_SECTION: |
355 | 0 | if (streq(l->key, "authorization")) { |
356 | 0 | parse_listener_prefix_authorization(c, l, &url_map); |
357 | 0 | } else if (!config_skip_section(c, l)) { |
358 | 0 | config_error(c, "Could not skip section"); |
359 | 0 | goto out; |
360 | 0 | } |
361 | 0 | break; |
362 | | |
363 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
364 | 0 | goto add_map; |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | 0 | config_error(c, "Expecting section end while parsing prefix"); |
369 | 0 | goto out; |
370 | | |
371 | 0 | add_map: |
372 | 0 | assert((handler && !module) || (!handler && module)); |
373 | | |
374 | 0 | if (handler) { |
375 | 0 | url_map.handler = handler; |
376 | 0 | url_map.flags |= HANDLER_PARSE_MASK | HANDLER_DATA_IS_HASH_TABLE; |
377 | 0 | url_map.data = hash; |
378 | 0 | url_map.module = NULL; |
379 | |
|
380 | 0 | hash = NULL; |
381 | 0 | } else if (module->create_from_hash && module->handle_request) { |
382 | 0 | lwan_status_debug("Initializing module %s from config", |
383 | 0 | get_module_name(module)); |
384 | |
|
385 | 0 | url_map.data = module->create_from_hash(prefix, hash); |
386 | 0 | if (!url_map.data) { |
387 | 0 | config_error(c, "Could not create module instance"); |
388 | 0 | goto out; |
389 | 0 | } |
390 | | |
391 | 0 | if (module->parse_conf && !module->parse_conf(url_map.data, isolated)) { |
392 | 0 | const char *msg = config_last_error(isolated); |
393 | |
|
394 | 0 | config_error(c, "Error from module: %s", msg ? msg : "Unknown"); |
395 | 0 | goto out; |
396 | 0 | } |
397 | | |
398 | 0 | url_map.handler = module->handle_request; |
399 | 0 | url_map.flags |= module->flags; |
400 | 0 | url_map.module = module; |
401 | 0 | } else if (UNLIKELY(!module->create_from_hash)) { |
402 | 0 | config_error(c, "Module isn't prepared to load settings from a file; " |
403 | 0 | "create_from_hash() method isn't present"); |
404 | 0 | goto out; |
405 | 0 | } else if (UNLIKELY(!module->handle_request)) { |
406 | 0 | config_error(c, "Module does not have handle_request() method"); |
407 | 0 | goto out; |
408 | 0 | } |
409 | | |
410 | 0 | add_url_map(&lwan->url_map_trie, prefix, &url_map); |
411 | |
|
412 | 0 | out: |
413 | 0 | hash_free(hash); |
414 | 0 | config_close(isolated); |
415 | 0 | } |
416 | | |
417 | | static void register_url_map(struct lwan *l, const struct lwan_url_map *map) |
418 | 0 | { |
419 | 0 | struct lwan_url_map *copy = add_url_map(&l->url_map_trie, NULL, map); |
420 | |
|
421 | 0 | if (copy->module && copy->module->create) { |
422 | 0 | lwan_status_debug("Initializing module %s from struct", |
423 | 0 | get_module_name(copy->module)); |
424 | |
|
425 | 0 | copy->data = copy->module->create(map->prefix, copy->args); |
426 | 0 | if (!copy->data) { |
427 | 0 | lwan_status_critical("Could not initialize module %s", |
428 | 0 | get_module_name(copy->module)); |
429 | 0 | } |
430 | | |
431 | 0 | copy->flags = copy->module->flags; |
432 | 0 | copy->handler = copy->module->handle_request; |
433 | 0 | } else { |
434 | 0 | copy->flags = HANDLER_PARSE_MASK; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | void lwan_set_url_map(struct lwan *l, const struct lwan_url_map *map) |
439 | 0 | { |
440 | 0 | lwan_trie_destroy(&l->url_map_trie); |
441 | 0 | if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap))) |
442 | 0 | lwan_status_critical_perror("Could not initialize trie"); |
443 | | |
444 | 0 | for (; map->prefix; map++) |
445 | 0 | register_url_map(l, map); |
446 | 0 | } |
447 | | |
448 | | __attribute__((no_sanitize_address)) |
449 | | void lwan_detect_url_map(struct lwan *l) |
450 | 0 | { |
451 | 0 | const struct lwan_handler_info *iter; |
452 | |
|
453 | 0 | lwan_trie_destroy(&l->url_map_trie); |
454 | 0 | if (UNLIKELY(!lwan_trie_init(&l->url_map_trie, destroy_urlmap))) |
455 | 0 | lwan_status_critical_perror("Could not initialize trie"); |
456 | | |
457 | 0 | LWAN_SECTION_FOREACH(lwan_handler, iter) { |
458 | 0 | if (!iter->route) |
459 | 0 | continue; |
460 | | |
461 | 0 | const struct lwan_url_map map = {.prefix = iter->route, |
462 | 0 | .handler = iter->handler}; |
463 | 0 | register_url_map(l, &map); |
464 | 0 | } |
465 | 0 | } |
466 | | |
467 | | const char *lwan_get_config_path(char *path_buf, size_t path_buf_len) |
468 | 0 | { |
469 | 0 | char buffer[PATH_MAX]; |
470 | |
|
471 | 0 | if (proc_pidpath(getpid(), buffer, sizeof(buffer)) < 0) |
472 | 0 | goto out; |
473 | | |
474 | 0 | char *path = strrchr(buffer, '/'); |
475 | 0 | if (!path) |
476 | 0 | goto out; |
477 | 0 | int ret = snprintf(path_buf, path_buf_len, "%s.conf", path + 1); |
478 | 0 | if (ret < 0 || ret >= (int)path_buf_len) |
479 | 0 | goto out; |
480 | | |
481 | 0 | return path_buf; |
482 | | |
483 | 0 | out: |
484 | 0 | return "lwan.conf"; |
485 | 0 | } |
486 | | |
487 | | static void parse_tls_listener(struct config *conf, const struct config_line *line, struct lwan *lwan) |
488 | 0 | { |
489 | 0 | #if !defined(LWAN_HAVE_MBEDTLS) |
490 | 0 | config_error(conf, "Lwan has been built without mbedTLS support"); |
491 | 0 | return; |
492 | 0 | #endif |
493 | | |
494 | 0 | lwan->config.tls_listener = strdup(line->value); |
495 | 0 | if (!lwan->config.tls_listener) { |
496 | 0 | config_error(conf, "Could not allocate memory for tls_listener"); |
497 | 0 | return; |
498 | 0 | } |
499 | | |
500 | 0 | lwan->config.ssl.cert = NULL; |
501 | 0 | lwan->config.ssl.key = NULL; |
502 | |
|
503 | 0 | while ((line = config_read_line(conf))) { |
504 | 0 | switch (line->type) { |
505 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
506 | 0 | if (!lwan->config.ssl.cert) |
507 | 0 | config_error(conf, "Missing path to certificate"); |
508 | 0 | if (!lwan->config.ssl.key) |
509 | 0 | config_error(conf, "Missing path to private key"); |
510 | 0 | return; |
511 | 0 | case CONFIG_LINE_TYPE_SECTION: |
512 | 0 | config_error(conf, "Unexpected section: %s", line->key); |
513 | 0 | return; |
514 | 0 | case CONFIG_LINE_TYPE_LINE: |
515 | 0 | if (streq(line->key, "cert")) { |
516 | 0 | free(lwan->config.ssl.cert); |
517 | 0 | lwan->config.ssl.cert = strdup(line->value); |
518 | 0 | if (!lwan->config.ssl.cert) |
519 | 0 | return lwan_status_critical("Could not copy string"); |
520 | 0 | } else if (streq(line->key, "key")) { |
521 | 0 | free(lwan->config.ssl.key); |
522 | 0 | lwan->config.ssl.key = strdup(line->value); |
523 | 0 | if (!lwan->config.ssl.key) |
524 | 0 | return lwan_status_critical("Could not copy string"); |
525 | 0 | } else if (streq(line->key, "hsts")) { |
526 | 0 | lwan->config.ssl.send_hsts_header = parse_bool(line->value, false); |
527 | 0 | } else { |
528 | 0 | config_error(conf, "Unexpected key: %s", line->key); |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | 0 | config_error(conf, "Expecting section end while parsing SSL configuration"); |
534 | 0 | } |
535 | | |
536 | | static void |
537 | | parse_listener(struct config *c, const struct config_line *l, struct lwan *lwan) |
538 | 0 | { |
539 | 0 | lwan->config.listener = strdup(l->value); |
540 | 0 | if (!lwan->config.listener) |
541 | 0 | config_error(c, "Could not allocate memory for listener"); |
542 | |
|
543 | 0 | while ((l = config_read_line(c))) { |
544 | 0 | switch (l->type) { |
545 | 0 | case CONFIG_LINE_TYPE_LINE: |
546 | 0 | config_error(c, "Unexpected key %s", l->key); |
547 | 0 | return; |
548 | 0 | case CONFIG_LINE_TYPE_SECTION: |
549 | 0 | config_error(c, "Unexpected section %s", l->key); |
550 | 0 | return; |
551 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
552 | 0 | return; |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | 0 | config_error(c, "Unexpected EOF while parsing listener"); |
557 | 0 | } |
558 | | |
559 | | static void |
560 | | parse_site(struct config *c, const struct config_line *l, struct lwan *lwan) |
561 | 0 | { |
562 | 0 | while ((l = config_read_line(c))) { |
563 | 0 | switch (l->type) { |
564 | 0 | case CONFIG_LINE_TYPE_LINE: |
565 | 0 | config_error(c, "Expecting prefix section"); |
566 | 0 | return; |
567 | 0 | case CONFIG_LINE_TYPE_SECTION: |
568 | | /* FIXME: per-site authorization? */ |
569 | |
|
570 | 0 | if (l->key[0] == '&') { |
571 | 0 | void *handler = find_handler(l->key + 1); |
572 | 0 | if (handler) { |
573 | 0 | parse_listener_prefix(c, l, lwan, NULL, handler); |
574 | 0 | continue; |
575 | 0 | } |
576 | | |
577 | 0 | config_error(c, "Could not find handler name: %s", l->key + 1); |
578 | 0 | return; |
579 | 0 | } |
580 | | |
581 | 0 | const struct lwan_module *module = find_module(l->key); |
582 | 0 | if (module) { |
583 | 0 | parse_listener_prefix(c, l, lwan, module, NULL); |
584 | 0 | continue; |
585 | 0 | } |
586 | | |
587 | 0 | config_error(c, "Invalid section or module not found: %s", l->key); |
588 | 0 | return; |
589 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
590 | 0 | return; |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | 0 | config_error(c, "Expecting section end while parsing listener"); |
595 | 0 | } |
596 | | |
597 | | static bool setup_from_config(struct lwan *lwan, const char *path) |
598 | 0 | { |
599 | 0 | const struct config_line *line; |
600 | 0 | struct config *conf; |
601 | 0 | bool has_site = false; |
602 | 0 | bool has_listener = false; |
603 | 0 | bool has_tls_listener = false; |
604 | 0 | char path_buf[PATH_MAX]; |
605 | |
|
606 | 0 | if (!path) |
607 | 0 | path = lwan_get_config_path(path_buf, sizeof(path_buf)); |
608 | 0 | lwan_status_info("Loading configuration file: %s", path); |
609 | |
|
610 | 0 | conf = config_open(path); |
611 | 0 | if (!conf) |
612 | 0 | return false; |
613 | | |
614 | 0 | if (!lwan_trie_init(&lwan->url_map_trie, destroy_urlmap)) |
615 | 0 | return false; |
616 | | |
617 | 0 | while ((line = config_read_line(conf))) { |
618 | 0 | switch (line->type) { |
619 | 0 | case CONFIG_LINE_TYPE_LINE: |
620 | 0 | if (streq(line->key, "keep_alive_timeout")) { |
621 | 0 | lwan->config.keep_alive_timeout = (unsigned int)parse_long( |
622 | 0 | line->value, default_config.keep_alive_timeout); |
623 | 0 | } else if (streq(line->key, "quiet")) { |
624 | 0 | lwan->config.quiet = |
625 | 0 | parse_bool(line->value, default_config.quiet); |
626 | 0 | } else if (streq(line->key, "proxy_protocol")) { |
627 | 0 | lwan->config.proxy_protocol = |
628 | 0 | parse_bool(line->value, default_config.proxy_protocol); |
629 | 0 | } else if (streq(line->key, "allow_cors")) { |
630 | 0 | lwan->config.allow_cors = |
631 | 0 | parse_bool(line->value, default_config.allow_cors); |
632 | 0 | } else if (streq(line->key, "use_dynamic_buffer")) { |
633 | 0 | lwan->config.use_dynamic_buffer = |
634 | 0 | parse_bool(line->value, default_config.use_dynamic_buffer); |
635 | 0 | } else if (streq(line->key, "expires")) { |
636 | 0 | lwan->config.expires = |
637 | 0 | parse_time_period(line->value, default_config.expires); |
638 | 0 | } else if (streq(line->key, "error_template")) { |
639 | 0 | free(lwan->config.error_template); |
640 | 0 | lwan->config.error_template = strdup(line->value); |
641 | 0 | } else if (streq(line->key, "threads")) { |
642 | 0 | long n_threads = |
643 | 0 | parse_long(line->value, default_config.n_threads); |
644 | 0 | if (n_threads < 0) |
645 | 0 | config_error(conf, "Invalid number of threads: %ld", |
646 | 0 | n_threads); |
647 | 0 | lwan->config.n_threads = (unsigned int)n_threads; |
648 | 0 | } else if (streq(line->key, "max_post_data_size")) { |
649 | 0 | long max_post_data_size = parse_long( |
650 | 0 | line->value, (long)default_config.max_post_data_size); |
651 | 0 | if (max_post_data_size < 0) |
652 | 0 | config_error(conf, "Negative maximum post data size"); |
653 | 0 | else if (max_post_data_size > 128 * (1 << 20)) |
654 | 0 | config_error(conf, |
655 | 0 | "Maximum post data can't be over 128MiB"); |
656 | 0 | lwan->config.max_post_data_size = (size_t)max_post_data_size; |
657 | 0 | } else if (streq(line->key, "max_put_data_size")) { |
658 | 0 | long max_put_data_size = parse_long( |
659 | 0 | line->value, (long)default_config.max_put_data_size); |
660 | 0 | if (max_put_data_size < 0) |
661 | 0 | config_error(conf, "Negative maximum put data size"); |
662 | 0 | else if (max_put_data_size > 128 * (1 << 20)) |
663 | 0 | config_error(conf, |
664 | 0 | "Maximum put data can't be over 128MiB"); |
665 | 0 | lwan->config.max_put_data_size = (size_t)max_put_data_size; |
666 | 0 | } else if (streq(line->key, "allow_temp_files")) { |
667 | 0 | bool has_post, has_put; |
668 | |
|
669 | 0 | if (strstr(line->value, "all")) { |
670 | 0 | has_post = has_put = true; |
671 | 0 | } else { |
672 | 0 | has_post = !!strstr(line->value, "post"); |
673 | 0 | has_put = !!strstr(line->value, "put"); |
674 | 0 | } |
675 | |
|
676 | 0 | lwan->config.allow_post_temp_file = has_post; |
677 | 0 | lwan->config.allow_put_temp_file = has_put; |
678 | 0 | } else { |
679 | 0 | config_error(conf, "Unknown config key: %s", line->key); |
680 | 0 | } |
681 | 0 | break; |
682 | 0 | case CONFIG_LINE_TYPE_SECTION: |
683 | 0 | if (streq(line->key, "site")) { |
684 | 0 | if (!has_site) { |
685 | 0 | parse_site(conf, line, lwan); |
686 | 0 | has_site = true; |
687 | 0 | } else { |
688 | 0 | config_error(conf, "Only one site may be configured"); |
689 | 0 | } |
690 | 0 | } else if (streq(line->key, "straitjacket")) { |
691 | 0 | lwan_straitjacket_enforce_from_config(conf); |
692 | 0 | } else if (streq(line->key, "headers")) { |
693 | 0 | parse_global_headers(conf, lwan); |
694 | 0 | } else if (streq(line->key, "listener")) { |
695 | 0 | if (has_listener) { |
696 | 0 | config_error(conf, "Listener already set up"); |
697 | 0 | } else { |
698 | 0 | parse_listener(conf, line, lwan); |
699 | 0 | has_listener = true; |
700 | 0 | } |
701 | 0 | } else if (streq(line->key, "tls_listener")) { |
702 | 0 | if (has_tls_listener) { |
703 | 0 | config_error(conf, "TLS Listener already set up"); |
704 | 0 | } else { |
705 | 0 | parse_tls_listener(conf, line, lwan); |
706 | 0 | has_tls_listener = true; |
707 | 0 | } |
708 | 0 | } else { |
709 | 0 | config_error(conf, "Unknown section type: %s", line->key); |
710 | 0 | } |
711 | 0 | break; |
712 | 0 | case CONFIG_LINE_TYPE_SECTION_END: |
713 | 0 | config_error(conf, "Unexpected section end"); |
714 | 0 | } |
715 | 0 | } |
716 | | |
717 | 0 | if (config_last_error(conf)) { |
718 | 0 | lwan_status_critical("Error on config file \"%s\", line %d: %s", path, |
719 | 0 | config_cur_line(conf), config_last_error(conf)); |
720 | 0 | lwan_trie_destroy(&lwan->url_map_trie); |
721 | 0 | } |
722 | | |
723 | 0 | config_close(conf); |
724 | |
|
725 | 0 | return true; |
726 | 0 | } |
727 | | |
728 | | static void try_setup_from_config(struct lwan *l, |
729 | | const struct lwan_config *config) |
730 | 0 | { |
731 | 0 | if (!setup_from_config(l, config->config_file_path)) { |
732 | 0 | if (config->config_file_path) { |
733 | 0 | lwan_status_critical("Could not read config file: %s", |
734 | 0 | config->config_file_path); |
735 | 0 | } |
736 | 0 | } |
737 | | |
738 | 0 | lwan_status_init(l); /* `quiet` key might have changed value. */ |
739 | |
|
740 | 0 | l->config.request_flags = |
741 | 0 | (l->config.proxy_protocol ? REQUEST_ALLOW_PROXY_REQS : 0) | |
742 | 0 | (l->config.allow_cors ? REQUEST_ALLOW_CORS : 0) | |
743 | 0 | (l->config.ssl.send_hsts_header ? REQUEST_WANTS_HSTS_HEADER : 0); |
744 | 0 | } |
745 | | |
746 | | static rlim_t setup_open_file_count_limits(void) |
747 | 0 | { |
748 | 0 | struct rlimit r; |
749 | |
|
750 | 0 | if (getrlimit(RLIMIT_NOFILE, &r) < 0) { |
751 | 0 | lwan_status_perror("Could not obtain maximum number of file " |
752 | 0 | "descriptors. Assuming %d", |
753 | 0 | OPEN_MAX); |
754 | 0 | return OPEN_MAX; |
755 | 0 | } |
756 | | |
757 | 0 | if (r.rlim_max != r.rlim_cur) { |
758 | 0 | const rlim_t current = r.rlim_cur; |
759 | |
|
760 | 0 | if (r.rlim_max == RLIM_INFINITY && r.rlim_cur < OPEN_MAX) { |
761 | 0 | r.rlim_cur = OPEN_MAX; |
762 | 0 | } else if (r.rlim_cur < r.rlim_max) { |
763 | 0 | r.rlim_cur = r.rlim_max; |
764 | 0 | } else { |
765 | | /* Shouldn't happen, so just return the current value. */ |
766 | 0 | goto out; |
767 | 0 | } |
768 | | |
769 | 0 | if (setrlimit(RLIMIT_NOFILE, &r) < 0) { |
770 | 0 | lwan_status_perror("Could not raise maximum number of file " |
771 | 0 | "descriptors to %" PRIu64 ". Leaving at " |
772 | 0 | "%" PRIu64, r.rlim_max, current); |
773 | 0 | r.rlim_cur = current; |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | 0 | out: |
778 | 0 | return r.rlim_cur; |
779 | 0 | } |
780 | | |
781 | | static void allocate_connections(struct lwan *l, size_t max_open_files) |
782 | 0 | { |
783 | 0 | const size_t sz = max_open_files * sizeof(struct lwan_connection); |
784 | |
|
785 | 0 | l->conns = lwan_aligned_alloc(sz, 64); |
786 | 0 | if (UNLIKELY(!l->conns)) |
787 | 0 | lwan_status_critical_perror("lwan_alloc_aligned"); |
788 | | |
789 | 0 | memset(l->conns, 0, sz); |
790 | 0 | } |
791 | | |
792 | | static void get_number_of_cpus(struct lwan *l) |
793 | 0 | { |
794 | 0 | long n_online_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
795 | 0 | long n_available_cpus = sysconf(_SC_NPROCESSORS_CONF); |
796 | |
|
797 | 0 | if (n_online_cpus < 0) { |
798 | 0 | lwan_status_warning( |
799 | 0 | "Could not get number of online CPUs, assuming 1 CPU"); |
800 | 0 | n_online_cpus = 1; |
801 | 0 | } |
802 | |
|
803 | 0 | if (n_available_cpus < 0) { |
804 | 0 | lwan_status_warning( |
805 | 0 | "Could not get number of available CPUs, assuming %ld CPUs", |
806 | 0 | n_online_cpus); |
807 | 0 | n_available_cpus = n_online_cpus; |
808 | 0 | } |
809 | |
|
810 | 0 | l->online_cpus = (unsigned int)n_online_cpus; |
811 | 0 | l->available_cpus = (unsigned int)n_available_cpus; |
812 | 0 | } |
813 | | |
814 | 0 | void lwan_init(struct lwan *l) { lwan_init_with_config(l, &default_config); } |
815 | | |
816 | | const struct lwan_config *lwan_get_default_config(void) |
817 | 0 | { |
818 | 0 | return &default_config; |
819 | 0 | } |
820 | | |
821 | | static char *dup_or_null(const char *s) |
822 | 0 | { |
823 | 0 | return s ? strdup(s) : NULL; |
824 | 0 | } |
825 | | |
826 | | void lwan_init_with_config(struct lwan *l, const struct lwan_config *config) |
827 | 0 | { |
828 | | /* Load defaults */ |
829 | 0 | memset(l, 0, sizeof(*l)); |
830 | 0 | memcpy(&l->config, config, sizeof(*config)); |
831 | 0 | l->config.listener = dup_or_null(l->config.listener); |
832 | 0 | l->config.config_file_path = dup_or_null(l->config.config_file_path); |
833 | 0 | l->config.ssl.key = dup_or_null(l->config.ssl.key); |
834 | 0 | l->config.ssl.cert = dup_or_null(l->config.ssl.cert); |
835 | | |
836 | | /* Initialize status first, as it is used by other things during |
837 | | * their initialization. */ |
838 | 0 | lwan_status_init(l); |
839 | | |
840 | | /* These will only print debugging messages. Debug messages are always |
841 | | * printed if we're on a debug build, so the quiet setting will be |
842 | | * respected. */ |
843 | 0 | lwan_job_thread_init(); |
844 | 0 | lwan_tables_init(); |
845 | | |
846 | | /* Get the number of CPUs here because straightjacket might be active |
847 | | * and this will block access to /proc and /sys, which will cause |
848 | | * get_number_of_cpus() to get incorrect fallback values. */ |
849 | 0 | get_number_of_cpus(l); |
850 | |
|
851 | 0 | try_setup_from_config(l, config); |
852 | |
|
853 | 0 | if (!l->headers.len) |
854 | 0 | build_response_headers(l, config->global_headers); |
855 | |
|
856 | 0 | lwan_response_init(l); |
857 | | |
858 | | /* Continue initialization as normal. */ |
859 | 0 | lwan_status_debug("Initializing lwan web server"); |
860 | |
|
861 | 0 | if (!l->config.n_threads) { |
862 | 0 | l->thread.count = l->online_cpus; |
863 | 0 | if (l->thread.count == 1) |
864 | 0 | l->thread.count = 2; |
865 | 0 | } else if (l->config.n_threads > 3 * l->online_cpus) { |
866 | 0 | l->thread.count = l->online_cpus * 3; |
867 | |
|
868 | 0 | lwan_status_warning("%d threads requested, but only %d online CPUs " |
869 | 0 | "(out of %d configured CPUs); capping to %d threads", |
870 | 0 | l->config.n_threads, l->online_cpus, l->available_cpus, |
871 | 0 | 3 * l->online_cpus); |
872 | 0 | } else if (l->config.n_threads > 255) { |
873 | 0 | l->thread.count = 256; |
874 | |
|
875 | 0 | lwan_status_warning("%d threads requested, but max 256 supported", |
876 | 0 | l->config.n_threads); |
877 | 0 | } else { |
878 | 0 | l->thread.count = l->config.n_threads; |
879 | 0 | } |
880 | |
|
881 | 0 | rlim_t max_open_files = setup_open_file_count_limits(); |
882 | 0 | allocate_connections(l, (size_t)max_open_files); |
883 | |
|
884 | 0 | l->thread.max_fd = (unsigned)max_open_files / (unsigned)l->thread.count; |
885 | 0 | lwan_status_info("Using %d threads, maximum %d sockets per thread", |
886 | 0 | l->thread.count, l->thread.max_fd); |
887 | |
|
888 | 0 | signal(SIGPIPE, SIG_IGN); |
889 | |
|
890 | 0 | lwan_readahead_init(); |
891 | 0 | lwan_thread_init(l); |
892 | 0 | lwan_http_authorize_init(); |
893 | 0 | } |
894 | | |
895 | | void lwan_shutdown(struct lwan *l) |
896 | 0 | { |
897 | 0 | lwan_status_info("Shutting down"); |
898 | |
|
899 | 0 | free(l->config.listener); |
900 | 0 | free(l->config.error_template); |
901 | 0 | free(l->config.config_file_path); |
902 | |
|
903 | 0 | lwan_always_bzero(l->config.ssl.cert, strlen(l->config.ssl.cert)); |
904 | 0 | free(l->config.ssl.cert); |
905 | 0 | lwan_always_bzero(l->config.ssl.key, strlen(l->config.ssl.key)); |
906 | 0 | free(l->config.ssl.key); |
907 | |
|
908 | 0 | lwan_job_thread_shutdown(); |
909 | 0 | lwan_thread_shutdown(l); |
910 | |
|
911 | 0 | lwan_status_debug("Shutting down URL handlers"); |
912 | 0 | lwan_trie_destroy(&l->url_map_trie); |
913 | |
|
914 | 0 | free(l->headers.value); |
915 | 0 | free(l->conns); |
916 | |
|
917 | 0 | lwan_response_shutdown(l); |
918 | 0 | lwan_tables_shutdown(); |
919 | 0 | lwan_status_shutdown(l); |
920 | 0 | lwan_http_authorize_shutdown(); |
921 | 0 | lwan_readahead_shutdown(); |
922 | 0 | } |
923 | | |
924 | | void lwan_main_loop(struct lwan *l) |
925 | 0 | { |
926 | 0 | lwan_status_info("Ready to serve"); |
927 | |
|
928 | 0 | lwan_job_thread_main_loop(); |
929 | 0 | } |
930 | | |
931 | | #ifdef CLOCK_MONOTONIC_COARSE |
932 | | __attribute__((constructor)) static void detect_fastest_monotonic_clock(void) |
933 | 10 | { |
934 | 10 | struct timespec ts; |
935 | | |
936 | 10 | if (!clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)) |
937 | 10 | monotonic_clock_id = CLOCK_MONOTONIC_COARSE; |
938 | 10 | } |
939 | | #endif |
940 | | |
941 | | void lwan_set_thread_name(const char *name) |
942 | 0 | { |
943 | 0 | char thread_name[16]; |
944 | 0 | char process_name[PATH_MAX]; |
945 | 0 | char *tmp; |
946 | 0 | int ret; |
947 | |
|
948 | 0 | if (proc_pidpath(getpid(), process_name, sizeof(process_name)) < 0) |
949 | 0 | return; |
950 | | |
951 | 0 | tmp = strrchr(process_name, '/'); |
952 | 0 | if (!tmp) |
953 | 0 | return; |
954 | | |
955 | 0 | ret = snprintf(thread_name, sizeof(thread_name), "%s %s", tmp + 1, name); |
956 | 0 | if (ret < 0) |
957 | 0 | return; |
958 | | |
959 | 0 | pthread_set_name_np(pthread_self(), thread_name); |
960 | 0 | } |