/* * 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); } } }