kore/src/kore.c

1026 lines
20 KiB
C
Raw Normal View History

2013-04-17 22:34:27 +02:00
/*
2022-01-31 22:02:06 +01:00
* Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
2013-04-17 22:34:27 +02:00
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
Massive rework of HTTP layer. This commit is a flag day, your old modules will almost certainly need to be updated in order to build properly with these changes. Summary of changes: - Offload HTTP bodies to disk if they are large (inspired by #100). (disabled by default) - The http_argument_get* macros now takes an explicit http_request parameter. - Kore will now throw 404 errors almost immediately after an HTTP request has come in instead of waiting until all data has arrived. API changes: - http_argument_get* macros now require an explicit http_request parameter. (no more magic invokations). - http_generic_404() is gone - http_populate_arguments() is gone - http_body_bytes() is gone - http_body_text() is gone - http_body_read() has been added - http_populate_post() has been added - http_populate_get() has been added - http_file_read() has been added - http_file_rewind() has been added - http_file_lookup() no longer takes name, fname, data and len parameters. - http_file_lookup() now returns a struct http_file pointer. - http_populate_multipart_form() no longer takes an secondary parameter. New configuration options: - http_body_disk_offload: Number of bytes after which Kore will offload the HTTP body to disk instead of retaining it in memory. If 0 this feature is disabled. (Default: 0) - http_body_disk_path: The path where Kore will store temporary HTTP body files. (this directory does not get created if http_body_disk_offload is 0). New example: The upload example has been added, demonstrating how to deal with file uploads from a multipart form.
2016-01-18 11:30:22 +01:00
#include <sys/stat.h>
#include <sys/un.h>
Massive rework of HTTP layer. This commit is a flag day, your old modules will almost certainly need to be updated in order to build properly with these changes. Summary of changes: - Offload HTTP bodies to disk if they are large (inspired by #100). (disabled by default) - The http_argument_get* macros now takes an explicit http_request parameter. - Kore will now throw 404 errors almost immediately after an HTTP request has come in instead of waiting until all data has arrived. API changes: - http_argument_get* macros now require an explicit http_request parameter. (no more magic invokations). - http_generic_404() is gone - http_populate_arguments() is gone - http_body_bytes() is gone - http_body_text() is gone - http_body_read() has been added - http_populate_post() has been added - http_populate_get() has been added - http_file_read() has been added - http_file_rewind() has been added - http_file_lookup() no longer takes name, fname, data and len parameters. - http_file_lookup() now returns a struct http_file pointer. - http_populate_multipart_form() no longer takes an secondary parameter. New configuration options: - http_body_disk_offload: Number of bytes after which Kore will offload the HTTP body to disk instead of retaining it in memory. If 0 this feature is disabled. (Default: 0) - http_body_disk_path: The path where Kore will store temporary HTTP body files. (this directory does not get created if http_body_disk_offload is 0). New example: The upload example has been added, demonstrating how to deal with file uploads from a multipart form.
2016-01-18 11:30:22 +01:00
2013-04-17 22:34:27 +02:00
#include <sys/socket.h>
#include <sys/resource.h>
2013-04-17 22:34:27 +02:00
#include <libgen.h>
#include <fcntl.h>
2016-01-22 12:08:13 +01:00
#include <stdio.h>
#include <netdb.h>
#include <signal.h>
2013-04-17 22:34:27 +02:00
#include "kore.h"
#include "hooks.h"
2013-04-17 22:34:27 +02:00
Massive rework of HTTP layer. This commit is a flag day, your old modules will almost certainly need to be updated in order to build properly with these changes. Summary of changes: - Offload HTTP bodies to disk if they are large (inspired by #100). (disabled by default) - The http_argument_get* macros now takes an explicit http_request parameter. - Kore will now throw 404 errors almost immediately after an HTTP request has come in instead of waiting until all data has arrived. API changes: - http_argument_get* macros now require an explicit http_request parameter. (no more magic invokations). - http_generic_404() is gone - http_populate_arguments() is gone - http_body_bytes() is gone - http_body_text() is gone - http_body_read() has been added - http_populate_post() has been added - http_populate_get() has been added - http_file_read() has been added - http_file_rewind() has been added - http_file_lookup() no longer takes name, fname, data and len parameters. - http_file_lookup() now returns a struct http_file pointer. - http_populate_multipart_form() no longer takes an secondary parameter. New configuration options: - http_body_disk_offload: Number of bytes after which Kore will offload the HTTP body to disk instead of retaining it in memory. If 0 this feature is disabled. (Default: 0) - http_body_disk_path: The path where Kore will store temporary HTTP body files. (this directory does not get created if http_body_disk_offload is 0). New example: The upload example has been added, demonstrating how to deal with file uploads from a multipart form.
2016-01-18 11:30:22 +01:00
#if !defined(KORE_NO_HTTP)
#include "http.h"
#endif
#if defined(KORE_USE_CURL)
#include "curl.h"
#endif
#if defined(KORE_USE_PGSQL)
#include "pgsql.h"
#endif
#if defined(KORE_USE_PYTHON)
#include "python_api.h"
#endif
#if defined(KORE_USE_LUA)
#include "lua_api.h"
#endif
Add acmev2 (RFC8555) support to Kore. A new acme process is created that communicates with the acme servers. This process does not hold any of your private keys (no account keys, no domain keys etc). Whenever the acme process requires a signed payload it will ask the keymgr process to do the signing with the relevant keys. This process is also sandboxed with pledge+unveil on OpenBSD and seccomp syscall filtering on Linux. The implementation only supports the tls-alpn-01 challenge. This means that you do not need to open additional ports on your machine. http-01 and dns-01 are currently not supported (no wildcard support). A new configuration option "acme_provider" is available and can be set to the acme server its directory. By default this will point to the live letsencrypt environment: https://acme-v02.api.letsencrypt.org/directory The acme process can be controlled via the following config options: - acme_root (where the acme process will chroot/chdir into). - acme_runas (the user the acme process will run as). If none are set, the values from 'root' and 'runas' are taken. If you want to turn on acme for domains you do it as follows: domain kore.io { acme yes } You do not need to specify certkey/certfile anymore, if they are present still they will be overwritten by the acme system. The keymgr will store all certificates and keys under its root (keymgr_root), the account key is stored as "/account-key.pem" and all obtained certificates go under "certificates/<domain>/fullchain.pem" while keys go under "certificates/<domain>/key.pem". Kore will automatically renew certificates if they will expire in 7 days or less.
2019-11-06 19:33:53 +01:00
#if defined(KORE_USE_ACME)
#include "acme.h"
#endif
2018-04-13 16:05:59 +02:00
volatile sig_atomic_t sig_recv;
struct kore_server_list kore_servers;
u_int8_t nlisteners;
int kore_argc = 0;
2013-06-26 11:18:32 +02:00
pid_t kore_pid = -1;
u_int16_t cpu_count = 1;
int kore_quiet = 0;
int skip_runas = 0;
int skip_chroot = 0;
u_int8_t worker_count = 0;
char **kore_argv = NULL;
int kore_mem_guard = 0;
int kore_foreground = 1;
char *kore_progname = NULL;
u_int32_t kore_socket_backlog = 5000;
int kore_quit = KORE_QUIT_NONE;
char *kore_pidfile = KORE_PIDFILE_DEFAULT;
2013-04-17 22:34:27 +02:00
struct kore_privsep worker_privsep;
extern char **environ;
extern char *__progname;
static size_t proctitle_maxlen = 0;
static void usage(void);
static void version(void);
2013-06-26 11:18:32 +02:00
static void kore_write_kore_pid(void);
static void kore_proctitle_setup(void);
static void kore_server_shutdown(void);
static void kore_server_start(int, char *[]);
static void kore_call_parent_configure(int, char **);
2013-04-17 22:34:27 +02:00
#if !defined(KORE_SINGLE_BINARY)
static const char *rarg0 = NULL;
#endif
static const char *parent_config_hook = KORE_CONFIG_HOOK;
static const char *parent_teardown_hook = KORE_TEARDOWN_HOOK;
#if defined(KORE_SINGLE_BINARY)
static const char *parent_daemonized_hook = KORE_DAEMONIZED_HOOK;
#endif
static void
usage(void)
{
if (kore_runtime_count() > 0) {
printf("Usage: %s [options] [app | script]\n", __progname);
} else {
printf("Usage: %s [options]\n", __progname);
}
2018-04-02 18:20:57 +02:00
printf("\n");
printf("Command-line options:\n");
#if !defined(KORE_SINGLE_BINARY)
printf("\t-c\tThe configuration file to load when starting.\n");
#endif
printf("\t-f\tDo not daemonize, everything runs in the foreground.\n");
printf("\t-h\tThis help text.\n");
printf("\t-n\tDo not do the chroot privsep step.\n");
printf("\t-q\tQuiet mode, only logs errors.\n");
printf("\t-r\tDo not do the privsep user swapping step.\n");
printf("\t-v\tDisplay %s build information.\n", __progname);
printf("\n");
printf("Environment options:\n");
printf(" env KORE_MEM_GUARD=1\n");
printf(" Enables memory pool guards and other protections.\n");
printf("\n");
printf(" Enabling this will include guard pages for each\n");
printf(" pool entry allocations and mark pool entries as\n");
printf(" PROT_NONE when unused.\n");
printf("\n");
printf(" This catches bugs and prevents memory vulnerabilities\n");
printf(" but with performance and memory pressure costs.\n");
printf("\n");
2018-04-02 18:20:57 +02:00
exit(1);
}
static void
version(void)
{
printf("%s ", kore_version);
#if defined(KORE_NO_HTTP)
printf("no-http ");
#endif
#if defined(KORE_USE_CURL)
printf("curl-%s ", LIBCURL_VERSION);
#endif
#if defined(KORE_USE_PGSQL)
printf("pgsql ");
#endif
#if defined(KORE_USE_TASKS)
printf("tasks ");
#endif
2017-01-25 22:21:30 +01:00
#if defined(KORE_USE_PYTHON)
2019-10-15 10:16:53 +02:00
printf("python-%s ", PY_VERSION);
2019-11-13 23:01:24 +01:00
#endif
#if defined(KORE_USE_LUA)
printf("lua-%s.%s.%s ",
LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
#endif
2019-11-13 23:01:24 +01:00
#if defined(KORE_USE_ACME)
printf("acme ");
#endif
#if defined(KORE_DEBUG)
printf("debug ");
2017-01-25 22:21:30 +01:00
#endif
if (!kore_tls_supported())
printf("notls ");
printf("\n");
exit(0);
}
2013-04-17 22:34:27 +02:00
int
main(int argc, char *argv[])
{
#if !defined(KORE_SINGLE_BINARY)
struct stat st;
#endif
struct kore_runtime_call *rcall;
2013-04-17 22:34:27 +02:00
kore_argc = argc;
kore_argv = argv;
#if !defined(KORE_SINGLE_BINARY)
kore_default_getopt(argc, argv);
#endif
kore_mem_init();
kore_msg_init();
kore_log_init();
kore_tls_init();
kore_progname = kore_strdup(argv[0]);
kore_proctitle_setup();
#if !defined(KORE_SINGLE_BINARY)
argc -= optind;
argv += optind;
#endif
#if !defined(KORE_SINGLE_BINARY)
if (kore_runtime_count() > 0) {
if (argc > 0) {
rarg0 = argv[0];
argc--;
argv++;
}
if (rarg0) {
if (lstat(rarg0, &st) == -1) {
if (errno == ENOENT)
rarg0 = NULL;
else
fatal("stat(%s): %s", rarg0, errno_s);
}
}
}
#endif
2017-02-22 20:09:11 +01:00
2013-06-26 11:18:32 +02:00
kore_pid = getpid();
nlisteners = 0;
LIST_INIT(&kore_servers);
kore_platform_init();
#if !defined(KORE_NO_HTTP)
http_parent_init();
#if defined(KORE_USE_CURL)
kore_curl_sysinit();
#endif
#if defined(KORE_USE_PGSQL)
kore_pgsql_sys_init();
#endif
kore_auth_init();
kore_validator_init();
kore_filemap_init();
Add acmev2 (RFC8555) support to Kore. A new acme process is created that communicates with the acme servers. This process does not hold any of your private keys (no account keys, no domain keys etc). Whenever the acme process requires a signed payload it will ask the keymgr process to do the signing with the relevant keys. This process is also sandboxed with pledge+unveil on OpenBSD and seccomp syscall filtering on Linux. The implementation only supports the tls-alpn-01 challenge. This means that you do not need to open additional ports on your machine. http-01 and dns-01 are currently not supported (no wildcard support). A new configuration option "acme_provider" is available and can be set to the acme server its directory. By default this will point to the live letsencrypt environment: https://acme-v02.api.letsencrypt.org/directory The acme process can be controlled via the following config options: - acme_root (where the acme process will chroot/chdir into). - acme_runas (the user the acme process will run as). If none are set, the values from 'root' and 'runas' are taken. If you want to turn on acme for domains you do it as follows: domain kore.io { acme yes } You do not need to specify certkey/certfile anymore, if they are present still they will be overwritten by the acme system. The keymgr will store all certificates and keys under its root (keymgr_root), the account key is stored as "/account-key.pem" and all obtained certificates go under "certificates/<domain>/fullchain.pem" while keys go under "certificates/<domain>/key.pem". Kore will automatically renew certificates if they will expire in 7 days or less.
2019-11-06 19:33:53 +01:00
#endif
#if defined(KORE_USE_ACME)
kore_acme_init();
#endif
kore_domain_init();
kore_module_init();
#if !defined(KORE_SINGLE_BINARY)
if (kore_runtime_count() == 0 && config_file == NULL)
usage();
#endif
kore_module_load(NULL, NULL, KORE_MODULE_NATIVE);
#if defined(KORE_USE_PYTHON)
kore_python_init();
#endif
#if defined(KORE_USE_LUA)
kore_lua_init();
#endif
#if !defined(KORE_SINGLE_BINARY)
if (kore_runtime_count() > 0 && rarg0 != NULL)
kore_runtime_resolve(rarg0, &st);
#endif
#if defined(KORE_SINGLE_BINARY)
kore_call_parent_configure(argc, argv);
#else
if (kore_runtime_count() > 0 && rarg0 != NULL)
kore_call_parent_configure(argc, argv);
#endif
kore_parse_config();
#if !defined(KORE_SINGLE_BINARY)
free(config_file);
#endif
#if !defined(KORE_NO_HTTP)
Massive rework of HTTP layer. This commit is a flag day, your old modules will almost certainly need to be updated in order to build properly with these changes. Summary of changes: - Offload HTTP bodies to disk if they are large (inspired by #100). (disabled by default) - The http_argument_get* macros now takes an explicit http_request parameter. - Kore will now throw 404 errors almost immediately after an HTTP request has come in instead of waiting until all data has arrived. API changes: - http_argument_get* macros now require an explicit http_request parameter. (no more magic invokations). - http_generic_404() is gone - http_populate_arguments() is gone - http_body_bytes() is gone - http_body_text() is gone - http_body_read() has been added - http_populate_post() has been added - http_populate_get() has been added - http_file_read() has been added - http_file_rewind() has been added - http_file_lookup() no longer takes name, fname, data and len parameters. - http_file_lookup() now returns a struct http_file pointer. - http_populate_multipart_form() no longer takes an secondary parameter. New configuration options: - http_body_disk_offload: Number of bytes after which Kore will offload the HTTP body to disk instead of retaining it in memory. If 0 this feature is disabled. (Default: 0) - http_body_disk_path: The path where Kore will store temporary HTTP body files. (this directory does not get created if http_body_disk_offload is 0). New example: The upload example has been added, demonstrating how to deal with file uploads from a multipart form.
2016-01-18 11:30:22 +01:00
if (http_body_disk_offload > 0) {
if (mkdir(http_body_disk_path, 0700) == -1 && errno != EEXIST) {
printf("can't create http_body_disk_path '%s': %s\n",
http_body_disk_path, errno_s);
return (KORE_RESULT_ERROR);
}
}
#endif
kore_signal_setup();
kore_server_start(argc, argv);
kore_server_shutdown();
rcall = kore_runtime_getcall(parent_teardown_hook);
if (rcall != NULL) {
kore_runtime_execute(rcall);
kore_free(rcall);
}
if (unlink(kore_pidfile) == -1 && errno != ENOENT)
kore_log(LOG_NOTICE, "failed to remove pidfile (%s)", errno_s);
kore_server_cleanup();
if (!kore_quiet)
kore_log(LOG_INFO, "goodbye");
#if defined(KORE_USE_PYTHON)
kore_python_cleanup();
#endif
#if defined(KORE_USE_LUA)
kore_lua_cleanup();
#endif
kore_mem_cleanup();
return (kore_quit);
2013-04-17 22:34:27 +02:00
}
void
kore_default_getopt(int argc, char **argv)
{
int ch;
#if !defined(KORE_SINGLE_BINARY)
while ((ch = getopt(argc, argv, "c:dfhnqrv")) != -1) {
#else
while ((ch = getopt(argc, argv, "dfhnqrv")) != -1) {
#endif
switch (ch) {
#if !defined(KORE_SINGLE_BINARY)
case 'c':
free(config_file);
if ((config_file = strdup(optarg)) == NULL)
fatal("strdup");
break;
#endif
case 'd':
kore_foreground = 0;
break;
case 'f':
printf("note: -f is the default now, "
"use -d to daemonize\n");
break;
case 'h':
usage();
break;
case 'n':
skip_chroot = 1;
break;
case 'q':
kore_quiet = 1;
break;
case 'r':
skip_runas = 1;
break;
case 'v':
version();
break;
default:
usage();
}
}
}
int
kore_server_bind(struct kore_server *srv, const char *ip, const char *port,
const char *ccb)
{
int r;
struct listener *l;
struct addrinfo hints, *results;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = 0;
r = getaddrinfo(ip, port, &hints, &results);
if (r != 0)
fatal("getaddrinfo(%s): %s", ip, gai_strerror(r));
l = kore_listener_create(srv);
l->host = kore_strdup(ip);
l->port = kore_strdup(port);
if (!kore_listener_init(l, results->ai_family, ccb)) {
freeaddrinfo(results);
return (KORE_RESULT_ERROR);
}
if (bind(l->fd, results->ai_addr, results->ai_addrlen) == -1) {
kore_listener_free(l);
freeaddrinfo(results);
kore_log(LOG_ERR, "bind(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
freeaddrinfo(results);
if (listen(l->fd, kore_socket_backlog) == -1) {
kore_listener_free(l);
kore_log(LOG_ERR, "listen(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
int
kore_server_bind_unix(struct kore_server *srv, const char *path,
const char *ccb)
{
struct listener *l;
int len;
struct sockaddr_un sun;
socklen_t socklen;
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
len = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", path);
if (len == -1 || (size_t)len >= sizeof(sun.sun_path)) {
kore_log(LOG_ERR, "unix socket path '%s' too long", path);
return (KORE_RESULT_ERROR);
}
#if defined(__linux__)
if (sun.sun_path[0] == '@')
sun.sun_path[0] = '\0';
socklen = sizeof(sun.sun_family) + len;
2019-05-28 21:44:46 +02:00
#else
socklen = sizeof(sun);
#endif
l = kore_listener_create(srv);
l->host = kore_strdup(path);
if (!kore_listener_init(l, AF_UNIX, ccb))
return (KORE_RESULT_ERROR);
if (sun.sun_path[0] != '\0') {
if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
kore_log(LOG_ERR, "unlink: %s: %s",
sun.sun_path, errno_s);
kore_listener_free(l);
return (KORE_RESULT_ERROR);
}
}
if (bind(l->fd, (struct sockaddr *)&sun, socklen) == -1) {
kore_log(LOG_ERR, "bind: %s", errno_s);
kore_listener_free(l);
return (KORE_RESULT_ERROR);
}
if (listen(l->fd, kore_socket_backlog) == -1) {
kore_log(LOG_ERR, "listen(): %s", errno_s);
kore_listener_free(l);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
struct kore_server *
kore_server_create(const char *name)
{
struct kore_server *srv;
srv = kore_calloc(1, sizeof(struct kore_server));
srv->name = kore_strdup(name);
if (kore_tls_supported())
srv->tls = 1;
else
srv->tls = 0;
TAILQ_INIT(&srv->domains);
LIST_INIT(&srv->listeners);
LIST_INSERT_HEAD(&kore_servers, srv, list);
return (srv);
}
void
kore_server_finalize(struct kore_server *srv)
{
struct listener *l;
const char *proto;
if (kore_quiet)
return;
LIST_FOREACH(l, &srv->listeners, list) {
if (srv->tls)
proto = "https";
else
proto = "http";
if (l->family == AF_UNIX) {
kore_log(LOG_INFO, "%s serving %s on %s",
srv->name, proto, l->host);
} else {
kore_log(LOG_INFO, "%s serving %s on %s:%s",
srv->name, proto, l->host, l->port);
}
}
}
struct listener *
kore_listener_create(struct kore_server *server)
{
struct listener *l;
l = kore_calloc(1, sizeof(struct listener));
nlisteners++;
LIST_INSERT_HEAD(&server->listeners, l, list);
l->server = server;
l->fd = -1;
l->evt.type = KORE_TYPE_LISTENER;
l->evt.handle = kore_listener_accept;
return (l);
}
struct kore_server *
kore_server_lookup(const char *name)
{
struct kore_server *srv;
LIST_FOREACH(srv, &kore_servers, list) {
if (!strcmp(srv->name, name))
return (srv);
}
return (NULL);
}
int
kore_listener_init(struct listener *l, int family, const char *ccb)
{
switch (family) {
case AF_INET:
case AF_INET6:
case AF_UNIX:
break;
default:
fatal("unknown address family %d", family);
}
l->family = family;
if ((l->fd = socket(family, SOCK_STREAM, 0)) == -1) {
kore_listener_free(l);
kore_log(LOG_ERR, "socket(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
if (fcntl(l->fd, F_SETFD, FD_CLOEXEC) == -1) {
kore_listener_free(l);
kore_log(LOG_ERR, "fcntl(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
if (!kore_connection_nonblock(l->fd, family != AF_UNIX)) {
kore_listener_free(l);
kore_log(LOG_ERR, "kore_connection_nonblock(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
if (!kore_sockopt(l->fd, SOL_SOCKET, SO_REUSEADDR)) {
kore_listener_free(l);
return (KORE_RESULT_ERROR);
}
if (ccb != NULL) {
if ((l->connect = kore_runtime_getcall(ccb)) == NULL) {
kore_log(LOG_ERR, "no such callback: '%s'", ccb);
kore_listener_free(l);
return (KORE_RESULT_ERROR);
}
} else {
l->connect = NULL;
}
return (KORE_RESULT_OK);
}
void
kore_server_free(struct kore_server *srv)
{
struct listener *l;
struct kore_domain *dom;
LIST_REMOVE(srv, list);
while ((dom = TAILQ_FIRST(&srv->domains)) != NULL)
kore_domain_free(dom);
while ((l = LIST_FIRST(&srv->listeners)) != NULL)
kore_listener_free(l);
kore_free(srv->name);
kore_free(srv);
}
void
kore_listener_free(struct listener *l)
{
int rm;
LIST_REMOVE(l, list);
if (l->fd != -1)
close(l->fd);
rm = 0;
#if defined(__linux__)
if (worker == NULL && l->family == AF_UNIX && l->host[0] != '@')
rm++;
#else
if (worker == NULL && l->family == AF_UNIX)
rm++;
#endif
if (rm) {
if (unlink(l->host) == -1) {
kore_log(LOG_NOTICE,
"failed to remove unix socket %s (%s)", l->host,
errno_s);
}
}
kore_free(l->host);
kore_free(l->port);
kore_free(l);
}
void
kore_listener_accept(void *arg, int error)
{
struct connection *c;
struct listener *l = arg;
u_int32_t accepted;
if (error)
fatal("error on listening socket");
if (!(l->evt.flags & KORE_EVENT_READ))
return;
accepted = 0;
while (worker_active_connections < worker_max_connections) {
if (worker_accept_threshold != 0 &&
accepted >= worker_accept_threshold) {
kore_worker_make_busy();
break;
}
if (!kore_connection_accept(l, &c))
break;
if (c == NULL)
break;
accepted++;
kore_platform_event_all(c->fd, c);
}
}
int
kore_sockopt(int fd, int what, int opt)
{
int on;
on = 1;
if (setsockopt(fd, what, opt, (const char *)&on, sizeof(on)) == -1) {
kore_log(LOG_ERR, "setsockopt(): %s", errno_s);
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
void
kore_signal_setup(void)
{
kore_signal_trap(SIGHUP);
kore_signal_trap(SIGQUIT);
kore_signal_trap(SIGTERM);
kore_signal_trap(SIGUSR1);
kore_signal_trap(SIGCHLD);
if (kore_foreground)
kore_signal_trap(SIGINT);
else
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGPIPE, SIG_IGN);
}
void
kore_signal_trap(int sig)
{
struct sigaction sa;
sig_recv = 0;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = kore_signal;
if (sigfillset(&sa.sa_mask) == -1)
fatal("sigfillset: %s", errno_s);
if (sigaction(sig, &sa, NULL) == -1)
fatal("sigaction: %s", errno_s);
}
void
kore_server_closeall(void)
{
struct listener *l;
struct kore_server *srv;
LIST_FOREACH(srv, &kore_servers, list) {
LIST_FOREACH(l, &srv->listeners, list)
l->fd = -1;
}
}
void
kore_server_cleanup(void)
{
struct kore_server *srv;
while ((srv = LIST_FIRST(&kore_servers)) != NULL)
kore_server_free(srv);
}
2013-06-26 11:18:32 +02:00
void
kore_signal(int sig)
{
sig_recv = sig;
}
void
kore_shutdown(void)
{
if (worker != NULL) {
kore_msg_send(KORE_MSG_PARENT, KORE_MSG_SHUTDOWN, NULL, 0);
return;
}
fatal("kore_shutdown: called from parent");
}
void
kore_proctitle(const char *title)
{
int len;
kore_argv[1] = NULL;
len = snprintf(kore_argv[0], proctitle_maxlen, "%s %s",
basename(kore_progname), title);
if (len == -1 || (size_t)len >= proctitle_maxlen)
fatal("proctitle '%s' too large", title);
memset(kore_argv[0] + len, 0, proctitle_maxlen - len);
}
void
kore_hooks_set(const char *config, const char *teardown, const char *daemonized)
{
parent_config_hook = config;
parent_teardown_hook = teardown;
#if defined(KORE_SINGLE_BINARY)
parent_daemonized_hook = daemonized;
#endif
}
static void
kore_proctitle_setup(void)
{
int i;
char *p;
proctitle_maxlen = 0;
for (i = 0; environ[i] != NULL; i++) {
if ((p = strdup(environ[i])) == NULL)
fatal("strdup");
proctitle_maxlen += strlen(environ[i]) + 1;
environ[i] = p;
}
for (i = 0; kore_argv[i] != NULL; i++)
proctitle_maxlen += strlen(kore_argv[i]) + 1;
}
2013-06-26 11:18:32 +02:00
static void
kore_server_start(int argc, char *argv[])
2013-06-26 11:18:32 +02:00
{
u_int32_t tmp;
struct kore_server *srv;
u_int64_t netwait;
int last_sig;
#if !defined(KORE_NO_HTTP)
int alog;
struct kore_domain *dom;
#endif
#if defined(KORE_SINGLE_BINARY)
2019-09-26 16:05:01 +02:00
struct kore_runtime_call *rcall;
#endif
if (!kore_quiet) {
kore_log(LOG_INFO, "%s %s starting, built=%s",
__progname, kore_version, kore_build_date);
kore_log(LOG_INFO, "memory pool protections: %s",
kore_mem_guard ? "enabled" : "disabled");
kore_log(LOG_INFO, "built-ins: "
#if defined(__linux__)
"seccomp "
#endif
#if defined(KORE_USE_PGSQL)
"pgsql "
#endif
#if defined(KORE_USE_TASKS)
"tasks "
#endif
#if defined(KORE_USE_JSONRPC)
"jsonrpc "
#endif
#if defined(KORE_USE_PYTHON)
"python "
#endif
#if defined(KORE_USE_ACME)
"acme "
#endif
#if defined(KORE_USE_CURL)
"curl "
2023-01-23 21:56:49 +01:00
#endif
#if defined(KORE_USE_LUA)
"lua "
#endif
);
}
kore_tls_log_version();
2021-01-11 23:35:16 +01:00
if (kore_foreground == 0) {
if (daemon(1, 0) == -1)
fatal("cannot daemon(): %s", errno_s);
#if defined(KORE_SINGLE_BINARY)
rcall = kore_runtime_getcall(parent_daemonized_hook);
if (rcall != NULL) {
kore_runtime_execute(rcall);
kore_free(rcall);
}
#endif
}
2013-06-26 11:18:32 +02:00
kore_pid = getpid();
kore_write_kore_pid();
2013-06-26 11:18:32 +02:00
#if !defined(KORE_SINGLE_BINARY)
if (kore_runtime_count() == 0 || rarg0 == NULL)
kore_call_parent_configure(argc, argv);
#endif
#if defined(KORE_USE_PYTHON)
kore_python_routes_resolve();
#endif
/* Check if keymgr will be active. */
if (kore_tls_supported()) {
LIST_FOREACH(srv, &kore_servers, list) {
if (srv->tls) {
kore_keymgr_active = 1;
break;
}
}
} else {
kore_keymgr_active = 0;
}
kore_platform_proctitle("[parent]");
if (!kore_worker_init()) {
kore_log(LOG_ERR, "last worker log lines:");
kore_log(LOG_ERR, "=====================================");
net_init();
kore_connection_init();
kore_platform_event_init();
kore_msg_parent_init();
kore_platform_event_wait(10);
kore_worker_dispatch_signal(SIGQUIT);
kore_log(LOG_ERR, "=====================================");
return;
}
2013-06-26 11:18:32 +02:00
/* Set worker_max_connections for kore_connection_init(). */
tmp = worker_max_connections;
worker_max_connections = worker_count;
net_init();
kore_connection_init();
kore_platform_event_init();
kore_msg_parent_init();
worker_max_connections = tmp;
kore_timer_init();
2018-12-22 09:41:55 +01:00
#if !defined(KORE_NO_HTTP)
alog = 0;
LIST_FOREACH(srv, &kore_servers, list) {
TAILQ_FOREACH(dom, &srv->domains, list) {
if (dom->accesslog != -1)
alog = 1;
}
}
if (alog) {
kore_timer_add(kore_accesslog_run, 100, NULL, 0);
kore_log(LOG_INFO, "accesslog vacuum is enabled");
}
2018-12-22 09:41:55 +01:00
#endif
#if defined(KORE_USE_PYTHON)
kore_msg_unregister(KORE_PYTHON_SEND_OBJ);
#endif
while (kore_quit == KORE_QUIT_NONE) {
last_sig = sig_recv;
if (last_sig != 0) {
switch (last_sig) {
case SIGHUP:
kore_worker_dispatch_signal(last_sig);
kore_module_reload(0);
break;
case SIGINT:
case SIGQUIT:
case SIGTERM:
kore_quit = KORE_QUIT_NORMAL;
kore_worker_dispatch_signal(last_sig);
continue;
case SIGUSR1:
kore_worker_dispatch_signal(last_sig);
break;
case SIGCHLD:
kore_worker_reap();
break;
default:
break;
}
if (sig_recv == last_sig)
sig_recv = 0;
else
continue;
}
netwait = kore_timer_next_run(kore_time_ms());
kore_platform_event_wait(netwait);
kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT);
kore_timer_run(kore_time_ms());
kore_worker_reap();
}
kore_worker_dispatch_signal(SIGQUIT);
}
static void
kore_server_shutdown(void)
{
if (!kore_quiet)
kore_log(LOG_INFO, "server shutting down");
kore_worker_shutdown();
2018-12-22 09:41:55 +01:00
#if !defined(KORE_NO_HTTP)
kore_accesslog_gather(NULL, kore_time_ms(), 1);
2018-12-22 09:41:55 +01:00
#endif
2016-01-04 22:40:14 +01:00
kore_platform_event_cleanup();
kore_connection_cleanup();
kore_domain_cleanup();
kore_tls_cleanup();
2016-01-04 22:40:14 +01:00
net_cleanup();
2013-06-26 11:18:32 +02:00
}
static void
2013-06-26 11:18:32 +02:00
kore_write_kore_pid(void)
{
FILE *fp;
if ((fp = fopen(kore_pidfile, "w+")) == NULL) {
printf("warning: couldn't write pid to %s (%s)\n",
kore_pidfile, errno_s);
} else {
2013-06-26 11:18:32 +02:00
fprintf(fp, "%d\n", kore_pid);
fclose(fp);
}
}
static void
kore_call_parent_configure(int argc, char **argv)
{
struct kore_runtime_call *rcall;
rcall = kore_runtime_getcall(parent_config_hook);
if (rcall != NULL) {
kore_runtime_configure(rcall, argc, argv);
kore_free(rcall);
}
}