LCOV - code coverage report
Current view: top level - lib - lwan.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 296 530 55.8 %
Date: 2023-04-18 16:19:03 Functions: 23 31 74.2 %

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

Generated by: LCOV version 1.15-2-gb9d6727