diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:57:58 +0000 |
commit | be1c7e50e1e8809ea56f2c9d472eccd8ffd73a97 (patch) | |
tree | 9754ff1ca740f6346cf8483ec915d4054bc5da2d /fluent-bit/lib/monkey/mk_server/mk_config.c | |
parent | Initial commit. (diff) | |
download | netdata-upstream.tar.xz netdata-upstream.zip |
Adding upstream version 1.44.3.upstream/1.44.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/lib/monkey/mk_server/mk_config.c')
-rw-r--r-- | fluent-bit/lib/monkey/mk_server/mk_config.c | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/mk_server/mk_config.c b/fluent-bit/lib/monkey/mk_server/mk_config.c new file mode 100644 index 00000000..f8dc9c00 --- /dev/null +++ b/fluent-bit/lib/monkey/mk_server/mk_config.c @@ -0,0 +1,636 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Monkey HTTP Server + * ================== + * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <monkey/monkey.h> +#include <monkey/mk_kernel.h> +#include <monkey/mk_config.h> +#include <monkey/mk_utils.h> +#include <monkey/mk_mimetype.h> +#include <monkey/mk_info.h> +#include <monkey/mk_core.h> +#include <monkey/mk_server.h> +#include <monkey/mk_plugin.h> +#include <monkey/mk_vhost.h> +#include <monkey/mk_mimetype.h> +#include <monkey/mk_info.h> + +#include <ctype.h> +#include <limits.h> +#include <mk_core/mk_dirent.h> +#include <sys/stat.h> + +struct mk_server_config *mk_config; + +static int mk_config_key_have(struct mk_list *list, const char *value) +{ + struct mk_list *head; + struct mk_string_line *entry; + + mk_list_foreach(head, list) { + entry = mk_list_entry(head, struct mk_string_line, _head); + if (strcasecmp(entry->val, value) == 0) { + return MK_TRUE; + } + } + return MK_FALSE; +} + +void mk_config_listeners_free(struct mk_server *server) +{ + struct mk_list *tmp; + struct mk_list *head; + struct mk_config_listener *l; + + mk_list_foreach_safe(head, tmp, &server->listeners) { + l = mk_list_entry(head, struct mk_config_listener, _head); + mk_list_del(&l->_head); + mk_mem_free(l->address); + mk_mem_free(l->port); + mk_mem_free(l); + } +} + +void mk_config_free_all(struct mk_server *server) +{ + mk_vhost_free_all(server); + mk_mimetype_free_all(server); + + if (server->config) { + mk_rconf_free(server->config); + } + + if (server->path_conf_root) { + mk_mem_free(server->path_conf_root); + } + + if (server->path_conf_pidfile) { + mk_mem_free(server->path_conf_pidfile); + } + + if (server->conf_user_pub) { + mk_mem_free(server->conf_user_pub); + } + + /* free config->index_files */ + if (server->index_files) { + mk_string_split_free(server->index_files); + } + + if (server->user) { + mk_mem_free(server->user); + } + + if (server->transport_layer) { + mk_mem_free(server->transport_layer); + } + + mk_config_listeners_free(server); + + mk_ptr_free(&server->server_software); + mk_mem_free(server); +} + +/* Print a specific error */ +static void mk_config_print_error_msg(char *variable, char *path) +{ + mk_err("[config] %s at %s has an invalid value", + variable, path); + mk_mem_free(path); + exit(EXIT_FAILURE); +} + +/* + * Check if at least one of the Listen interfaces are being used by another + * process. + */ +int mk_config_listen_check_busy(struct mk_server *server) +{ + int fd; + struct mk_list *head; + struct mk_plugin *p; + struct mk_config_listener *listen; + + p = mk_plugin_cap(MK_CAP_SOCK_PLAIN, server); + if (!p) { + mk_warn("Listen check: consider build monkey with basic socket handling!"); + return MK_FALSE; + } + + mk_list_foreach(head, &server->listeners) { + listen = mk_list_entry(head, struct mk_config_listener, _head); + + fd = mk_socket_connect(listen->address, atol(listen->port), MK_FALSE); + if (fd != -1) { + close(fd); + return MK_TRUE; + } + } + + return MK_FALSE; +} + +int mk_config_listen_parse(char *value, struct mk_server *server) +{ + int ret = -1; + int flags = 0; + long port_num; + char *address = NULL; + char *port = NULL; + char *divider; + struct mk_list *list = NULL; + struct mk_string_line *listener; + + list = mk_string_split_line(value); + if (!list) { + goto error; + } + + if (mk_list_is_empty(list) == 0) { + goto error; + } + + /* Parse the listener interface */ + listener = mk_list_entry_first(list, struct mk_string_line, _head); + if (listener->val[0] == '[') { + /* IPv6 address */ + divider = strchr(listener->val, ']'); + if (divider == NULL) { + mk_err("[config] Expected closing ']' in IPv6 address."); + goto error; + } + if (divider[1] != ':' || divider[2] == '\0') { + mk_err("[config] Expected ':port' after IPv6 address."); + goto error; + } + + address = mk_string_copy_substr(listener->val + 1, 0, + divider - listener->val - 1); + port = mk_string_dup(divider + 2); + } + else if (strchr(listener->val, ':') != NULL) { + /* IPv4 address */ + divider = strrchr(listener->val, ':'); + if (divider == NULL || divider[1] == '\0') { + mk_err("[config] Expected ':port' after IPv4 address."); + goto error; + } + + address = mk_string_copy_substr(listener->val, 0, + divider - listener->val); + port = mk_string_dup(divider + 1); + } + else { + /* Port only */ + address = NULL; + port = mk_string_dup(listener->val); + } + + errno = 0; + port_num = strtol(port, NULL, 10); + if (errno != 0 || port_num == LONG_MAX || port_num == LONG_MIN) { + mk_warn("Using defaults, could not understand \"Listen %s\"", + listener->val); + port = NULL; + } + + /* Check extra properties of the listener */ + flags = MK_CAP_HTTP; + if (mk_config_key_have(list, "!http")) { + flags |= ~MK_CAP_HTTP; + } + +#ifdef MK_HAVE_HTTP2 + if (mk_config_key_have(list, "h2")) { + flags |= (MK_CAP_HTTP2 | MK_CAP_SOCK_TLS); + } + + if (mk_config_key_have(list, "h2c")) { + flags |= MK_CAP_HTTP2; + } +#endif + + if (mk_config_key_have(list, "tls")) { + flags |= MK_CAP_SOCK_TLS; + } + + /* register the new listener */ + mk_config_listener_add(address, port, flags, server); + mk_string_split_free(list); + list = NULL; + ret = 0; + +error: + if (address) { + mk_mem_free(address); + } + if (port) { + mk_mem_free(port); + } + if (list) { + mk_string_split_free(list); + } + + return ret; +} + +static int mk_config_listen_read(struct mk_rconf_section *section, + struct mk_server *server) +{ + int ret; + struct mk_list *cur; + struct mk_rconf_entry *entry; + + mk_list_foreach(cur, §ion->entries) { + entry = mk_list_entry(cur, struct mk_rconf_entry, _head); + if (strcasecmp(entry->key, "Listen")) { + continue; + } + + ret = mk_config_listen_parse(entry->val, server); + if (ret != 0) { + return -1; + } + } + + return 0; +} + +/* Read configuration files */ +static int mk_config_read_files(char *path_conf, char *file_conf, + struct mk_server *server) +{ + unsigned long len; + char *tmp = NULL; + struct stat checkdir; + struct mk_rconf *cnf; + struct mk_rconf_section *section; + + if (!path_conf) { + return -1; + } + + if (!file_conf) { + file_conf = "monkey.conf"; + } + + server->path_conf_root = mk_string_dup(path_conf); + + if (stat(server->path_conf_root, &checkdir) == -1) { + mk_err("ERROR: Cannot find/open '%s'", server->path_conf_root); + return -1; + } + + mk_string_build(&tmp, &len, "%s/%s", path_conf, file_conf); + cnf = mk_rconf_open(tmp); + if (!cnf) { + mk_mem_free(tmp); + mk_err("Cannot read '%s'", server->conf_main); + return -1; + } + section = mk_rconf_section_get(cnf, "SERVER"); + if (!section) { + mk_err("ERROR: No 'SERVER' section defined"); + return -1; + } + + /* Map source configuration */ + server->config = cnf; + + /* Listen */ + if (!server->port_override) { + /* Process each Listen entry */ + if (mk_config_listen_read(section, server)) { + mk_err("[config] Failed to read listen sections."); + } + if (mk_list_is_empty(&server->listeners) == 0) { + mk_warn("[config] No valid Listen entries found, set default"); + mk_config_listener_add(NULL, NULL, MK_CAP_HTTP, server); + } + } + else { + mk_config_listener_add(NULL, server->port_override, + MK_CAP_HTTP, server); + } + + /* Number of thread workers */ + if (server->workers == -1) { + server->workers = (size_t) mk_rconf_section_get_key(section, + "Workers", + MK_RCONF_NUM); + } + + if (server->workers < 1) { + server->workers = mk_utils_get_system_core_count(); + + if (server->workers < 1) { + mk_config_print_error_msg("Workers", tmp); + } + } + + /* Timeout */ + server->timeout = (size_t) mk_rconf_section_get_key(section, + "Timeout", MK_RCONF_NUM); + if (server->timeout < 1) { + mk_config_print_error_msg("Timeout", tmp); + } + + /* KeepAlive */ + server->keep_alive = (size_t) mk_rconf_section_get_key(section, + "KeepAlive", + MK_RCONF_BOOL); + if (server->keep_alive == MK_ERROR) { + mk_config_print_error_msg("KeepAlive", tmp); + } + + /* MaxKeepAliveRequest */ + server->max_keep_alive_request = (size_t) + mk_rconf_section_get_key(section, + "MaxKeepAliveRequest", + MK_RCONF_NUM); + + if (server->max_keep_alive_request == 0) { + mk_config_print_error_msg("MaxKeepAliveRequest", tmp); + } + + /* KeepAliveTimeout */ + server->keep_alive_timeout = (size_t) mk_rconf_section_get_key(section, + "KeepAliveTimeout", + MK_RCONF_NUM); + if (server->keep_alive_timeout == 0) { + mk_config_print_error_msg("KeepAliveTimeout", tmp); + } + + /* Pid File */ + if (!server->path_conf_pidfile) { + server->path_conf_pidfile = mk_rconf_section_get_key(section, + "PidFile", + MK_RCONF_STR); + } + + /* Home user's directory /~ */ + server->conf_user_pub = mk_rconf_section_get_key(section, + "UserDir", + MK_RCONF_STR); + + /* Index files */ + server->index_files = mk_rconf_section_get_key(section, + "Indexfile", MK_RCONF_LIST); + + /* HideVersion Variable */ + server->hideversion = (size_t) mk_rconf_section_get_key(section, + "HideVersion", + MK_RCONF_BOOL); + if (server->hideversion == MK_ERROR) { + mk_config_print_error_msg("HideVersion", tmp); + } + + /* User Variable */ + server->user = mk_rconf_section_get_key(section, "User", MK_RCONF_STR); + + /* Resume */ + server->resume = (size_t) mk_rconf_section_get_key(section, + "Resume", MK_RCONF_BOOL); + if (server->resume == MK_ERROR) { + mk_config_print_error_msg("Resume", tmp); + } + + /* Max Request Size */ + server->max_request_size = (size_t) mk_rconf_section_get_key(section, + "MaxRequestSize", + MK_RCONF_NUM); + if (server->max_request_size <= 0) { + mk_config_print_error_msg("MaxRequestSize", tmp); + } + else { + server->max_request_size *= 1024; + } + + /* Symbolic Links */ + server->symlink = (size_t) mk_rconf_section_get_key(section, + "SymLink", MK_RCONF_BOOL); + if (server->symlink == MK_ERROR) { + mk_config_print_error_msg("SymLink", tmp); + } + + /* Transport Layer plugin */ + if (!server->transport_layer) { + server->transport_layer = mk_rconf_section_get_key(section, + "TransportLayer", + MK_RCONF_STR); + } + + /* Default Mimetype */ + mk_mem_free(tmp); + tmp = mk_rconf_section_get_key(section, "DefaultMimeType", MK_RCONF_STR); + if (tmp) { + mk_string_build(&server->mimetype_default_str, &len, "%s\r\n", tmp); + } + + /* File Descriptor Table (FDT) */ + server->fdt = (size_t) mk_rconf_section_get_key(section, + "FDT", + MK_RCONF_BOOL); + + /* FIXME: Overcapacity not ready */ + server->fd_limit = (size_t) mk_rconf_section_get_key(section, + "FDLimit", + MK_RCONF_NUM); + /* Get each worker clients capacity based on FDs system limits */ + server->server_capacity = mk_server_capacity(server); + + + if (!server->one_shot) { + mk_vhost_init(path_conf, server); + } + else { + mk_vhost_set_single(server->one_shot, server); + } + + mk_mem_free(tmp); + return 0; +} + +void mk_config_signature(struct mk_server *server) +{ + unsigned long len; + + /* Server Signature */ + if (server->hideversion == MK_FALSE) { + snprintf(server->server_signature, + sizeof(server->server_signature) - 1, + "Monkey/%s", MK_VERSION_STR); + } + else { + snprintf(server->server_signature, + sizeof(server->server_signature) - 1, + "Monkey"); + } + len = snprintf(server->server_signature_header, + sizeof(server->server_signature_header) - 1, + "Server: %s\r\n", server->server_signature); + server->server_signature_header_len = len; +} + +/* read main configuration from monkey.conf */ +void mk_config_start_configure(struct mk_server *server) +{ + int ret; + unsigned long len; + + ret = mk_config_read_files(server->path_conf_root, + server->conf_main, server); + if (ret != 0) { + return; + } + + /* Load mimes */ + mk_mimetype_read_config(server); + + mk_ptr_reset(&server->server_software); + + /* Basic server information */ + if (server->hideversion == MK_FALSE) { + mk_string_build(&server->server_software.data, + &len, "Monkey/%s (%s)", MK_VERSION_STR, MK_BUILD_OS); + server->server_software.len = len; + } + else { + mk_string_build(&server->server_software.data, &len, "Monkey Server"); + server->server_software.len = len; + } +} + +/* Register a new listener into the main configuration */ +struct mk_config_listener *mk_config_listener_add(char *address, + char *port, int flags, + struct mk_server *server) +{ + struct mk_list *head; + struct mk_config_listener *check; + struct mk_config_listener *listen = NULL; + + listen = mk_mem_alloc(sizeof(struct mk_config_listener)); + if (!listen) { + mk_err("[listen_add] malloc() failed"); + return NULL; + } + + if (!address) { + listen->address = mk_string_dup(MK_DEFAULT_LISTEN_ADDR); + } + else { + listen->address = mk_string_dup(address); + } + + /* Set the port */ + if (!port) { + mk_err("[listen_add] TCP port not defined"); + exit(EXIT_FAILURE); + } + + listen->port = mk_string_dup(port); + listen->flags = flags; + + /* Before to add a new listener, lets make sure it's not a duplicated */ + mk_list_foreach(head, &server->listeners) { + check = mk_list_entry(head, struct mk_config_listener, _head); + if (strcmp(listen->address, check->address) == 0 && + strcmp(listen->port, check->port) == 0) { + mk_warn("Listener: duplicated %s:%s, skip.", + listen->address, listen->port); + + /* free resources */ + mk_mem_free(listen->address); + mk_mem_free(listen->port); + mk_mem_free(listen); + return NULL; + } + } + + mk_list_add(&listen->_head, &server->listeners); + return listen; +} + +void mk_config_set_init_values(struct mk_server *server) +{ + /* Init values */ + server->is_seteuid = MK_FALSE; + server->timeout = 15; + server->hideversion = MK_FALSE; + server->keep_alive = MK_TRUE; + server->keep_alive_timeout = 15; + server->max_keep_alive_request = 50; + server->resume = MK_TRUE; + server->standard_port = 80; + server->symlink = MK_FALSE; + server->nhosts = 0; + mk_list_init(&server->hosts); + server->user = NULL; + server->open_flags = O_RDONLY; /* The only place this is effectively used (other than the sanity check) + * is mk_http.c where it's used to test for file existence and the fd is apparently leaked */ + server->index_files = NULL; + server->conf_user_pub = NULL; + server->workers = 1; + + /* TCP REUSEPORT: available on Linux >= 3.9 */ + if (server->scheduler_mode == -1) { + if (server->kernel_features & MK_KERNEL_SO_REUSEPORT) { + server->scheduler_mode = MK_SCHEDULER_REUSEPORT; + } + else { + server->scheduler_mode = MK_SCHEDULER_FAIR_BALANCING; + } + } + + /* Max request buffer size allowed + * right now, every chunk size is 4KB (4096 bytes), + * so we are setting a maximum request size to 32 KB */ + server->max_request_size = MK_REQUEST_CHUNK * 8; + + /* Internals */ + server->safe_event_write = MK_FALSE; + + /* Init plugin list */ + mk_list_init(&server->plugins); + + /* Init listeners */ + mk_list_init(&server->listeners); +} + +void mk_config_sanity_check(struct mk_server *server) +{ + /* Check O_NOATIME for current user, flag will just be used + * if running user is allowed to. + */ + int fd; + int flags; + + if (!server->path_conf_root) { + return; + } + + flags = server->open_flags; + flags |= O_NOATIME; + fd = open(server->path_conf_root, flags); + + if (fd > -1) { + server->open_flags = flags; + close(fd); + } +} |