odyssey/core/od_config.c

642 lines
14 KiB
C

/*
* 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("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("workers", OD_LWORKERS),
od_keyword("client_max", OD_LCLIENT_MAX),
/* 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;
/* 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;
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;
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;
/* 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;
}