odissey: begin reconstruction for new arch

This commit is contained in:
Dmitry Simonenko 2017-05-24 14:57:15 +03:00
parent f11f115006
commit ca5cf157dd
23 changed files with 2461 additions and 2 deletions

View File

@ -49,4 +49,4 @@ message (STATUS "")
message (STATUS "ODISSEY (version: ${OD_VERSION_GIT} ${OD_VERSION_BUILD})")
message (STATUS "")
add_subdirectory(core)
add_subdirectory(src)

@ -1 +1 @@
Subproject commit cb588f49734eb302b98dc372990eed45facacf0b
Subproject commit 069879865159c1f082bc036db7a974d867f4d191

150
src/od.c Normal file
View File

@ -0,0 +1,150 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <machinarium.h>
#include <soprano.h>
#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 <config_file>", 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;
}

25
src/od.h Normal file
View File

@ -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 */

720
src/od_config.c Normal file
View File

@ -0,0 +1,720 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <machinarium.h>
#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;
}

70
src/od_config.h Normal file
View File

@ -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 */

45
src/od_daemon.c Normal file
View File

@ -0,0 +1,45 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <machinarium.h>
#include <soprano.h>
#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;
}

12
src/od_daemon.h Normal file
View File

@ -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 */

273
src/od_lex.c Normal file
View File

@ -0,0 +1,273 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#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;
}

64
src/od_lex.h Normal file
View File

@ -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 */

68
src/od_list.h Normal file
View File

@ -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 */

91
src/od_log.c Normal file
View File

@ -0,0 +1,91 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <machinarium.h>
#include <soprano.h>
#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;
}

62
src/od_log.h Normal file
View File

@ -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 */

16
src/od_macro.h Normal file
View File

@ -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 */

52
src/od_pid.c Normal file
View File

@ -0,0 +1,52 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#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;
}

21
src/od_pid.h Normal file
View File

@ -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

451
src/od_scheme.c Normal file
View File

@ -0,0 +1,451 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <machinarium.h>
#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);
}
}
}

159
src/od_scheme.h Normal file
View File

@ -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 */

88
src/od_syslog.c Normal file
View File

@ -0,0 +1,88 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#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);
}

33
src/od_syslog.h Normal file
View File

@ -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 */

13
src/od_version.h Normal file
View File

@ -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 */

13
src/od_version.h.cmake Normal file
View File

@ -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 */

33
src/odissey.c Normal file
View File

@ -0,0 +1,33 @@
/*
* odissey.
*
* PostgreSQL connection pooler and request router.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <machinarium.h>
#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;
}