From ca5cf157dd31590edde72b6a20a05966e7b61794 Mon Sep 17 00:00:00 2001 From: Dmitry Simonenko Date: Wed, 24 May 2017 14:57:15 +0300 Subject: [PATCH] odissey: begin reconstruction for new arch --- CMakeLists.txt | 2 +- lib/machinarium | 2 +- src/od.c | 150 +++++++++ src/od.h | 25 ++ src/od_config.c | 720 +++++++++++++++++++++++++++++++++++++++++ src/od_config.h | 70 ++++ src/od_daemon.c | 45 +++ src/od_daemon.h | 12 + src/od_lex.c | 273 ++++++++++++++++ src/od_lex.h | 64 ++++ src/od_list.h | 68 ++++ src/od_log.c | 91 ++++++ src/od_log.h | 62 ++++ src/od_macro.h | 16 + src/od_pid.c | 52 +++ src/od_pid.h | 21 ++ src/od_scheme.c | 451 ++++++++++++++++++++++++++ src/od_scheme.h | 159 +++++++++ src/od_syslog.c | 88 +++++ src/od_syslog.h | 33 ++ src/od_version.h | 13 + src/od_version.h.cmake | 13 + src/odissey.c | 33 ++ 23 files changed, 2461 insertions(+), 2 deletions(-) create mode 100644 src/od.c create mode 100644 src/od.h create mode 100644 src/od_config.c create mode 100644 src/od_config.h create mode 100644 src/od_daemon.c create mode 100644 src/od_daemon.h create mode 100644 src/od_lex.c create mode 100644 src/od_lex.h create mode 100644 src/od_list.h create mode 100644 src/od_log.c create mode 100644 src/od_log.h create mode 100644 src/od_macro.h create mode 100644 src/od_pid.c create mode 100644 src/od_pid.h create mode 100644 src/od_scheme.c create mode 100644 src/od_scheme.h create mode 100644 src/od_syslog.c create mode 100644 src/od_syslog.h create mode 100644 src/od_version.h create mode 100644 src/od_version.h.cmake create mode 100644 src/odissey.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c50c7a0..1bcfad20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,4 +49,4 @@ message (STATUS "") message (STATUS "ODISSEY (version: ${OD_VERSION_GIT} ${OD_VERSION_BUILD})") message (STATUS "") -add_subdirectory(core) +add_subdirectory(src) diff --git a/lib/machinarium b/lib/machinarium index cb588f49..06987986 160000 --- a/lib/machinarium +++ b/lib/machinarium @@ -1 +1 @@ -Subproject commit cb588f49734eb302b98dc372990eed45facacf0b +Subproject commit 069879865159c1f082bc036db7a974d867f4d191 diff --git a/src/od.c b/src/od.c new file mode 100644 index 00000000..29f336a7 --- /dev/null +++ b/src/od.c @@ -0,0 +1,150 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "od_macro.h" +#include "od_version.h" +#include "od_list.h" +#include "od_pid.h" +#include "od_syslog.h" +#include "od_log.h" +#include "od_daemon.h" +#include "od_scheme.h" +#include "od_lex.h" +#include "od_config.h" +#include "od.h" + +/* +#include "od_stat.h" +#include "od_server.h" +#include "od_server_pool.h" +#include "od_client.h" +#include "od_client_list.h" +#include "od_client_pool.h" +#include "od_route_id.h" +#include "od_route.h" +#include "od_route_pool.h" +#include "od_pooler.h" +*/ + +void od_init(od_t *od) +{ + od_pidinit(&od->pid); + od_syslog_init(&od->syslog); + od_loginit(&od->log, &od->pid, &od->syslog); + od_schemeinit(&od->scheme); + od_configinit(&od->config, &od->log, &od->scheme); + + signal(SIGPIPE, SIG_IGN); +} + +void od_free(od_t *od) +{ + if (od->scheme.pid_file) + od_pidfile_unlink(&od->pid, od->scheme.pid_file); + od_schemefree(&od->scheme); + od_configclose(&od->config); + od_logclose(&od->log); + od_syslog_close(&od->syslog); +} + +static inline void +od_usage(od_t *od, char *path) +{ + od_log(&od->log, NULL, "odissey (version: %s %s)", + OD_VERSION_GIT, + OD_VERSION_BUILD); + od_log(&od->log, NULL, "usage: %s ", path); +} + +int od_main(od_t *od, int argc, char **argv) +{ + /* validate command line options */ + if (argc != 2) { + od_usage(od, argv[0]); + return 1; + } + char *config_file; + if (argc == 2) { + if (strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "--help") == 0) { + od_usage(od, argv[0]); + return 0; + } + config_file = argv[1]; + } + /* read config file */ + int rc; + rc = od_configopen(&od->config, config_file); + if (rc == -1) + return 1; + rc = od_configparse(&od->config); + if (rc == -1) + return 1; + /* set log verbosity level */ + od_logset_verbosity(&od->log, od->scheme.log_verbosity); + /* run as daemon */ + if (od->scheme.daemonize) { + rc = od_daemonize(); + if (rc == -1) + return 1; + /* update pid */ + od_pidinit(&od->pid); + } + /* reopen log file after config parsing */ + if (od->scheme.log_file) { + rc = od_logopen(&od->log, od->scheme.log_file); + if (rc == -1) { + od_error(&od->log, NULL, "failed to open log file '%s'", + od->scheme.log_file); + return 1; + } + } + /* syslog */ + if (od->scheme.syslog) { + od_syslog_open(&od->syslog, + od->scheme.syslog_ident, + od->scheme.syslog_facility); + } + od_log(&od->log, NULL, "odissey (version: %s %s)", + OD_VERSION_GIT, + OD_VERSION_BUILD); + od_log(&od->log, NULL, ""); + /* validate configuration scheme */ + rc = od_schemevalidate(&od->scheme, &od->log); + if (rc == -1) + return 1; + /* print configuration scheme */ + if (od->scheme.log_verbosity >= 1) { + od_schemeprint(&od->scheme, &od->log); + od_log(&od->log, NULL, ""); + } + /* create pid file */ + if (od->scheme.pid_file) + od_pidfile_create(&od->pid, od->scheme.pid_file); + +#if 0 + /* run connection pooler */ + od_pooler_t pooler; + rc = od_pooler_init(&pooler, od); + if (rc == -1) + return 1; + rc = od_pooler_start(&pooler); + if (rc == -1) + return 1; +#endif + return 0; +} diff --git a/src/od.h b/src/od.h new file mode 100644 index 00000000..d4c79c4f --- /dev/null +++ b/src/od.h @@ -0,0 +1,25 @@ +#ifndef OD_H_ +#define OD_H_ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od od_t; + +struct od +{ + od_pid_t pid; + od_syslog_t syslog; + od_log_t log; + od_config_t config; + od_scheme_t scheme; +}; + +void od_init(od_t*); +void od_free(od_t*); +int od_main(od_t*, int, char**); + +#endif /* OD_H */ diff --git a/src/od_config.c b/src/od_config.c new file mode 100644 index 00000000..9b4fe450 --- /dev/null +++ b/src/od_config.c @@ -0,0 +1,720 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "od_macro.h" +#include "od_list.h" +#include "od_pid.h" +#include "od_syslog.h" +#include "od_log.h" +#include "od_scheme.h" +#include "od_lex.h" +#include "od_config.h" + +#define od_keyword(name, token) { name, sizeof(name) - 1, token } + +static od_keyword_t od_config_keywords[] = +{ + /* main */ + od_keyword("odissey", OD_LODISSEY), + od_keyword("yes", OD_LYES), + od_keyword("no", OD_LNO), + od_keyword("on", OD_LON), + od_keyword("off", OD_LOFF), + od_keyword("daemonize", OD_LDAEMONIZE), + od_keyword("log_verbosity", OD_LLOG_VERBOSITY), + od_keyword("log_file", OD_LLOG_FILE), + od_keyword("pid_file", OD_LPID_FILE), + od_keyword("syslog", OD_LSYSLOG), + od_keyword("syslog_ident", OD_LSYSLOG_IDENT), + od_keyword("syslog_facility", OD_LSYSLOG_FACILITY), + od_keyword("stats_period", OD_LSTATS_PERIOD), + od_keyword("pooling", OD_LPOOLING), + /* listen */ + od_keyword("listen", OD_LLISTEN), + od_keyword("host", OD_LHOST), + od_keyword("port", OD_LPORT), + od_keyword("backlog", OD_LBACKLOG), + od_keyword("nodelay", OD_LNODELAY), + od_keyword("keepalive", OD_LKEEPALIVE), + od_keyword("readahead", OD_LREADAHEAD), + od_keyword("workers", OD_LWORKERS), + od_keyword("client_max", OD_LCLIENT_MAX), + od_keyword("tls_mode", OD_LTLS_MODE), + od_keyword("tls_ca_file", OD_LTLS_CA_FILE), + od_keyword("tls_key_file", OD_LTLS_KEY_FILE), + od_keyword("tls_cert_file", OD_LTLS_CERT_FILE), + od_keyword("tls_protocols", OD_LTLS_PROTOCOLS), + /* server */ + od_keyword("server", OD_LSERVER), + /* routing */ + od_keyword("routing", OD_LROUTING), + od_keyword("default", OD_LDEFAULT), + od_keyword("mode", OD_LMODE), + od_keyword("database", OD_LDATABASE), + od_keyword("user", OD_LUSER), + od_keyword("password", OD_LPASSWORD), + od_keyword("ttl", OD_LTTL), + od_keyword("cancel", OD_LCANCEL), + od_keyword("discard", OD_LDISCARD), + od_keyword("rollback", OD_LROLLBACK), + od_keyword("pool_size", OD_LPOOL_SIZE), + od_keyword("pool_timeout", OD_LPOOL_TIMEOUT), + /* users */ + od_keyword("authentication", OD_LAUTHENTICATION), + od_keyword("users", OD_LUSERS), + od_keyword("deny", OD_LDENY), + { NULL, 0, 0 } +}; + +void +od_configinit(od_config_t *config, + od_log_t *log, + od_scheme_t *scheme) +{ + od_lexinit(&config->lex); + config->log = log; + config->scheme = scheme; +} + +int +od_configopen(od_config_t *config, char *file) +{ + /* read file */ + struct stat st; + int rc = lstat(file, &st); + if (rc == -1) { + od_error(config->log, NULL, "failed to open config file '%s'", + file); + return -1; + } + char *config_buf = malloc(st.st_size); + if (config_buf == NULL) { + od_error(config->log, NULL, "memory allocation error"); + return -1; + } + FILE *f = fopen(file, "r"); + if (f == NULL) { + free(config_buf); + od_error(config->log, NULL, "failed to open config file '%s'", + file); + return -1; + } + rc = fread(config_buf, st.st_size, 1, f); + fclose(f); + if (rc != 1) { + free(config_buf); + od_error(config->log, NULL, "failed to open config file '%s'", + file); + return -1; + } + od_lexopen(&config->lex, od_config_keywords, config_buf, + st.st_size); + config->scheme->config_file = file; + return 0; +} + +void +od_configclose(od_config_t *config) +{ + od_lexfree(&config->lex); +} + +static void +od_configerror(od_config_t *config, od_token_t *tk, char *fmt, ...) +{ + char msg[256]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + int line = config->lex.line; + if (tk) + line = tk->line; + od_error(config->log, NULL, "%s:%d %s", + config->scheme->config_file, line, msg); +} + +static int +od_confignext(od_config_t *config, int id, od_token_t **tk) +{ + od_token_t *tkp = NULL; + int token = od_lexpop(&config->lex, &tkp); + if (token == OD_LERROR) { + od_configerror(config, NULL, "%s", config->lex.error); + return -1; + } + if (tk) { + *tk = tkp; + } + if (token != id) { + if (id < 0xff && ispunct(id)) { + od_configerror(config, tkp, "expected '%c'", id); + return -1; + } + od_configerror(config, tkp, "expected '%s'", + od_lexname_of(&config->lex, id)); + return -1; + } + return 0; +} + +static int +od_confignext_yes_no(od_config_t *config, od_token_t **tk) +{ + int rc; + rc = od_lexpop(&config->lex, tk); + if (rc == OD_LYES) + return 1; + if (rc == OD_LNO) + return 0; + od_configerror(config, *tk, "expected yes/no"); + return -1; +} + +static int +od_configparse_listen(od_config_t *config) +{ + if (od_confignext(config, '{', NULL) == -1) + return -1; + od_token_t *tk; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* host */ + case OD_LHOST: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->host = tk->v.string; + continue; + /* port */ + case OD_LPORT: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->port = tk->v.num; + continue; + /* backlog */ + case OD_LBACKLOG: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->backlog = tk->v.num; + continue; + /* nodelay */ + case OD_LNODELAY: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + config->scheme->nodelay = rc; + continue; + /* keepalive */ + case OD_LKEEPALIVE: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->keepalive = tk->v.num; + continue; + /* readahead */ + case OD_LREADAHEAD: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->readahead = tk->v.num; + continue; + /* client_max */ + case OD_LCLIENT_MAX: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->client_max = tk->v.num; + continue; + /* workers */ + case OD_LWORKERS: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->workers = tk->v.num; + continue; + /* tls_mode */ + case OD_LTLS_MODE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->tls_mode = tk->v.string; + continue; + /* tls_ca_file */ + case OD_LTLS_CA_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->tls_ca_file = tk->v.string; + continue; + /* tls_key_file */ + case OD_LTLS_KEY_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->tls_key_file = tk->v.string; + continue; + /* tls_cert_file */ + case OD_LTLS_CERT_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->tls_cert_file = tk->v.string; + continue; + /* tls_protocols */ + case OD_LTLS_PROTOCOLS: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->tls_protocols = tk->v.string; + continue; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +static int +od_configparse_server(od_config_t *config) +{ + od_schemeserver_t *server = + od_schemeserver_add(config->scheme); + if (server == NULL) + return -1; + od_token_t *tk; + int rc; + /* name */ + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->name = tk->v.string; + if (od_confignext(config, '{', NULL) == -1) + return -1; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* host */ + case OD_LHOST: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->host = tk->v.string; + continue; + /* port */ + case OD_LPORT: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + server->port = tk->v.num; + continue; + /* tls_mode */ + case OD_LTLS_MODE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->tls_mode = tk->v.string; + continue; + /* tls_ca_file */ + case OD_LTLS_CA_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->tls_ca_file = tk->v.string; + continue; + /* tls_key_file */ + case OD_LTLS_KEY_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->tls_key_file = tk->v.string; + continue; + /* tls_cert_file */ + case OD_LTLS_CERT_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->tls_cert_file = tk->v.string; + continue; + /* tls_protocols */ + case OD_LTLS_PROTOCOLS: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + server->tls_protocols = tk->v.string; + continue; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +static int +od_configparse_route(od_config_t *config, od_token_t *name) +{ + od_schemeroute_t *route = + od_schemeroute_add(config->scheme); + if (route == NULL) + return -1; + if (name == NULL) { + route->is_default = 1; + route->target = ""; + } else { + route->target = name->v.string; + } + if (od_confignext(config, '{', NULL) == -1) + return -1; + od_token_t *tk; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* server */ + case OD_LSERVER: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + route->route = tk->v.string; + continue; + /* client_max */ + case OD_LCLIENT_MAX: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + route->client_max = tk->v.num; + continue; + /* pool_size */ + case OD_LPOOL_SIZE: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + route->pool_size = tk->v.num; + continue; + /* pool_timeout */ + case OD_LPOOL_TIMEOUT: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + route->pool_timeout = tk->v.num; + continue; + /* database */ + case OD_LDATABASE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + route->database = tk->v.string; + continue; + /* user */ + case OD_LUSER: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + route->user = tk->v.string; + route->user_len = strlen(route->user); + continue; + /* password */ + case OD_LPASSWORD: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + route->password = tk->v.string; + route->password_len = strlen(route->password); + continue; + /* ttl */ + case OD_LTTL: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + route->ttl = tk->v.num; + continue; + /* cancel */ + case OD_LCANCEL: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + route->cancel = rc; + continue; + /* discard */ + case OD_LDISCARD: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + route->discard = rc; + continue; + /* rollback */ + case OD_LROLLBACK: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + route->rollback = rc; + continue; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +static int +od_configparse_routing(od_config_t *config) +{ + if (od_confignext(config, '{', NULL) == -1) + return -1; + od_token_t *tk; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* mode */ + case OD_LMODE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->routing = tk->v.string; + continue; + /* route (database name) */ + case OD_LSTRING: + rc = od_configparse_route(config, tk); + if (rc == -1) + return -1; + break; + /* route default */ + case OD_LDEFAULT: + rc = od_configparse_route(config, NULL); + if (rc == -1) + return -1; + break; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +static int +od_configparse_user(od_config_t *config, od_token_t *name) +{ + od_schemeuser_t *user = + od_schemeuser_add(config->scheme); + if (user == NULL) + return -1; + if (name == NULL) { + user->is_default = 1; + user->user = ""; + } else { + user->user = name->v.string; + } + if (od_confignext(config, '{', NULL) == -1) + return -1; + od_token_t *tk; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* authentication */ + case OD_LAUTHENTICATION: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + user->auth = tk->v.string; + break; + /* password */ + case OD_LPASSWORD: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + user->password = tk->v.string; + user->password_len = strlen(user->password); + continue; + /* deny */ + case OD_LDENY: + user->is_deny = 1; + continue; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +static int +od_configparse_users(od_config_t *config) +{ + if (od_confignext(config, '{', NULL) == -1) + return -1; + od_token_t *tk; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* user */ + case OD_LSTRING: + rc = od_configparse_user(config, tk); + if (rc == -1) + return -1; + break; + /* user default */ + case OD_LDEFAULT: + rc = od_configparse_user(config, NULL); + if (rc == -1) + return -1; + break; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} + +int +od_configparse(od_config_t *config) +{ + od_token_t *tk; + if (od_confignext(config, OD_LODISSEY, NULL) == -1) + return -1; + if (od_confignext(config, '{', NULL) == -1) + return -1; + int rc; + int eof = 0; + while (! eof) + { + rc = od_lexpop(&config->lex, &tk); + switch (rc) { + /* daemonize */ + case OD_LDAEMONIZE: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + config->scheme->daemonize = rc; + continue; + /* log_verbosity */ + case OD_LLOG_VERBOSITY: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->log_verbosity = tk->v.num; + continue; + /* log_file */ + case OD_LLOG_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->log_file = tk->v.string; + continue; + /* pid_file */ + case OD_LPID_FILE: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->pid_file = tk->v.string; + continue; + /* syslog */ + case OD_LSYSLOG: + rc = od_confignext_yes_no(config, &tk); + if (rc == -1) + return -1; + config->scheme->syslog = rc; + continue; + /* syslog_ident */ + case OD_LSYSLOG_IDENT: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->syslog_ident = tk->v.string; + continue; + /* syslog_facility */ + case OD_LSYSLOG_FACILITY: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->syslog_facility = tk->v.string; + continue; + /* stats_period */ + case OD_LSTATS_PERIOD: + if (od_confignext(config, OD_LNUMBER, &tk) == -1) + return -1; + config->scheme->stats_period = tk->v.num; + continue; + /* pooling */ + case OD_LPOOLING: + if (od_confignext(config, OD_LSTRING, &tk) == -1) + return -1; + config->scheme->pooling = tk->v.string; + continue; + /* listen */ + case OD_LLISTEN: + rc = od_configparse_listen(config); + if (rc == -1) + return -1; + continue; + /* server */ + case OD_LSERVER: + rc = od_configparse_server(config); + if (rc == -1) + return -1; + continue; + /* routing */ + case OD_LROUTING: + rc = od_configparse_routing(config); + if (rc == -1) + return -1; + continue; + /* users */ + case OD_LUSERS: + rc = od_configparse_users(config); + if (rc == -1) + return -1; + continue; + case OD_LEOF: + od_configerror(config, tk, "unexpected end of config file"); + return -1; + case '}': + eof = 1; + continue; + default: + od_configerror(config, tk, "unknown option"); + return -1; + } + } + return 0; +} diff --git a/src/od_config.h b/src/od_config.h new file mode 100644 index 00000000..564d23cb --- /dev/null +++ b/src/od_config.h @@ -0,0 +1,70 @@ +#ifndef OD_CONFIG_H +#define OD_CONFIG_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +enum +{ + OD_LODISSEY = OD_LCUSTOM, + OD_LYES, + OD_LNO, + OD_LON, + OD_LOFF, + OD_LDAEMONIZE, + OD_LLOG_VERBOSITY, + OD_LLOG_FILE, + OD_LPID_FILE, + OD_LSYSLOG, + OD_LSYSLOG_IDENT, + OD_LSYSLOG_FACILITY, + OD_LSTATS_PERIOD, + OD_LPOOLING, + OD_LLISTEN, + OD_LHOST, + OD_LPORT, + OD_LBACKLOG, + OD_LNODELAY, + OD_LKEEPALIVE, + OD_LREADAHEAD, + OD_LWORKERS, + OD_LCLIENT_MAX, + OD_LTLS_MODE, + OD_LTLS_CA_FILE, + OD_LTLS_KEY_FILE, + OD_LTLS_CERT_FILE, + OD_LTLS_PROTOCOLS, + OD_LSERVER, + OD_LROUTING, + OD_LDEFAULT, + OD_LMODE, + OD_LDATABASE, + OD_LUSER, + OD_LPASSWORD, + OD_LTTL, + OD_LCANCEL, + OD_LDISCARD, + OD_LROLLBACK, + OD_LPOOL_SIZE, + OD_LPOOL_TIMEOUT, + OD_LAUTHENTICATION, + OD_LUSERS, + OD_LDENY +}; + +typedef struct +{ + od_lex_t lex; + od_log_t *log; + od_scheme_t *scheme; +} od_config_t; + +void od_configinit(od_config_t*, od_log_t*, od_scheme_t*); +int od_configopen(od_config_t*, char*); +void od_configclose(od_config_t*); +int od_configparse(od_config_t*); + +#endif /* OD_CONFIG_H */ diff --git a/src/od_daemon.c b/src/od_daemon.c new file mode 100644 index 00000000..02de82d7 --- /dev/null +++ b/src/od_daemon.c @@ -0,0 +1,45 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "od_macro.h" +#include "od_daemon.h" + +int od_daemonize(void) +{ + pid_t pid = fork(); + if (pid < 0) + return -1; + if (pid > 0) { + /* shutdown parent */ + _exit(0); + } + setsid(); + int fd; + fd = open("/dev/null", O_RDWR); + if (fd < 0) + return -1; + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) + close(fd); + return 0; +} diff --git a/src/od_daemon.h b/src/od_daemon.h new file mode 100644 index 00000000..9d62f9eb --- /dev/null +++ b/src/od_daemon.h @@ -0,0 +1,12 @@ +#ifndef OD_DAEMON_H +#define OD_DAEMON_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +int od_daemonize(void); + +#endif /* OD_DAEMON_H */ diff --git a/src/od_lex.c b/src/od_lex.c new file mode 100644 index 00000000..5221e343 --- /dev/null +++ b/src/od_lex.c @@ -0,0 +1,273 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include +#include + +#include "od_macro.h" +#include "od_list.h" +#include "od_lex.h" + +void od_lexinit(od_lex_t *lex) +{ + memset(lex, 0, sizeof(*lex)); + od_listinit(&lex->stack); + od_listinit(&lex->list); +} + +void od_lexopen(od_lex_t *lex, od_keyword_t *list, char *buf, int size) +{ + lex->buf = buf; + lex->size = size; + lex->keywords = list; +} + +void od_lexfree(od_lex_t *lex) +{ + od_list_t *i, *n; + od_listforeach_safe(&lex->list, i, n) { + od_token_t *tk = od_container_of(i, od_token_t, link_alloc); + if (tk->id == OD_LSTRING || + tk->id == OD_LID) { + free(tk->v.string); + } + free(tk); + } + if (lex->buf) + free(lex->buf); + if (lex->error) + free(lex->error); +} + +char *od_lexname_of(od_lex_t *lex, int id) +{ + switch (id) { + case OD_LEOF: return "eof"; + case OD_LERROR: return "error"; + case OD_LNUMBER: return "number"; + case OD_LSTRING: return "string"; + case OD_LID: return "name"; + case OD_LPUNCT: return "punctuation"; + } + int i; + for (i = 0 ; lex->keywords[i].name ; i++) + if (lex->keywords[i].id == id) + return lex->keywords[i].name; + return NULL; +} + +void od_lexpush(od_lex_t *lex, od_token_t *tk) +{ + od_listpush(&lex->stack, &tk->link); + lex->count++; +} + +static int +od_lexerror(od_lex_t *lex, const char *fmt, ...) +{ + if (fmt == NULL) + return OD_LEOF; + if (lex->error) + free(lex->error); + char msg[256]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + lex->error = strdup(msg); + return OD_LERROR; +} + +static inline od_token_t* +od_lexalloc(od_lex_t *lex, int id, int line) +{ + od_token_t *tk = malloc(sizeof(od_token_t)); + if (tk == NULL) + return NULL; + memset(tk, 0, sizeof(*tk)); + tk->id = id; + tk->line = line; + od_listinit(&tk->link); + od_listinit(&tk->link_alloc); + od_listappend(&lex->list, &tk->link_alloc); + return tk; +} + +static inline int +od_lexnext(od_lex_t *lex) { + if (lex->pos == lex->size) + return 0; + lex->pos++; + return 1; +} + +static inline uint8_t +od_lexchar(od_lex_t *lex) { + return *(lex->buf + lex->pos); +} + +static inline od_token_t* +od_lexpop_stack(od_lex_t *lex) +{ + if (lex->count == 0) + return NULL; + od_token_t *tk = od_container_of(lex->stack.next, od_token_t, link); + od_listunlink(&tk->link); + lex->count--; + return tk; +} + +int od_lexpop(od_lex_t *lex, od_token_t **result) +{ + /* stack first */ + if (lex->count) { + *result = od_lexpop_stack(lex); + if ((*result)->id == OD_LPUNCT) + return (*result)->v.num; + return (*result)->id; + } + + /* skip white-spaces and comments */ + unsigned char ch; + while (1) { + if (lex->pos == lex->size) { + *result = od_lexalloc(lex, OD_LEOF, lex->line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + return OD_LEOF; + } + ch = od_lexchar(lex); + if (isspace(ch)) { + if (ch == '\n') { + if (((lex->pos + 1) != lex->size)) + lex->line++; + } + od_lexnext(lex); + continue; + } + if (ch == '#') { + while (1) { + if (lex->pos == lex->size) { + *result = od_lexalloc(lex, OD_LEOF, lex->line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + return OD_LEOF; + } + od_lexnext(lex); + ch = od_lexchar(lex); + if (ch == '\n') { + if (((lex->pos + 1) != lex->size)) + lex->line++; + od_lexnext(lex); + break; + } + } + continue; + } + break; + } + + /* token position */ + int line = lex->line; + int start = lex->pos; + int size = 0; + + /* string */ + ch = od_lexchar(lex); + if (ch == '\"') { + start++; + while (1) { + if (od_lexnext(lex) == 0) + return od_lexerror(lex, "bad string definition"); + ch = od_lexchar(lex); + if (ch == '\"') + break; + if (ch == '\n') + return od_lexerror(lex, "bad string definition"); + } + size = lex->pos - start; + od_lexnext(lex); + *result = od_lexalloc(lex, OD_LSTRING, line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + od_token_t *tk = *result; + tk->v.string = malloc(size + 1); + if (tk->v.string == NULL) + return od_lexerror(lex, "memory allocation error"); + memcpy(tk->v.string, lex->buf + start, size); + tk->v.string[size] = 0; + return OD_LSTRING; + } + + /* punctuation */ + if (ispunct(ch) && ch != '_') { + od_lexnext(lex); + *result = od_lexalloc(lex, OD_LPUNCT, line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + (*result)->v.num = ch; + return ch; + } + + /* numeric value */ + if (isdigit(ch)) { + int64_t num = 0; + while (1) { + ch = od_lexchar(lex); + if (isdigit(ch)) + num = (num * 10) + ch - '0'; + else + break; + if (od_lexnext(lex) == 0) + break; + } + *result = od_lexalloc(lex, OD_LNUMBER, line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + (*result)->v.num = num; + return OD_LNUMBER; + } + + /* skip to the end of lexem */ + while (1) { + ch = od_lexchar(lex); + if (isspace(ch) || (ispunct(ch) && ch != '_')) + break; + if (od_lexnext(lex) == 0) + break; + } + size = lex->pos - start; + + /* match keyword */ + int i = 0; + for ( ; lex->keywords[i].name ; i++) { + if (lex->keywords[i].size != size) + continue; + if (strncasecmp(lex->keywords[i].name, lex->buf + start, size) == 0) { + *result = od_lexalloc(lex, lex->keywords[i].id, line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + return lex->keywords[i].id; + } + } + + /* identification */ + *result = od_lexalloc(lex, OD_LID, line); + if (*result == NULL) + return od_lexerror(lex, "memory allocation error"); + od_token_t *tk = *result; + tk->v.string = malloc(size + 1); + if (tk->v.string == NULL) + return od_lexerror(lex, "memory allocation error"); + memcpy(tk->v.string, lex->buf + start, size); + tk->v.string[size] = 0; + return OD_LID; +} diff --git a/src/od_lex.h b/src/od_lex.h new file mode 100644 index 00000000..2389515d --- /dev/null +++ b/src/od_lex.h @@ -0,0 +1,64 @@ +#ifndef OD_LEX_H +#define OD_LEX_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_keyword od_keyword_t; +typedef struct od_token od_token_t; +typedef struct od_lex od_lex_t; + +enum +{ + OD_LERROR = -1, + OD_LEOF = 0, + OD_LNUMBER = 1000, + OD_LPUNCT, + OD_LID, + OD_LSTRING, + OD_LCUSTOM +}; + +struct od_keyword +{ + char *name; + int size; + int id; +}; + +struct od_token +{ + int id; + union { + uint64_t num; + char *string; + } v; + int line; + od_list_t link_alloc; + od_list_t link; +}; + +struct od_lex +{ + char *buf; + int size; + int pos; + int line; + od_keyword_t *keywords; + int count; + od_list_t stack; + od_list_t list; + char *error; +}; + +void od_lexinit(od_lex_t*); +void od_lexopen(od_lex_t*, od_keyword_t*, char*, int); +void od_lexfree(od_lex_t*); +char *od_lexname_of(od_lex_t*, int); +void od_lexpush(od_lex_t*, od_token_t*); +int od_lexpop(od_lex_t*, od_token_t**); + +#endif /* OD_LEX_H */ diff --git a/src/od_list.h b/src/od_list.h new file mode 100644 index 00000000..4f2c92b3 --- /dev/null +++ b/src/od_list.h @@ -0,0 +1,68 @@ +#ifndef OD_LIST_H +#define OD_LIST_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_list od_list_t; + +struct od_list +{ + od_list_t *next, *prev; +}; + +static inline void +od_listinit(od_list_t *list) +{ + list->next = list->prev = list; +} + +static inline void +od_listappend(od_list_t *list, od_list_t *node) +{ + node->next = list; + node->prev = list->prev; + node->prev->next = node; + node->next->prev = node; +} + +static inline void +od_listunlink(od_list_t *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +static inline void +od_listpush(od_list_t *list, od_list_t *node) +{ + node->next = list->next; + node->prev = list; + node->prev->next = node; + node->next->prev = node; +} + +static inline od_list_t* +od_listpop(od_list_t *list) +{ + register od_list_t *pop = list->next; + od_listunlink(pop); + return pop; +} + +static inline int +od_listempty(od_list_t *list) +{ + return list->next == list && list->prev == list; +} + +#define od_listforeach(LIST, I) \ + for (I = (LIST)->next; I != LIST; I = (I)->next) + +#define od_listforeach_safe(LIST, I, N) \ + for (I = (LIST)->next; I != LIST && (N = I->next); I = N) + +#endif /* OD_LIST_H */ diff --git a/src/od_log.c b/src/od_log.c new file mode 100644 index 00000000..4a2df707 --- /dev/null +++ b/src/od_log.c @@ -0,0 +1,91 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "od_macro.h" +#include "od_pid.h" +#include "od_syslog.h" +#include "od_log.h" +/*#include "od_io.h"*/ + +int od_loginit(od_log_t *l, od_pid_t *pid, od_syslog_t *syslog) +{ + l->verbosity = 0; + l->pid = pid; + l->syslog = syslog; + l->fd = 0; + return 0; +} + +int od_logopen(od_log_t *l, char *path) +{ + int rc = open(path, O_RDWR|O_CREAT|O_APPEND, 0644); + if (rc == -1) + return -1; + l->fd = rc; + return 0; +} + +int od_logclose(od_log_t *l) +{ + if (l->fd == -1) + return 0; + int rc = close(l->fd); + l->fd = -1; + return rc; +} + +int od_logv(od_log_t *l, od_syslogprio_t prio, + machine_io_t peer, + char *ident, + char *fmt, va_list args) +{ + char buffer[512]; + /* pid */ + int len = snprintf(buffer, sizeof(buffer), "%d ", l->pid->pid); + /* time */ + struct timeval tv; + gettimeofday(&tv, NULL); + len += strftime(buffer + len, sizeof(buffer) - len, "%d %b %H:%M:%S.", + localtime(&tv.tv_sec)); + len += snprintf(buffer + len, sizeof(buffer) - len, "%03d ", + (int)tv.tv_usec / 1000); + /* message ident */ + if (ident) + len += snprintf(buffer + len, sizeof(buffer) - len, "%s ", ident); + /* peer */ + // XXX + (void)peer; + /* + if (peer) { + char *peer_name = od_getpeername(peer); + len += snprintf(buffer + len, sizeof(buffer) - len, "%s ", peer_name); + } + */ + /* message */ + len += vsnprintf(buffer + len, sizeof(buffer) - len, fmt, args); + va_end(args); + len += snprintf(buffer + len, sizeof(buffer), "\n"); + int rc = write(l->fd, buffer, len); + od_syslog(l->syslog, prio, buffer); + return rc > 0; +} diff --git a/src/od_log.h b/src/od_log.h new file mode 100644 index 00000000..68987db5 --- /dev/null +++ b/src/od_log.h @@ -0,0 +1,62 @@ +#ifndef OD_LOG_H +#define OD_LOG_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_log od_log_t; + +struct od_log +{ + int fd; + int verbosity; + od_pid_t *pid; + od_syslog_t *syslog; +}; + +int od_loginit(od_log_t*, od_pid_t*, od_syslog_t*); +int od_logopen(od_log_t*, char*); +int od_logclose(od_log_t*); +int od_logv(od_log_t*, od_syslogprio_t, machine_io_t, char*, char*, va_list); + +static inline void +od_logset_verbosity(od_log_t *l, int level) { + l->verbosity = level; +} + +static inline int +od_log(od_log_t *l, machine_io_t peer, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + int rc = od_logv(l, OD_SYSLOG_INFO, peer, NULL, fmt, args); + va_end(args); + return rc; +} + +static inline int +od_debug(od_log_t *l, machine_io_t peer, char *fmt, ...) +{ + if (l->verbosity < 2) + return 0; + va_list args; + va_start(args, fmt); + int rc = od_logv(l, OD_SYSLOG_DEBUG, peer, "debug:", fmt, args); + va_end(args); + return rc; +} + +static inline int +od_error(od_log_t *l, machine_io_t peer, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + int rc = od_logv(l, OD_SYSLOG_ERROR, peer, "error:", fmt, args); + va_end(args); + return rc; +} + +#endif /* OD_LOG_H */ diff --git a/src/od_macro.h b/src/od_macro.h new file mode 100644 index 00000000..47ab29d9 --- /dev/null +++ b/src/od_macro.h @@ -0,0 +1,16 @@ +#ifndef OD_MACRO_H +#define OD_MACRO_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#define od_likely(EXPR) __builtin_expect(!! (EXPR), 1) +#define od_unlikely(EXPR) __builtin_expect(!! (EXPR), 0) + +#define od_container_of(N, T, F) \ + ((T*)((char*)(N) - __builtin_offsetof(T, F))) + +#endif /* OD_MACRO_H */ diff --git a/src/od_pid.c b/src/od_pid.c new file mode 100644 index 00000000..1a250a5e --- /dev/null +++ b/src/od_pid.c @@ -0,0 +1,52 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "od_macro.h" +#include "od_pid.h" + +void od_pidinit(od_pid_t *pid) +{ + pid->pid = getpid(); +} + +int od_pidfile_create(od_pid_t *pid, char *path) +{ + char buffer[32]; + int size = snprintf(buffer, sizeof(buffer), "%d\n", pid->pid); + int rc; + rc = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (rc == -1) + return -1; + int fd = rc; + rc = write(fd, buffer, size); + if (rc != size) { + close(fd); + return -1; + } + rc = close(fd); + return rc; +} + +int od_pidfile_unlink(od_pid_t *pid, char *path) +{ + (void)pid; + int rc = unlink(path); + return rc; +} diff --git a/src/od_pid.h b/src/od_pid.h new file mode 100644 index 00000000..cf37c298 --- /dev/null +++ b/src/od_pid.h @@ -0,0 +1,21 @@ +#ifndef OD_PID_H +#define OD_PID_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_pid od_pid_t; + +struct od_pid +{ + pid_t pid; +}; + +void od_pidinit(od_pid_t*); +int od_pidfile_create(od_pid_t*, char*); +int od_pidfile_unlink(od_pid_t*, char*); + +#endif diff --git a/src/od_scheme.c b/src/od_scheme.c new file mode 100644 index 00000000..1ff30eb7 --- /dev/null +++ b/src/od_scheme.c @@ -0,0 +1,451 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include + +#include + +#include "od_macro.h" +#include "od_list.h" +#include "od_pid.h" +#include "od_syslog.h" +#include "od_log.h" +#include "od_scheme.h" + +void od_schemeinit(od_scheme_t *scheme) +{ + scheme->config_file = NULL; + scheme->daemonize = 0; + scheme->log_verbosity = 1; + scheme->log_file = NULL; + scheme->pid_file = NULL; + scheme->syslog = 0; + scheme->syslog_ident = NULL; + scheme->syslog_facility = NULL; + scheme->stats_period = 0; + scheme->host = NULL; + scheme->port = 6432; + scheme->backlog = 128; + scheme->nodelay = 1; + scheme->keepalive = 7200; + scheme->readahead = 8096; + scheme->workers = 1; + scheme->client_max = 100; + scheme->tls_verify = OD_TDISABLE; + scheme->tls_mode = NULL; + scheme->tls_ca_file = NULL; + scheme->tls_key_file = NULL; + scheme->tls_cert_file = NULL; + scheme->tls_protocols = NULL; + scheme->pooling = NULL; + scheme->pooling_mode = OD_PUNDEF; + scheme->routing = NULL; + scheme->routing_mode = OD_RUNDEF; + scheme->routing_default = NULL; + scheme->server_id = 0; + scheme->users_default = NULL; + od_listinit(&scheme->servers); + od_listinit(&scheme->routing_table); + od_listinit(&scheme->users); +} + +void od_schemefree(od_scheme_t *scheme) +{ + od_list_t *i, *n; + od_listforeach_safe(&scheme->servers, i, n) { + od_schemeserver_t *server; + server = od_container_of(i, od_schemeserver_t, link); + free(server); + } + od_listforeach_safe(&scheme->routing_table, i, n) { + od_schemeroute_t *route; + route = od_container_of(i, od_schemeroute_t, link); + free(route); + } + od_listforeach_safe(&scheme->users, i, n) { + od_schemeuser_t *user; + user = od_container_of(i, od_schemeuser_t, link); + free(user); + } +} + +od_schemeserver_t* +od_schemeserver_add(od_scheme_t *scheme) +{ + od_schemeserver_t *s = + (od_schemeserver_t*)malloc(sizeof(*s)); + if (s == NULL) + return NULL; + memset(s, 0, sizeof(*s)); + s->id = scheme->server_id++; + od_listinit(&s->link); + od_listappend(&scheme->servers, &s->link); + return s; +} + +od_schemeserver_t* +od_schemeserver_match(od_scheme_t *scheme, char *name) +{ + od_list_t *i; + od_listforeach(&scheme->servers, i) { + od_schemeserver_t *server; + server = od_container_of(i, od_schemeserver_t, link); + if (strcmp(server->name, name) == 0) + return server; + } + return NULL; +} + +od_schemeroute_t* +od_schemeroute_match(od_scheme_t *scheme, char *name) +{ + od_list_t *i; + od_listforeach(&scheme->routing_table, i) { + od_schemeroute_t *route; + route = od_container_of(i, od_schemeroute_t, link); + if (strcmp(route->target, name) == 0) + return route; + } + return NULL; +} + +static inline void +od_schemeroute_init(od_schemeroute_t *route) +{ + route->client_max = 100; + route->pool_size = 100; + route->cancel = 1; + route->discard = 1; + route->rollback = 1; +} + +static inline void +od_schemeuser_init(od_schemeuser_t *user) +{ + user->auth_mode = OD_AUNDEF; + user->auth = NULL; +} + +od_schemeroute_t* +od_schemeroute_add(od_scheme_t *scheme) +{ + od_schemeroute_t *r = + (od_schemeroute_t*)malloc(sizeof(*r)); + if (r == NULL) + return NULL; + memset(r, 0, sizeof(*r)); + od_schemeroute_init(r); + od_listinit(&r->link); + od_listappend(&scheme->routing_table, &r->link); + return r; +} + +od_schemeuser_t* +od_schemeuser_add(od_scheme_t *scheme) +{ + od_schemeuser_t *user = + (od_schemeuser_t*)malloc(sizeof(*user)); + if (user == NULL) + return NULL; + memset(user, 0, sizeof(*user)); + od_schemeuser_init(user); + od_listinit(&user->link); + od_listappend(&scheme->users, &user->link); + return user; +} + +od_schemeuser_t* +od_schemeuser_match(od_scheme_t *scheme, char *name) +{ + od_list_t *i; + od_listforeach(&scheme->users, i) { + od_schemeuser_t *user; + user = od_container_of(i, od_schemeuser_t, link); + if (strcmp(user->user, name) == 0) + return user; + } + return NULL; +} + +int od_schemevalidate(od_scheme_t *scheme, od_log_t *log) +{ + /* pooling mode */ + if (scheme->pooling == NULL) { + od_error(log, NULL, "pooling mode is not set"); + return -1; + } + if (strcmp(scheme->pooling, "session") == 0) + scheme->pooling_mode = OD_PSESSION; + else + if (strcmp(scheme->pooling, "transaction") == 0) + scheme->pooling_mode = OD_PTRANSACTION; + + if (scheme->pooling_mode == OD_PUNDEF) { + od_error(log, NULL, "unknown pooling mode"); + return -1; + } + + /* routing mode */ + if (scheme->routing == NULL) { + od_error(log, NULL, "routing mode is not set"); + return -1; + } + if (strcmp(scheme->routing, "forward") == 0) + scheme->routing_mode = OD_RFORWARD; + + if (scheme->routing_mode == OD_RUNDEF) { + od_error(log, NULL, "unknown routing mode"); + return -1; + } + + /* listen */ + if (scheme->host == NULL) + scheme->host = "*"; + + /* tls */ + if (scheme->tls_mode) { + if (strcmp(scheme->tls_mode, "disable") == 0) { + scheme->tls_verify = OD_TDISABLE; + } else + if (strcmp(scheme->tls_mode, "allow") == 0) { + scheme->tls_verify = OD_TALLOW; + } else + if (strcmp(scheme->tls_mode, "require") == 0) { + scheme->tls_verify = OD_TREQUIRE; + } else + if (strcmp(scheme->tls_mode, "verify_ca") == 0) { + scheme->tls_verify = OD_TVERIFY_CA; + } else + if (strcmp(scheme->tls_mode, "verify_full") == 0) { + scheme->tls_verify = OD_TVERIFY_FULL; + } else { + od_error(log, NULL, "unknown tls mode"); + return -1; + } + } + + /* servers */ + if (od_listempty(&scheme->servers)) { + od_error(log, NULL, "no servers defined"); + return -1; + } + od_list_t *i; + od_listforeach(&scheme->servers, i) { + od_schemeserver_t *server; + server = od_container_of(i, od_schemeserver_t, link); + if (server->host == NULL) { + od_error(log, NULL, "server '%s': no host is specified", + server->name); + return -1; + } + if (server->tls_mode) { + if (strcmp(server->tls_mode, "disable") == 0) { + server->tls_verify = OD_TDISABLE; + } else + if (strcmp(server->tls_mode, "allow") == 0) { + server->tls_verify = OD_TALLOW; + } else + if (strcmp(server->tls_mode, "require") == 0) { + server->tls_verify = OD_TREQUIRE; + } else + if (strcmp(server->tls_mode, "verify_ca") == 0) { + server->tls_verify = OD_TVERIFY_CA; + } else + if (strcmp(server->tls_mode, "verify_full") == 0) { + server->tls_verify = OD_TVERIFY_FULL; + } else { + od_error(log, NULL, "unknown server tls mode"); + return -1; + } + } + } + + od_schemeroute_t *default_route = NULL; + + /* routing table */ + od_listforeach(&scheme->routing_table, i) { + od_schemeroute_t *route; + route = od_container_of(i, od_schemeroute_t, link); + if (route->route == NULL) { + od_error(log, NULL, "route '%s': no route server is specified", + route->target); + return -1; + } + route->server = od_schemeserver_match(scheme, route->route); + if (route->server == NULL) { + od_error(log, NULL, "route '%s': no route server '%s' found", + route->target); + return -1; + } + if (route->is_default) { + if (default_route) { + od_error(log, NULL, "more than one default route"); + return -1; + } + default_route = route; + } + } + scheme->routing_default = default_route; + + /* users */ + if (od_listempty(&scheme->users)) { + od_error(log, NULL, "no users defined"); + return -1; + } + + od_schemeuser_t *default_user = NULL; + + od_listforeach(&scheme->users, i) { + od_schemeuser_t *user; + user = od_container_of(i, od_schemeuser_t, link); + if (! user->auth) { + if (user->is_default) + od_error(log, NULL, "default user authentication mode is not defined"); + else + od_error(log, NULL, "user '%s' authentication mode is not defined", + user->user); + return -1; + } + if (strcmp(user->auth, "none") == 0) { + user->auth_mode = OD_ANONE; + } else + if (strcmp(user->auth, "clear_text") == 0) { + user->auth_mode = OD_ACLEAR_TEXT; + if (user->password == NULL) { + od_error(log, NULL, "user '%s' password is not set", + user->user); + return -1; + } + } else + if (strcmp(user->auth, "md5") == 0) { + user->auth_mode = OD_AMD5; + if (user->password == NULL) { + od_error(log, NULL, "user '%s' password is not set", + user->user); + return -1; + } + } else { + od_error(log, NULL, "user '%s' has unknown authentication mode", + user->user); + return -1; + } + if (user->is_default) { + if (default_user) { + od_error(log, NULL, "more than one default user"); + return -1; + } + default_user = user; + } + } + scheme->users_default = default_user; + return 0; +} + +void od_schemeprint(od_scheme_t *scheme, od_log_t *log) +{ + od_log(log, NULL, "using configuration file '%s'", + scheme->config_file); + if (scheme->log_verbosity) + od_log(log, NULL, "log_verbosity %d", scheme->log_verbosity); + if (scheme->log_file) + od_log(log, NULL, "log_file %s", scheme->log_file); + if (scheme->pid_file) + od_log(log, NULL, "pid_file %s", scheme->pid_file); + if (scheme->syslog) + od_log(log, NULL, "syslog %d", scheme->syslog); + if (scheme->syslog_ident) + od_log(log, NULL, "syslog_ident %s", scheme->syslog_ident); + if (scheme->syslog_facility) + od_log(log, NULL, "syslog_facility %s", scheme->syslog_facility); + if (scheme->stats_period) + od_log(log, NULL, "stats_period %d", scheme->stats_period); + if (scheme->daemonize) + od_log(log, NULL, "daemonize %s", + scheme->daemonize ? "yes" : "no"); + od_log(log, NULL, ""); + od_log(log, NULL, "pooling %s", scheme->pooling); + od_log(log, NULL, ""); + od_log(log, NULL, "listen"); + od_log(log, NULL, " host %s ", scheme->host); + od_log(log, NULL, " port %d", scheme->port); + od_log(log, NULL, " backlog %d", scheme->backlog); + od_log(log, NULL, " nodelay %d", scheme->nodelay); + od_log(log, NULL, " keepalive %d", scheme->keepalive); + od_log(log, NULL, " readahead %d", scheme->readahead); + if (scheme->tls_mode) + od_log(log, NULL, " tls_mode %s", scheme->tls_mode); + if (scheme->tls_ca_file) + od_log(log, NULL, " tls_ca_file %s", scheme->tls_ca_file); + if (scheme->tls_key_file) + od_log(log, NULL, " tls_key_file %s", scheme->tls_key_file); + if (scheme->tls_cert_file) + od_log(log, NULL, " tls_cert_file %s", scheme->tls_cert_file); + if (scheme->tls_protocols) + od_log(log, NULL, " tls_protocols %s", scheme->tls_protocols); + od_log(log, NULL, ""); + od_log(log, NULL, "servers"); + od_list_t *i; + od_listforeach(&scheme->servers, i) { + od_schemeserver_t *server; + server = od_container_of(i, od_schemeserver_t, link); + od_log(log, NULL, " <%s> %s", + server->name ? server->name : "", + server->is_default ? "default" : ""); + od_log(log, NULL, " host %s", server->host); + od_log(log, NULL, " port %d", server->port); + if (server->tls_mode) + od_log(log, NULL, " tls_mode %s", server->tls_mode); + if (server->tls_ca_file) + od_log(log, NULL, " tls_ca_file %s", server->tls_ca_file); + if (server->tls_key_file) + od_log(log, NULL, " tls_key_file %s", server->tls_key_file); + if (server->tls_cert_file) + od_log(log, NULL, " tls_cert_file %s", server->tls_cert_file); + if (server->tls_protocols) + od_log(log, NULL, " tls_protocols %s", server->tls_protocols); + } + od_log(log, NULL, ""); + od_log(log, NULL, "routing"); + od_log(log, NULL, " mode %s", scheme->routing); + od_listforeach(&scheme->routing_table, i) { + od_schemeroute_t *route; + route = od_container_of(i, od_schemeroute_t, link); + od_log(log, NULL, " <%s>", route->target); + od_log(log, NULL, " server %s", route->route); + if (route->database) + od_log(log, NULL, " database %s", route->database); + if (route->user) + od_log(log, NULL, " user %s", route->user); + od_log(log, NULL, " ttl %d", route->ttl); + od_log(log, NULL, " cancel %s", + route->discard ? "yes" : "no"); + od_log(log, NULL, " rollback %s", + route->discard ? "yes" : "no"); + od_log(log, NULL, " discard %s", + route->discard ? "yes" : "no"); + od_log(log, NULL, " pool_size %d", route->pool_size); + od_log(log, NULL, " pool_timeout %d", route->pool_timeout); + } + if (! od_listempty(&scheme->users)) { + od_log(log, NULL, ""); + od_log(log, NULL, "users"); + od_listforeach(&scheme->users, i) { + od_schemeuser_t *user; + user = od_container_of(i, od_schemeuser_t, link); + if (user->is_default) + od_log(log, NULL, " default"); + else + od_log(log, NULL, " <%s>", user->user); + if (user->is_deny) + od_log(log, NULL, " deny"); + od_log(log, NULL, " authentication %s", user->auth); + } + } +} diff --git a/src/od_scheme.h b/src/od_scheme.h new file mode 100644 index 00000000..129d0ade --- /dev/null +++ b/src/od_scheme.h @@ -0,0 +1,159 @@ +#ifndef OD_SCHEME_H +#define OD_SCHEME_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_schemeserver od_schemeserver_t; +typedef struct od_schemeroute od_schemeroute_t; +typedef struct od_schemeuser od_schemeuser_t; +typedef struct od_scheme od_scheme_t; + +typedef enum +{ + OD_PUNDEF, + OD_PSESSION, + OD_PTRANSACTION +} od_pooling_t; + +typedef enum +{ + OD_RUNDEF, + OD_RFORWARD +} od_routing_t; + +typedef enum +{ + OD_AUNDEF, + OD_ANONE, + OD_ACLEAR_TEXT, + OD_AMD5 +} od_auth_t; + +typedef enum +{ + OD_TDISABLE, + OD_TALLOW, + OD_TREQUIRE, + OD_TVERIFY_CA, + OD_TVERIFY_FULL +} od_tls_t; + +struct od_schemeserver +{ + int id; + char *name; + char *host; + int port; + od_tls_t tls_verify; + char *tls_mode; + char *tls_ca_file; + char *tls_key_file; + char *tls_cert_file; + char *tls_protocols; + int is_default; + od_list_t link; +}; + +struct od_schemeroute +{ + od_schemeserver_t *server; + int is_default; + char *target; + char *route; + char *database; + char *user; + int user_len; + char *password; + int password_len; + int ttl; + int cancel; + int discard; + int rollback; + int client_max; + int pool_size; + int pool_timeout; + od_list_t link; +}; + +struct od_schemeuser +{ + char *auth; + od_auth_t auth_mode; + char *user; + char *password; + int password_len; + int is_default; + int is_deny; + od_list_t link; +}; + +struct od_scheme +{ + char *config_file; + int server_id; + /* main */ + int daemonize; + int log_verbosity; + char *log_file; + char *pid_file; + int syslog; + char *syslog_ident; + char *syslog_facility; + int stats_period; + int readahead; + char *pooling; + od_pooling_t pooling_mode; + /* listen */ + char *host; + int port; + int backlog; + int nodelay; + int keepalive; + int workers; + int client_max; + od_tls_t tls_verify; + char *tls_mode; + char *tls_ca_file; + char *tls_key_file; + char *tls_cert_file; + char *tls_protocols; + /* servers */ + od_list_t servers; + /* routing */ + char *routing; + od_routing_t routing_mode; + od_schemeroute_t *routing_default; + od_list_t routing_table; + /* users */ + od_list_t users; + od_schemeuser_t *users_default; +}; + +void od_schemeinit(od_scheme_t*); +void od_schemefree(od_scheme_t*); +int od_schemevalidate(od_scheme_t*, od_log_t*); +void od_schemeprint(od_scheme_t*, od_log_t*); + +od_schemeserver_t* +od_schemeserver_add(od_scheme_t*); + +od_schemeserver_t* +od_schemeserver_match(od_scheme_t*, char*); + +od_schemeroute_t* +od_schemeroute_add(od_scheme_t*); + +od_schemeroute_t* +od_schemeroute_match(od_scheme_t*, char*); + +od_schemeuser_t* +od_schemeuser_add(od_scheme_t*); + +od_schemeuser_t* +od_schemeuser_match(od_scheme_t*, char*); + +#endif /* OD_SCHEME_H */ diff --git a/src/od_syslog.c b/src/od_syslog.c new file mode 100644 index 00000000..e1d94e1b --- /dev/null +++ b/src/od_syslog.c @@ -0,0 +1,88 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "od_macro.h" +#include "od_syslog.h" + +typedef struct od_syslog_facility od_syslog_facility_t; + +struct od_syslog_facility +{ + char *name; + int id; +}; + +od_syslog_facility_t od_syslog_facilities[] = +{ + { "daemon", LOG_DAEMON }, + { "user", LOG_USER }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { NULL, 0 } +}; + +static int od_syslog_prio[] = { + LOG_INFO, LOG_ERR, LOG_DEBUG +}; + +int od_syslog_open(od_syslog_t *slog, char *ident, char *facility) +{ + int facility_id = LOG_DAEMON; + if (facility) { + int i = 0; + od_syslog_facility_t *facility_ptr; + for (;;) { + facility_ptr = &od_syslog_facilities[i]; + if (facility_ptr->name == NULL) + break; + if (strcasecmp(facility_ptr->name, facility) == 0) { + facility_id = facility_ptr->id; + break; + } + i++; + } + } + slog->in_use = 1; + if (ident == NULL) + ident = "odissey"; + openlog(ident, 0, facility_id); + return 0; +} + +void od_syslog_close(od_syslog_t *slog) +{ + if (! slog->in_use) + return; + closelog(); +} + +void od_syslog(od_syslog_t *slog, od_syslogprio_t prio, char *msg) +{ + if (slog->in_use) + syslog(od_syslog_prio[prio], "%s", msg); +} diff --git a/src/od_syslog.h b/src/od_syslog.h new file mode 100644 index 00000000..4ab90405 --- /dev/null +++ b/src/od_syslog.h @@ -0,0 +1,33 @@ +#ifndef OD_SYSLOG_H +#define OD_SYSLOG_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +typedef struct od_syslog od_syslog_t; + +typedef enum +{ + OD_SYSLOG_INFO, + OD_SYSLOG_ERROR, + OD_SYSLOG_DEBUG +} od_syslogprio_t; + +struct od_syslog +{ + int in_use; +}; + +static inline void +od_syslog_init(od_syslog_t *syslog) { + syslog->in_use = 0; +} + +int od_syslog_open(od_syslog_t*, char*, char*); +void od_syslog_close(od_syslog_t*); +void od_syslog(od_syslog_t*, od_syslogprio_t, char*); + +#endif /* OD_SYSLOG_H */ diff --git a/src/od_version.h b/src/od_version.h new file mode 100644 index 00000000..5ef8f6f4 --- /dev/null +++ b/src/od_version.h @@ -0,0 +1,13 @@ +#ifndef OD_VERSION_H +#define OD_VERSION_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#define OD_VERSION_GIT "f11f115" +#define OD_VERSION_BUILD "debug" + +#endif /* OD_VERSION_H */ diff --git a/src/od_version.h.cmake b/src/od_version.h.cmake new file mode 100644 index 00000000..81964ced --- /dev/null +++ b/src/od_version.h.cmake @@ -0,0 +1,13 @@ +#ifndef OD_VERSION_H +#define OD_VERSION_H + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#cmakedefine OD_VERSION_GIT "@OD_VERSION_GIT@" +#cmakedefine OD_VERSION_BUILD "@OD_VERSION_BUILD@" + +#endif /* OD_VERSION_H */ diff --git a/src/odissey.c b/src/odissey.c new file mode 100644 index 00000000..f5183ce9 --- /dev/null +++ b/src/odissey.c @@ -0,0 +1,33 @@ + +/* + * odissey. + * + * PostgreSQL connection pooler and request router. +*/ + +#include +#include +#include +#include + +#include + +#include "od_macro.h" +#include "od_list.h" +#include "od_pid.h" +#include "od_syslog.h" +#include "od_log.h" +#include "od_scheme.h" +#include "od_lex.h" +#include "od_config.h" +#include "od.h" + +int main(int argc, char *argv[]) +{ + od_t odissey; + od_init(&odissey); + int rc = od_main(&odissey, argc, argv); + od_free(&odissey); + return rc; + return 0; +}