/* * Odyssey. * * Scalable PostgreSQL connection pooler. */ #include #include #include typedef enum { OD_LYES, OD_LNO, OD_LINCLUDE, OD_LDAEMONIZE, OD_LPRIORITY, OD_LSEQROUTING, OD_LLOG_TO_STDOUT, OD_LLOG_DEBUG, OD_LLOG_CONFIG, OD_LLOG_SESSION, OD_LLOG_QUERY, OD_LLOG_FILE, OD_LLOG_FORMAT, OD_LLOG_STATS, /* Prometheus */ OD_LLOG_GENERAL_STATS_PROM, OD_LLOG_ROUTE_STATS_PROM, OD_LPROMHTTP_PORT, OD_LPID_FILE, OD_LUNIX_SOCKET_DIR, OD_LUNIX_SOCKET_MODE, OD_LLOCKS_DIR, OD_LENABLE_ONLINE_RESTART, OD_LGRACEFUL_DIE_ON_ERRORS, OD_LBINDWITH_REUSEPORT, OD_LLOG_SYSLOG, OD_LLOG_SYSLOG_IDENT, OD_LLOG_SYSLOG_FACILITY, OD_LSTATS_INTERVAL, OD_LLISTEN, OD_LHOST, OD_LPORT, OD_LTARGET_SESSION_ATTRS, OD_LBACKLOG, OD_LNODELAY, OD_LKEEPALIVE, OD_LKEEPALIVE_INTERVAL, OD_LKEEPALIVE_PROBES, OD_LKEEPALIVE_USR_TIMEOUT, OD_LREADAHEAD, OD_LWORKERS, OD_LRESOLVERS, OD_LPIPELINE, OD_LPACKET_READ_SIZE, OD_LPACKET_WRITE_QUEUE, OD_LCACHE, OD_LCACHE_CHUNK, OD_LCACHE_MSG_GC_SIZE, OD_LCACHE_COROUTINE, OD_LCOROUTINE_STACK_SIZE, OD_LCLIENT_MAX, OD_LCLIENT_MAX_ROUTING, OD_LSERVER_LOGIN_RETRY, OD_LCLIENT_LOGIN_TIMEOUT, OD_LCLIENT_FWD_ERROR, OD_LPRESERVE_SESSION_SERVER_CONN, OD_LAPPLICATION_NAME_ADD_HOST, OD_LSERVER_LIFETIME, OD_LTLS, OD_LTLS_CA_FILE, OD_LTLS_KEY_FILE, OD_LTLS_CERT_FILE, OD_LTLS_PROTOCOLS, OD_LCOMPRESSION, OD_LSTORAGE, OD_LTYPE, OD_LSERVERS_MAX_ROUTING, OD_LDEFAULT, OD_LDATABASE, OD_LUSER, OD_LPASSWORD, OD_LROLE, OD_LPOOL, OD_LPOOL_ROUTING, OD_LPOOL_PRESERVE_PREP_STMT, #ifdef LDAP_FOUND OD_LLDAPPOOL_SIZE, OD_LLDAPPOOL_TIMEOUT, OD_LLDAPPOOL_TTL, #endif OD_LPOOL_SIZE, OD_LPOOL_TIMEOUT, OD_LPOOL_TTL, OD_LPOOL_DISCARD, OD_LPOOL_SMART_DISCARD, OD_LPOOL_DISCARD_QUERY, OD_LPOOL_CANCEL, OD_LPOOL_ROLLBACK, OD_LPOOL_RESERVE_PREPARED_STATEMENT, OD_LPOOL_CLIENT_IDLE_TIMEOUT, OD_LPOOL_IDLE_IN_TRANSACTION_TIMEOUT, OD_LSTORAGE_DB, OD_LSTORAGE_USER, OD_LSTORAGE_PASSWORD, OD_LAUTHENTICATION, OD_LAUTH_COMMON_NAME, OD_LAUTH_PAM_SERVICE, OD_LAUTH_MODULE, OD_LAUTH_QUERY, OD_LAUTH_QUERY_DB, OD_LAUTH_QUERY_USER, OD_LAUTH_LDAP_SERVICE, OD_LAUTH_PASSWORD_PASSTHROUGH, OD_LAUTH_MDB_IAMPROXY_ENABLE, OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH, OD_LQUANTILES, OD_LMODULE, OD_LLDAP_ENDPOINT, OD_LLDAP_SERVER, OD_LLDAP_PORT, OD_LLDAP_PREFIX, OD_LLDAP_SUFFIX, OD_LLDAP_BASEDN, OD_LLDAP_BINDDN, OD_LLDAP_URL, OD_LLDAP_SEARCH_ATTRIBUTE, OD_LLDAP_BIND_PASSWD, OD_LLDAP_SCHEME, OD_LLDAP_SCOPE, OD_LLDAP_SEARCH_FILTER, OD_LLDAP_ENDPOINT_NAME, OD_LLDAP_STORAGE_CREDENTIALS_ATTR, OD_LLDAP_STORAGE_CREDENTIALS, OD_LLDAP_STORAGE_USERNAME, OD_LLDAP_STORAGE_PASSWORD, OD_LWATCHDOG, OD_LWATCHDOG_LAG_QUERY, OD_LWATCHDOG_LAG_INTERVAL, OD_LCATCHUP_TIMEOUT, OD_LCATCHUP_CHECKS, OD_LOPTIONS, OD_LBACKEND_STARTUP_OPTIONS, OD_LHBA_FILE, } od_lexeme_t; static od_keyword_t od_config_keywords[] = { /* main */ od_keyword("yes", OD_LYES), od_keyword("no", OD_LNO), od_keyword("include", OD_LINCLUDE), od_keyword("daemonize", OD_LDAEMONIZE), od_keyword("priority", OD_LPRIORITY), od_keyword("sequential_routing", OD_LSEQROUTING), od_keyword("pid_file", OD_LPID_FILE), od_keyword("unix_socket_dir", OD_LUNIX_SOCKET_DIR), od_keyword("unix_socket_mode", OD_LUNIX_SOCKET_MODE), od_keyword("locks_dir", OD_LLOCKS_DIR), od_keyword("enable_online_restart", OD_LENABLE_ONLINE_RESTART), od_keyword("graceful_die_on_errors", OD_LGRACEFUL_DIE_ON_ERRORS), od_keyword("bindwith_reuseport", OD_LBINDWITH_REUSEPORT), /* logging */ od_keyword("log_debug", OD_LLOG_DEBUG), od_keyword("log_to_stdout", OD_LLOG_TO_STDOUT), od_keyword("log_config", OD_LLOG_CONFIG), od_keyword("log_session", OD_LLOG_SESSION), od_keyword("log_query", OD_LLOG_QUERY), od_keyword("log_file", OD_LLOG_FILE), od_keyword("log_format", OD_LLOG_FORMAT), od_keyword("log_stats", OD_LLOG_STATS), od_keyword("log_syslog", OD_LLOG_SYSLOG), od_keyword("log_syslog_ident", OD_LLOG_SYSLOG_IDENT), od_keyword("log_syslog_facility", OD_LLOG_SYSLOG_FACILITY), od_keyword("stats_interval", OD_LSTATS_INTERVAL), /* Prometheus */ od_keyword("log_general_stats_prom", OD_LLOG_GENERAL_STATS_PROM), od_keyword("log_route_stats_prom", OD_LLOG_ROUTE_STATS_PROM), od_keyword("promhttp_server_port", OD_LPROMHTTP_PORT), /* listen */ od_keyword("listen", OD_LLISTEN), od_keyword("host", OD_LHOST), od_keyword("port", OD_LPORT), /* target_session_attrs */ od_keyword("target_session_attrs", OD_LTARGET_SESSION_ATTRS), od_keyword("backlog", OD_LBACKLOG), od_keyword("nodelay", OD_LNODELAY), /* TCP keepalive */ od_keyword("keepalive", OD_LKEEPALIVE), od_keyword("keepalive_keep_interval", OD_LKEEPALIVE_INTERVAL), od_keyword("keepalive_probes", OD_LKEEPALIVE_PROBES), od_keyword("keepalive_usr_timeout", OD_LKEEPALIVE_USR_TIMEOUT), /* */ od_keyword("readahead", OD_LREADAHEAD), od_keyword("workers", OD_LWORKERS), od_keyword("resolvers", OD_LRESOLVERS), od_keyword("pipeline", OD_LPIPELINE), od_keyword("packet_read_size", OD_LPACKET_READ_SIZE), od_keyword("packet_write_queue", OD_LPACKET_WRITE_QUEUE), od_keyword("cache", OD_LCACHE), od_keyword("cache_chunk", OD_LCACHE_CHUNK), od_keyword("cache_msg_gc_size", OD_LCACHE_MSG_GC_SIZE), od_keyword("cache_coroutine", OD_LCACHE_COROUTINE), od_keyword("coroutine_stack_size", OD_LCOROUTINE_STACK_SIZE), /* client */ od_keyword("client_max", OD_LCLIENT_MAX), od_keyword("client_max_routing", OD_LCLIENT_MAX_ROUTING), od_keyword("server_login_retry", OD_LSERVER_LOGIN_RETRY), od_keyword("client_login_timeout", OD_LCLIENT_LOGIN_TIMEOUT), od_keyword("client_fwd_error", OD_LCLIENT_FWD_ERROR), od_keyword("reserve_session_server_connection", OD_LPRESERVE_SESSION_SERVER_CONN), od_keyword("application_name_add_host", OD_LAPPLICATION_NAME_ADD_HOST), od_keyword("server_lifetime", OD_LSERVER_LIFETIME), /* tls */ od_keyword("tls", OD_LTLS), 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), od_keyword("compression", OD_LCOMPRESSION), /* storage */ od_keyword("storage", OD_LSTORAGE), od_keyword("type", OD_LTYPE), od_keyword("server_max_routing", OD_LSERVERS_MAX_ROUTING), od_keyword("default", OD_LDEFAULT), /* database */ od_keyword("database", OD_LDATABASE), od_keyword("user", OD_LUSER), od_keyword("password", OD_LPASSWORD), od_keyword("role", OD_LROLE), od_keyword("pool", OD_LPOOL), od_keyword("pool_preserve_prep_stmt", OD_LPOOL_PRESERVE_PREP_STMT), od_keyword("pool_routing", OD_LPOOL_ROUTING), #ifdef LDAP_FOUND od_keyword("ldap_pool_size", OD_LLDAPPOOL_SIZE), od_keyword("ldap_pool_timeout", OD_LLDAPPOOL_TIMEOUT), od_keyword("ldap_pool_ttl", OD_LLDAPPOOL_TTL), #endif od_keyword("pool_size", OD_LPOOL_SIZE), od_keyword("pool_timeout", OD_LPOOL_TIMEOUT), od_keyword("pool_ttl", OD_LPOOL_TTL), od_keyword("pool_discard", OD_LPOOL_DISCARD), od_keyword("pool_discard_query", OD_LPOOL_DISCARD_QUERY), od_keyword("pool_smart_discard", OD_LPOOL_SMART_DISCARD), od_keyword("pool_cancel", OD_LPOOL_CANCEL), od_keyword("pool_rollback", OD_LPOOL_ROLLBACK), od_keyword("pool_reserve_prepared_statement", OD_LPOOL_RESERVE_PREPARED_STATEMENT), od_keyword("pool_client_idle_timeout", OD_LPOOL_CLIENT_IDLE_TIMEOUT), od_keyword("pool_idle_in_transaction_timeout", OD_LPOOL_IDLE_IN_TRANSACTION_TIMEOUT), od_keyword("storage_db", OD_LSTORAGE_DB), od_keyword("storage_user", OD_LSTORAGE_USER), od_keyword("storage_password", OD_LSTORAGE_PASSWORD), /* auth */ od_keyword("authentication", OD_LAUTHENTICATION), od_keyword("auth_common_name", OD_LAUTH_COMMON_NAME), od_keyword("auth_query", OD_LAUTH_QUERY), od_keyword("auth_query_db", OD_LAUTH_QUERY_DB), od_keyword("auth_query_user", OD_LAUTH_QUERY_USER), od_keyword("auth_pam_service", OD_LAUTH_PAM_SERVICE), od_keyword("auth_module", OD_LAUTH_MODULE), od_keyword("password_passthrough", OD_LAUTH_PASSWORD_PASSTHROUGH), od_keyword("load_module", OD_LMODULE), od_keyword("hba_file", OD_LHBA_FILE), od_keyword("enable_mdb_iamproxy_auth", OD_LAUTH_MDB_IAMPROXY_ENABLE), od_keyword("mdb_iamproxy_socket_path", OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH), /* ldap */ od_keyword("ldap_endpoint", OD_LLDAP_ENDPOINT), od_keyword("ldapserver", OD_LLDAP_SERVER), od_keyword("ldapport", OD_LLDAP_PORT), od_keyword("ldapprefix", OD_LLDAP_PREFIX), od_keyword("ldapsuffix", OD_LLDAP_SUFFIX), od_keyword("ldapbasedn", OD_LLDAP_BASEDN), od_keyword("ldapbinddn", OD_LLDAP_BINDDN), od_keyword("ldapbindpasswd", OD_LLDAP_BIND_PASSWD), od_keyword("ldapurl", OD_LLDAP_URL), od_keyword("ldapsearchattribute", OD_LLDAP_SEARCH_ATTRIBUTE), od_keyword("ldapscheme", OD_LLDAP_SCHEME), od_keyword("ldapsearchfilter", OD_LLDAP_SEARCH_FILTER), od_keyword("ldapscope", OD_LLDAP_SCOPE), od_keyword("ldap_endpoint_name", OD_LLDAP_ENDPOINT_NAME), od_keyword("ldap_storage_credentials_attr", OD_LLDAP_STORAGE_CREDENTIALS_ATTR), od_keyword("ldap_storage_credentials", OD_LLDAP_STORAGE_CREDENTIALS), od_keyword("ldap_storage_username", OD_LLDAP_STORAGE_USERNAME), od_keyword("ldap_storage_password", OD_LLDAP_STORAGE_PASSWORD), /* watchdog */ od_keyword("watchdog", OD_LWATCHDOG), od_keyword("watchdog_lag_query", OD_LWATCHDOG_LAG_QUERY), od_keyword("watchdog_lag_interval", OD_LWATCHDOG_LAG_INTERVAL), od_keyword("catchup_timeout", OD_LCATCHUP_TIMEOUT), od_keyword("catchup_checks", OD_LCATCHUP_CHECKS), /* options */ od_keyword("options", OD_LOPTIONS), od_keyword("backend_startup_options", OD_LBACKEND_STARTUP_OPTIONS), /* stats */ od_keyword("quantiles", OD_LQUANTILES), { 0, 0, 0 }, }; static od_keyword_t od_role_keywords[] = { od_keyword("admin", OD_RULE_ROLE_ADMIN), od_keyword("stat", OD_RULE_ROLE_STAT), od_keyword("notallow", OD_RULE_ROLE_NOTALLOW), { 0, 0, 0 }, }; static inline int od_config_reader_watchdog(od_config_reader_t *reader, od_storage_watchdog_t *watchdog, od_extention_t *extentions); static int od_config_reader_open(od_config_reader_t *reader, char *config_file) { reader->config_file = config_file; /* read file */ char *config_buf = NULL; FILE *file = fopen(config_file, "r"); if (file == NULL) goto error; fseek(file, 0, SEEK_END); int size = (int)ftell(file); if (size == -1) goto error; fseek(file, 0, SEEK_SET); config_buf = malloc(size); if (config_buf == NULL) goto error; int rc = fread(config_buf, size, 1, file); if (rc != 1) { free(config_buf); goto error; } switch (fclose(file)) { case 0: { reader->data = config_buf; reader->data_size = size; od_parser_init(&reader->parser, reader->data, reader->data_size); return 0; } case EOF: { od_errorf(reader->error, "failed to close config file '%s': %d", config_file, errno); free(config_buf); return NOT_OK_RESPONSE; } default: assert(0); } error: od_errorf(reader->error, "failed to open config file '%s'", config_file); if (file) { fclose(file); } return NOT_OK_RESPONSE; } static void od_config_reader_close(od_config_reader_t *reader) { if (reader->data_size > 0) free(reader->data); } static bool od_config_reader_is(od_config_reader_t *reader, int id) { od_token_t token; int rc; token.line = 0; rc = od_parser_next(&reader->parser, &token); od_parser_push(&reader->parser, &token); if (rc != id) return false; return true; } static inline bool od_config_reader_symbol_is(od_config_reader_t *reader, char symbol) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); od_parser_push(&reader->parser, &token); if (rc != OD_PARSER_SYMBOL) return false; if (token.value.num != (int64_t)symbol) return false; return true; } static bool od_config_reader_keyword_is(od_config_reader_t *reader, od_keyword_t *keyword) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); od_parser_push(&reader->parser, &token); if (rc != OD_PARSER_KEYWORD) return false; od_keyword_t *match; match = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) return false; if (keyword != match) return false; return true; } bool od_config_reader_keyword(od_config_reader_t *reader, od_keyword_t *keyword) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_KEYWORD) goto error; od_keyword_t *match; match = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) goto error; if (keyword != match) goto error; return true; error: od_parser_push(&reader->parser, &token); char *kwname = "unknown"; if (keyword) kwname = keyword->name; od_config_reader_error(reader, &token, "expected '%s'", kwname); return false; } static bool od_config_reader_quantiles(od_config_reader_t *reader, char *value, double **quantiles, int *count) { int comma_cnt = 1; char *c = value; while (*c) { if (*c == ',') comma_cnt++; c++; } *quantiles = malloc(sizeof(double) * comma_cnt); if (*quantiles == NULL) { return false; } double *array = *quantiles; *count = 0; c = value; while (*c) { int length = sscanf(c, "%lf", array + *count); if (length != 1 || array[*count] > 1 || array[*count] < 0) { od_config_reader_error(reader, NULL, "incorrect quantile value"); free(*quantiles); return false; } *count += 1; while (*c != '\0' && *c != ',') { c++; } if (*c == '\0') break; c++; } return true; } static bool od_config_reader_string(od_config_reader_t *reader, char **value) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_STRING) { od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "expected 'string'"); return false; } char *copy = malloc(token.value.string.size + 1); if (copy == NULL) { od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "memory allocation error"); return false; } memcpy(copy, token.value.string.pointer, token.value.string.size); copy[token.value.string.size] = 0; if (*value) free(*value); *value = copy; return true; } static bool od_config_reader_number(od_config_reader_t *reader, int *number) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_NUM) { od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "expected 'number'"); return false; } /* uint64 to int conversion */ *number = token.value.num; return true; } static bool od_config_reader_number64(od_config_reader_t *reader, uint64_t *number) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_NUM) { od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "expected 'number'"); return false; } *number = token.value.num; return true; } static bool od_config_reader_yes_no(od_config_reader_t *reader, int *value) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_KEYWORD) goto error; od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) goto error; switch (keyword->id) { case OD_LYES: *value = 1; break; case OD_LNO: *value = 0; break; default: goto error; } return true; error: od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "expected 'yes/no'"); return false; } static int od_config_reader_storage_host(od_config_reader_t *reader, od_rule_storage_t *storage) { size_t i; size_t j; size_t tmp; size_t len; size_t endpoint_cnt; size_t closing_bracked_indx; size_t host_len; size_t host_off; if (!od_config_reader_string(reader, &storage->host)) { return NOT_OK_RESPONSE; } endpoint_cnt = 0; len = strlen(storage->host); /* string in format host[,host...] */ for (i = 0; i < len; i++) { if (storage->host[i] == ',') { ++endpoint_cnt; } } ++endpoint_cnt; storage->endpoints_count = 0; storage->endpoints = malloc(sizeof(od_storage_endpoint_t) * endpoint_cnt); if (storage->endpoints == NULL) { return NOT_OK_RESPONSE; } for (i = 0; i < len;) { closing_bracked_indx = len + 1; for (j = i; j + 1 < len && storage->host[j + 1] != ','; ++j) { switch (storage->host[j]) { case '[': if (j > i) { return NOT_OK_RESPONSE; /* wrong entry format */ } break; case ']': if (closing_bracked_indx < j) { return NOT_OK_RESPONSE; /* wrong entry format */ } closing_bracked_indx = j; break; } } if (storage->host[i] != '[') { /* block format is host[,host] */ host_len = j - i + 1; host_off = i; storage->endpoints[storage->endpoints_count].port = 0; } else { if (closing_bracked_indx == len + 1) { /* matching bracked was not met */ return NOT_OK_RESPONSE; } /* [host]:port */ host_len = closing_bracked_indx - i - 1; host_off = i + 1; storage->endpoints[storage->endpoints_count].port = 0; /* ]:1234 */ /* ^ ^ */ /* iter betwwen this two localtions */ for (tmp = closing_bracked_indx + 2; tmp <= j; ++tmp) { if (!isdigit(storage->host[tmp])) { return NOT_OK_RESPONSE; } storage->endpoints[storage->endpoints_count] .port *= 10; storage->endpoints[storage->endpoints_count] .port += storage->host[tmp] - '0'; } } /* copy the host name */ storage->endpoints[storage->endpoints_count].host = malloc(sizeof(char) * (host_len + 1)); if (storage->endpoints[storage->endpoints_count].host == NULL) { return NOT_OK_RESPONSE; } memcpy(storage->endpoints[storage->endpoints_count].host, storage->host + host_off, host_len); storage->endpoints[storage->endpoints_count].host[host_len] = '\0'; storage->endpoints_count++; /* storage->host[j] == ',' or j == len - 1 */ i = j + 2; } return OK_RESPONSE; } static int od_config_reader_listen(od_config_reader_t *reader) { od_config_t *config = reader->config; od_config_listen_t *listen; listen = od_config_listen_add(config); if (listen == NULL) { return NOT_OK_RESPONSE; } /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { return OK_RESPONSE; } /* fall through */ default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); return NOT_OK_RESPONSE; } switch (keyword->id) { /* host */ case OD_LHOST: if (!od_config_reader_string(reader, &listen->host)) return NOT_OK_RESPONSE; continue; /* port */ case OD_LPORT: if (!od_config_reader_number(reader, &listen->port)) return NOT_OK_RESPONSE; continue; /* client_login_timeout */ case OD_LCLIENT_LOGIN_TIMEOUT: if (!od_config_reader_number( reader, &listen->client_login_timeout)) return NOT_OK_RESPONSE; continue; /* backlog */ case OD_LBACKLOG: if (!od_config_reader_number(reader, &listen->backlog)) return NOT_OK_RESPONSE; continue; /* tls */ case OD_LTLS: if (!od_config_reader_string(reader, &listen->tls_opts->tls)) return NOT_OK_RESPONSE; continue; /* tls_ca_file */ case OD_LTLS_CA_FILE: if (!od_config_reader_string( reader, &listen->tls_opts->tls_ca_file)) return NOT_OK_RESPONSE; continue; /* tls_key_file */ case OD_LTLS_KEY_FILE: if (!od_config_reader_string( reader, &listen->tls_opts->tls_key_file)) return NOT_OK_RESPONSE; continue; /* tls_cert_file */ case OD_LTLS_CERT_FILE: if (!od_config_reader_string( reader, &listen->tls_opts->tls_cert_file)) return NOT_OK_RESPONSE; continue; /* tls_protocols */ case OD_LTLS_PROTOCOLS: if (!od_config_reader_string( reader, &listen->tls_opts->tls_protocols)) return NOT_OK_RESPONSE; continue; /* compression */ case OD_LCOMPRESSION: if (!od_config_reader_yes_no(reader, &listen->compression)) return NOT_OK_RESPONSE; continue; default: od_config_reader_error(reader, &token, "unexpected parameter"); return NOT_OK_RESPONSE; } } /* unreach */ return NOT_OK_RESPONSE; } static int od_config_reader_storage(od_config_reader_t *reader, od_extention_t *extentions) { char *tmp = NULL; od_rule_storage_t *storage; storage = od_rules_storage_allocate(); if (storage == NULL) return NOT_OK_RESPONSE; /* name */ if (!od_config_reader_string(reader, &storage->name)) goto error; if (od_rules_storage_match(reader->rules, storage->name) != NULL) { od_config_reader_error(reader, NULL, "duplicate storage definition: %s", storage->name); goto error; } od_rules_storage_add(reader->rules, storage); /* { */ if (!od_config_reader_symbol(reader, '{')) goto error; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: { od_config_reader_error(reader, &token, "unexpected end of config file"); goto error; } case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { return OK_RESPONSE; } /* fall through */ default: { od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); goto error; } } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); goto error; } switch (keyword->id) { /* type */ case OD_LTYPE: if (!od_config_reader_string(reader, &storage->type)) goto error; continue; /* host */ case OD_LHOST: if (od_config_reader_storage_host(reader, storage) != OK_RESPONSE) goto error; continue; /* port */ case OD_LPORT: if (!od_config_reader_number(reader, &storage->port)) goto error; continue; /* target_session_attrs */ case OD_LTARGET_SESSION_ATTRS: if (!od_config_reader_string(reader, &tmp)) { goto error; } if (strcmp(tmp, "read-write") == 0) { storage->target_session_attrs = OD_TARGET_SESSION_ATTRS_RW; } else if (strcmp(tmp, "any") == 0) { storage->target_session_attrs = OD_TARGET_SESSION_ATTRS_ANY; } else if (strcmp(tmp, "read-only") == 0) { storage->target_session_attrs = OD_TARGET_SESSION_ATTRS_RO; } else { goto error; } free(tmp); tmp = NULL; continue; /* tls */ case OD_LTLS: if (!od_config_reader_string(reader, &storage->tls_opts->tls)) goto error; continue; /* tls_ca_file */ case OD_LTLS_CA_FILE: if (!od_config_reader_string( reader, &storage->tls_opts->tls_ca_file)) goto error; continue; /* tls_key_file */ case OD_LTLS_KEY_FILE: if (!od_config_reader_string( reader, &storage->tls_opts->tls_key_file)) goto error; continue; /* tls_cert_file */ case OD_LTLS_CERT_FILE: if (!od_config_reader_string( reader, &storage->tls_opts->tls_cert_file)) goto error; continue; /* tls_protocols */ case OD_LTLS_PROTOCOLS: if (!od_config_reader_string( reader, &storage->tls_opts->tls_protocols)) goto error; continue; /* server_max_routing */ case OD_LSERVERS_MAX_ROUTING: if (!od_config_reader_number( reader, &storage->server_max_routing)) goto error; continue; /* watchdog */ case OD_LWATCHDOG: storage->watchdog = od_storage_watchdog_allocate(reader->global); if (storage->watchdog == NULL) goto error; if (od_config_reader_watchdog(reader, storage->watchdog, extentions) == NOT_OK_RESPONSE) goto error; continue; default: { od_config_reader_error(reader, &token, "unexpected parameter"); goto error; } } } /* unreach */ error: if (storage->watchdog) { od_storage_watchdog_free(storage->watchdog); } od_rules_storage_free(storage); return NOT_OK_RESPONSE; } static inline int od_config_reader_pgoptions_kv_pair( od_config_reader_t *reader, od_token_t *token, char **optarg, size_t *optarg_len, char **optval, size_t *optval_len) { *optarg_len = token->value.string.size; *optarg = malloc(*optarg_len + 1); if (*optarg == NULL) { return NOT_OK_RESPONSE; } memcpy(*optarg, token->value.string.pointer, token->value.string.size); (*optarg)[*optarg_len] = 0; int rc; rc = od_parser_next(&reader->parser, token); if (rc != OD_PARSER_STRING) { free(*optarg); return NOT_OK_RESPONSE; } *optval_len = token->value.string.size; *optval = malloc(*optval_len + 1); if (*optval == NULL) { free(*optarg); return NOT_OK_RESPONSE; } memcpy(*optval, token->value.string.pointer, token->value.string.size); (*optval)[*optval_len] = 0; return OK_RESPONSE; } static inline int od_config_reader_pgoptions(od_config_reader_t *reader, kiwi_vars_t *dest) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* { */ if (token.value.num == '{') break; /* fall through */ default: od_config_reader_error(reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } char *optarg = NULL, *optval = NULL; size_t optarg_len, optval_len; for (;;) { rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_STRING: if (od_config_reader_pgoptions_kv_pair( reader, &token, &optarg, &optarg_len, &optval, &optval_len) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } kiwi_vars_update(dest, optarg, optarg_len + 1, optval, optval_len + 1); free(optarg); free(optval); break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') return 0; /* fall through */ case OD_PARSER_KEYWORD: default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } } } static inline int od_config_reader_backend_pgoptions(od_config_reader_t *reader, od_rule_t *rule) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* { */ if (token.value.num == '{') break; /* fall through */ default: od_config_reader_error(reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } untyped_kiwi_var_t *ptr = NULL; rule->backend_startup_vars_sz = 0; size_t backend_startup_vars_alloc_sz = 4; rule->backend_startup_vars = malloc(sizeof(untyped_kiwi_var_t) * backend_startup_vars_alloc_sz); if (rule->backend_startup_vars == NULL) { /* oom */ return NOT_OK_RESPONSE; } for (;;) { rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_STRING: assert(rule->backend_startup_vars_sz <= backend_startup_vars_alloc_sz); if (rule->backend_startup_vars_sz == backend_startup_vars_alloc_sz) { backend_startup_vars_alloc_sz *= 2; rule->backend_startup_vars = realloc( rule->backend_startup_vars, sizeof(untyped_kiwi_var_t) * backend_startup_vars_alloc_sz); if (rule->backend_startup_vars == NULL) { /* oom */ return NOT_OK_RESPONSE; } } ptr = &rule->backend_startup_vars [rule->backend_startup_vars_sz]; if (od_config_reader_pgoptions_kv_pair( reader, &token, &ptr->name, &ptr->name_len, &ptr->value, &ptr->value_len) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } rule->backend_startup_vars_sz++; break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { return OK_RESPONSE; } /* fall through */ case OD_PARSER_KEYWORD: default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } } } #ifdef LDAP_FOUND static inline od_retcode_t od_config_reader_ldap_storage_credentials(od_config_reader_t *reader, od_rule_t *rule) { od_ldap_storage_credentials_t *lsc_current; lsc_current = od_ldap_storage_credentials_alloc(); if (!lsc_current) { goto error; } /* name */ if (!od_config_reader_string(reader, &lsc_current->name)) { goto error; } if (strlen(lsc_current->name) == 0) { od_config_reader_error( reader, NULL, "empty ldap storage credentials definition"); goto error; } if (od_ldap_storage_credentials_find(&rule->ldap_storage_creds_list, lsc_current->name) != NULL) { od_config_reader_error( reader, NULL, "duplicate ldap storage credentials definition: %s", lsc_current->name); goto error; } od_rule_ldap_storage_credentials_add(rule, lsc_current); /* { */ if (!od_config_reader_symbol(reader, '{')) { goto error; } for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { if (lsc_current->lsc_username == NULL) { od_config_reader_error( reader, NULL, "lsc_username in ldap_storage_credentials '%s' is not defined", lsc_current->name); goto error; } if (lsc_current->lsc_password == NULL) { od_config_reader_error( reader, NULL, "lsc_password in ldap_storage_credentials '%s' is not defined", lsc_current->name); goto error; } return OK_RESPONSE; } /* fall through */ case OD_PARSER_KEYWORD: break; default: od_config_reader_error(reader, &token, "unexpected symbol or token"); goto error; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); return NOT_OK_RESPONSE; } switch (keyword->id) { case OD_LLDAP_STORAGE_USERNAME: { if (!od_config_reader_string( reader, &lsc_current->lsc_username)) goto error; if (strlen(lsc_current->lsc_username) == 0) { od_config_reader_error( reader, &token, "lsc_username in ldap_storage_credentials '%s' cannot be empty", lsc_current->name); goto error; } } break; case OD_LLDAP_STORAGE_PASSWORD: { if (!od_config_reader_string( reader, &lsc_current->lsc_password)) goto error; if (strlen(lsc_current->lsc_password) == 0) { od_config_reader_error( reader, &token, "lsc_password in ldap_storage_credentials '%s' cannot be empty", lsc_current->name); goto error; } } break; } } return OK_RESPONSE; error: if (lsc_current) { od_ldap_storage_credentials_free(lsc_current); } return NOT_OK_RESPONSE; } #endif static int od_config_reader_rule_settings(od_config_reader_t *reader, od_rule_t *rule, od_extention_t *extentions, od_storage_watchdog_t *watchdog) { rule->mdb_iamproxy_socket_path = NULL; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); return NOT_OK_RESPONSE; case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') return 0; /* fall through */ default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_list_t *i; bool token_ok = false; od_list_foreach(&extentions->modules->link, i) { od_module_t *curr_module; curr_module = od_container_of(i, od_module_t, link); rc = curr_module->config_rule_init_cb( rule, reader, &token); if (rc == OD_MODULE_CB_OK_RETCODE) { // do not "break" cycle here - let every module to read // this init param token_ok = true; } } if (!token_ok) { od_config_reader_error(reader, &token, "unknown parameter"); return NOT_OK_RESPONSE; } /* continue reading config */ continue; } switch (keyword->id) { /* authentication */ case OD_LAUTHENTICATION: if (!od_config_reader_string(reader, &rule->auth)) return NOT_OK_RESPONSE; #ifndef USE_SCRAM if (strcmp(rule->auth, "scram-sha-256") == 0) { od_config_reader_error( reader, &token, "SCRAM auth is not supported in this build, try to recompile"); return NOT_OK_RESPONSE; } #endif break; /* auth_common_name */ case OD_LAUTH_COMMON_NAME: { if (od_config_reader_is(reader, OD_PARSER_KEYWORD)) { if (!od_config_reader_keyword( reader, &od_config_keywords[OD_LDEFAULT])) return NOT_OK_RESPONSE; rule->auth_common_name_default = 1; break; } od_rule_auth_t *auth; auth = od_rules_auth_add(rule); if (auth == NULL) return NOT_OK_RESPONSE; if (!od_config_reader_string(reader, &auth->common_name)) return NOT_OK_RESPONSE; break; } /* auth_module */ case OD_LAUTH_MODULE: if (!od_config_reader_string(reader, &rule->auth_module)) return NOT_OK_RESPONSE; break; /* mdb_iamproxy authentication */ case OD_LAUTH_MDB_IAMPROXY_ENABLE: { if (!od_config_reader_yes_no( reader, &rule->enable_mdb_iamproxy_auth)) return NOT_OK_RESPONSE; break; } case OD_LAUTH_MDB_IAMPROXY_SOCKET_PATH: { if (!od_config_reader_string( reader, &rule->mdb_iamproxy_socket_path)) return NOT_OK_RESPONSE; break; } #ifdef PAM_FOUND /* auth_pam_service */ case OD_LAUTH_PAM_SERVICE: if (!od_config_reader_string(reader, &rule->auth_pam_service)) return NOT_OK_RESPONSE; break; #endif /* auth_query */ case OD_LAUTH_QUERY: if (!od_config_reader_string(reader, &rule->auth_query)) return NOT_OK_RESPONSE; break; /* auth_query_db */ case OD_LAUTH_QUERY_DB: if (!od_config_reader_string(reader, &rule->auth_query_db)) return NOT_OK_RESPONSE; break; /* auth_query_user */ case OD_LAUTH_QUERY_USER: if (!od_config_reader_string(reader, &rule->auth_query_user)) return NOT_OK_RESPONSE; break; /* auth_query_user */ case OD_LAUTH_PASSWORD_PASSTHROUGH: if (!od_config_reader_yes_no( reader, &rule->enable_password_passthrough)) return NOT_OK_RESPONSE; break; /* password */ case OD_LPASSWORD: if (!od_config_reader_string(reader, &rule->password)) return NOT_OK_RESPONSE; rule->password_len = strlen(rule->password); continue; /* role */ case OD_LROLE: { od_token_t token; int rc; od_keyword_t *keyword; rc = od_parser_next(&reader->parser, &token); if (rc != OD_PARSER_STRING) { od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); return NOT_OK_RESPONSE; } keyword = od_keyword_match(od_role_keywords, &token); if (keyword == NULL) { od_parser_push(&reader->parser, &token); od_config_reader_error(reader, &token, "expected role"); return NOT_OK_RESPONSE; } rule->user_role = keyword->id; break; } /* client_max */ case OD_LCLIENT_MAX: if (!od_config_reader_number(reader, &rule->client_max)) return NOT_OK_RESPONSE; rule->client_max_set = 1; continue; /* client_fwd_error */ case OD_LCLIENT_FWD_ERROR: if (!od_config_reader_yes_no(reader, &rule->client_fwd_error)) return NOT_OK_RESPONSE; continue; /* reserve_session_server_connection */ case OD_LPRESERVE_SESSION_SERVER_CONN: if (!od_config_reader_yes_no( reader, &rule->reserve_session_server_connection)) { return NOT_OK_RESPONSE; } continue; /* quantiles */ case OD_LQUANTILES: { char *quantiles_str = NULL; if (!od_config_reader_string(reader, &quantiles_str)) { free(quantiles_str); return NOT_OK_RESPONSE; } if (!od_config_reader_quantiles( reader, quantiles_str, &rule->quantiles, &rule->quantiles_count)) { free(quantiles_str); return NOT_OK_RESPONSE; } free(quantiles_str); } break; /* application_name_add_host */ case OD_LAPPLICATION_NAME_ADD_HOST: if (!od_config_reader_yes_no( reader, &rule->application_name_add_host)) return NOT_OK_RESPONSE; continue; /* server_lifetime */ case OD_LSERVER_LIFETIME: { int server_lifetime; if (!od_config_reader_number(reader, &server_lifetime)) return NOT_OK_RESPONSE; rule->server_lifetime_us = server_lifetime * 1000000L; } continue; #ifdef LDAP_FOUND /* ldap_pool_size */ case OD_LLDAPPOOL_SIZE: if (!od_config_reader_number(reader, &rule->ldap_pool_size)) return NOT_OK_RESPONSE; continue; /* ldap_pool_timeout */ case OD_LLDAPPOOL_TIMEOUT: if (!od_config_reader_number(reader, &rule->ldap_pool_timeout)) return NOT_OK_RESPONSE; continue; /* ldap_pool_ttl */ case OD_LLDAPPOOL_TTL: if (!od_config_reader_number(reader, &rule->ldap_pool_ttl)) return NOT_OK_RESPONSE; continue; #endif /* pool */ case OD_LPOOL: if (!od_config_reader_string(reader, &rule->pool->type)) return NOT_OK_RESPONSE; continue; /* pool routing */ case OD_LPOOL_ROUTING: if (!od_config_reader_string(reader, &rule->pool->routing_type)) return NOT_OK_RESPONSE; continue; /* pool_size */ case OD_LPOOL_SIZE: if (!od_config_reader_number(reader, &rule->pool->size)) return NOT_OK_RESPONSE; continue; /* pool_timeout */ case OD_LPOOL_TIMEOUT: if (!od_config_reader_number(reader, &rule->pool->timeout)) return NOT_OK_RESPONSE; continue; /* pool_ttl */ case OD_LPOOL_TTL: if (!od_config_reader_number(reader, &rule->pool->ttl)) return NOT_OK_RESPONSE; continue; /* pool_discard */ case OD_LPOOL_DISCARD: if (!od_config_reader_yes_no(reader, &rule->pool->discard)) return NOT_OK_RESPONSE; continue; /* pool_smart_discard */ case OD_LPOOL_SMART_DISCARD: if (!od_config_reader_yes_no( reader, &rule->pool->smart_discard)) return NOT_OK_RESPONSE; continue; /* pool_discard_query */ case OD_LPOOL_DISCARD_QUERY: if (!od_config_reader_string( reader, &rule->pool->discard_query)) return NOT_OK_RESPONSE; continue; /* pool_cancel */ case OD_LPOOL_CANCEL: if (!od_config_reader_yes_no(reader, &rule->pool->cancel)) return NOT_OK_RESPONSE; continue; /* pool_rollback */ case OD_LPOOL_ROLLBACK: if (!od_config_reader_yes_no(reader, &rule->pool->rollback)) return NOT_OK_RESPONSE; continue; case OD_LPOOL_RESERVE_PREPARED_STATEMENT: if (!od_config_reader_yes_no( reader, &rule->pool->reserve_prepared_statement)) return NOT_OK_RESPONSE; continue; /* pool_client_idle_timeout */ case OD_LPOOL_CLIENT_IDLE_TIMEOUT: if (!od_config_reader_number64( reader, &rule->pool->client_idle_timeout)) { return NOT_OK_RESPONSE; } rule->pool->client_idle_timeout *= interval_usec; continue; /* pool_idle_in_transaction_timeout */ case OD_LPOOL_IDLE_IN_TRANSACTION_TIMEOUT: if (!od_config_reader_number64( reader, &rule->pool->idle_in_transaction_timeout)) { return NOT_OK_RESPONSE; } rule->pool->idle_in_transaction_timeout *= interval_usec; continue; /* storage */ case OD_LSTORAGE: if (!od_config_reader_string(reader, &rule->storage_name)) return NOT_OK_RESPONSE; continue; /* storage_database */ case OD_LSTORAGE_DB: if (!od_config_reader_string(reader, &rule->storage_db)) return NOT_OK_RESPONSE; continue; /* storage_user */ case OD_LSTORAGE_USER: if (!od_config_reader_string(reader, &rule->storage_user)) return NOT_OK_RESPONSE; rule->storage_user_len = strlen(rule->storage_user); continue; /* storage_password */ case OD_LSTORAGE_PASSWORD: if (!od_config_reader_string(reader, &rule->storage_password)) return NOT_OK_RESPONSE; rule->storage_password_len = strlen(rule->storage_password); continue; /* log_debug */ case OD_LLOG_DEBUG: if (!od_config_reader_yes_no(reader, &rule->log_debug)) return NOT_OK_RESPONSE; continue; /* log_query */ case OD_LLOG_QUERY: if (!od_config_reader_yes_no(reader, &rule->log_query)) return NOT_OK_RESPONSE; continue; case OD_LLDAP_ENDPOINT_NAME: { #ifdef LDAP_FOUND if (!od_config_reader_string(reader, &rule->ldap_endpoint_name)) return NOT_OK_RESPONSE; od_ldap_endpoint_t *le = od_ldap_endpoint_find( &reader->rules->ldap_endpoints, rule->ldap_endpoint_name); if (le == NULL) { od_config_reader_error( reader, NULL, "ldap endpoint %s is unknown", rule->ldap_endpoint_name); return NOT_OK_RESPONSE; } rule->ldap_endpoint = le; continue; #else od_config_reader_error( reader, NULL, "ldap is not supported, check if ldap library is available on the system"); return NOT_OK_RESPONSE; #endif } case OD_LLDAP_STORAGE_CREDENTIALS_ATTR: { #ifdef LDAP_FOUND if (!od_config_reader_string( reader, &rule->ldap_storage_credentials_attr)) { return NOT_OK_RESPONSE; } if (rule->ldap_endpoint_name == NULL) { od_config_reader_error( reader, NULL, "ldap_endpoint_name is not defined for rule with ldap_storage_credentials_attr '%s'", rule->ldap_storage_credentials_attr); return NOT_OK_RESPONSE; } if (strlen(rule->ldap_storage_credentials_attr) == 0) { od_config_reader_error( reader, NULL, "ldap_storage_credentials_attr cannot be empty for rule with ldap_endpoint_name '%s'", rule->ldap_endpoint_name); return NOT_OK_RESPONSE; } continue; #else od_config_reader_error( reader, NULL, "ldap is not supported, check if ldap library is available on the system"); return NOT_OK_RESPONSE; #endif } case OD_LLDAP_STORAGE_CREDENTIALS: { #ifdef LDAP_FOUND if (od_config_reader_ldap_storage_credentials( reader, rule) != OK_RESPONSE) { return NOT_OK_RESPONSE; } if (rule->ldap_storage_credentials_attr == NULL) { od_config_reader_error( reader, NULL, "ldap_storage_credentials_attr is not defined for rule with ldap_endpoint_name '%s'", rule->ldap_endpoint_name); return NOT_OK_RESPONSE; } continue; #else od_config_reader_error( reader, NULL, "ldap is not supported, check if ldap library is available on the system"); return NOT_OK_RESPONSE; #endif } case OD_LWATCHDOG_LAG_QUERY: if (watchdog == NULL) { od_config_reader_error( reader, NULL, "watchdog settings specified for non-watchdog route"); return NOT_OK_RESPONSE; } if (!od_config_reader_string(reader, &watchdog->query)) { return NOT_OK_RESPONSE; } continue; case OD_LWATCHDOG_LAG_INTERVAL: if (watchdog == NULL) { od_config_reader_error( reader, NULL, "watchdog settings specified for non-watchdog route"); return NOT_OK_RESPONSE; } if (!od_config_reader_number(reader, &watchdog->interval)) { return NOT_OK_RESPONSE; } continue; case OD_LCATCHUP_TIMEOUT: if (!od_config_reader_number(reader, &rule->catchup_timeout)) { return NOT_OK_RESPONSE; } continue; case OD_LCATCHUP_CHECKS: if (!od_config_reader_number(reader, &rule->catchup_checks)) { return NOT_OK_RESPONSE; } continue; /* options */ case OD_LOPTIONS: if (od_config_reader_pgoptions(reader, &rule->vars) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } continue; /* backend startup options */ case OD_LBACKEND_STARTUP_OPTIONS: if (od_config_reader_backend_pgoptions(reader, rule) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } continue; default: return NOT_OK_RESPONSE; } } return NOT_OK_RESPONSE; } static int od_config_reader_route(od_config_reader_t *reader, char *db_name, int db_name_len, int db_is_default, od_extention_t *extentions) { char *user_name = NULL; int user_name_len = 0; int user_is_default = 0; /* user name or default */ if (od_config_reader_is(reader, OD_PARSER_STRING)) { if (!od_config_reader_string(reader, &user_name)) return NOT_OK_RESPONSE; } else { if (!od_config_reader_keyword(reader, &od_config_keywords[OD_LDEFAULT])) return NOT_OK_RESPONSE; user_is_default = 1; user_name = strdup("default_user"); if (user_name == NULL) return NOT_OK_RESPONSE; } user_name_len = strlen(user_name); /* address and mask or default */ char *addr_str = NULL; char *mask_str = NULL; od_address_range_t address_range; address_range = od_address_range_create_default(); address_range.string_value = NULL; address_range.string_value_len = 0; address_range.is_default = 0; address_range.is_hostname = 0; if (od_config_reader_is(reader, OD_PARSER_STRING)) { if (!od_config_reader_string(reader, &address_range.string_value)) return NOT_OK_RESPONSE; } else { bool is_default_keyword; is_default_keyword = od_config_reader_keyword_is( reader, &od_config_keywords[OD_LDEFAULT]); if (!is_default_keyword && !od_config_reader_symbol_is(reader, '{')) return NOT_OK_RESPONSE; if (is_default_keyword) od_config_reader_keyword( reader, &od_config_keywords[OD_LDEFAULT]); address_range = od_address_range_create_default(); if (address_range.string_value == NULL) return NOT_OK_RESPONSE; } if (address_range.is_default == 0) { addr_str = strdup(address_range.string_value); mask_str = strchr(addr_str, '/'); if (mask_str) *mask_str++ = 0; if (od_address_read(&address_range.addr, addr_str) == NOT_OK_RESPONSE) { int is_valid_hostname = od_address_hostname_validate( address_range.string_value); if (is_valid_hostname == -1) { od_config_reader_error( reader, NULL, "could not compile regex"); return NOT_OK_RESPONSE; } else if (is_valid_hostname == 0) { address_range.is_hostname = 1; } else { od_config_reader_error(reader, NULL, "invalid address"); return NOT_OK_RESPONSE; } } else if (mask_str) { if (od_address_range_read_prefix(&address_range, mask_str) == -1) { od_config_reader_error( reader, NULL, "invalid network prefix length"); return NOT_OK_RESPONSE; } } else { od_config_reader_error(reader, NULL, "expected network mask"); return NOT_OK_RESPONSE; } } /* ensure rule does not exists and add new rule */ od_rule_t *rule; rule = od_rules_match(reader->rules, db_name, user_name, &address_range, db_is_default, user_is_default, 0); if (rule) { od_errorf(reader->error, "route '%s.%s': is redefined", db_name, user_name); free(user_name); return NOT_OK_RESPONSE; } rule = od_rules_add(reader->rules); if (rule == NULL) { free(user_name); return NOT_OK_RESPONSE; } rule->user_is_default = user_is_default; rule->user_name_len = user_name_len; rule->user_name = strdup(user_name); free(user_name); if (rule->user_name == NULL) return NOT_OK_RESPONSE; rule->db_is_default = db_is_default; rule->db_name_len = db_name_len; rule->db_name = strdup(db_name); if (rule->db_name == NULL) return NOT_OK_RESPONSE; address_range.string_value_len = strlen(address_range.string_value); rule->address_range = address_range; free(addr_str); /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; /* unreach */ return od_config_reader_rule_settings(reader, rule, extentions, NULL); } static inline int od_config_reader_watchdog(od_config_reader_t *reader, od_storage_watchdog_t *watchdog, od_extention_t *extentions) { watchdog->route_usr = "watchdog_int"; watchdog->route_db = "watchdog_int"; int user_name_len = 0; user_name_len = strlen(watchdog->route_usr); /* ensure rule does not exists and add new rule */ od_rule_t *rule; od_address_range_t address_range = od_address_range_create_default(); rule = od_rules_match(reader->rules, watchdog->route_db, watchdog->route_usr, &address_range, 0, 0, 1); if (rule) { od_errorf(reader->error, "route '%s.%s': is redefined", watchdog->route_db, watchdog->route_usr); return NOT_OK_RESPONSE; } rule = od_rules_add(reader->rules); if (rule == NULL) { return NOT_OK_RESPONSE; } rule->user_is_default = 0; rule->user_name_len = user_name_len; rule->user_name = strdup(watchdog->route_usr); if (rule->user_name == NULL) { return NOT_OK_RESPONSE; } rule->db_is_default = 0; rule->db_name_len = strlen(watchdog->route_db); rule->db_name = strdup(watchdog->route_db); if (rule->db_name == NULL) return NOT_OK_RESPONSE; rule->address_range = address_range; /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; /* unreach */ if (od_config_reader_rule_settings(reader, rule, extentions, watchdog) == NOT_OK_RESPONSE) { return NOT_OK_RESPONSE; } // force several settings watchdog->storage_db = rule->storage_db; watchdog->storage_user = rule->storage_user; rule->pool->routing = OD_RULE_POOL_INTERVAL; return OK_RESPONSE; } #ifdef LDAP_FOUND static inline od_retcode_t od_config_reader_ldap_endpoint(od_config_reader_t *reader) { od_ldap_endpoint_t *ldap_current; ldap_current = od_ldap_endpoint_alloc(); if (!ldap_current) { goto error; } /* name */ if (!od_config_reader_string(reader, &ldap_current->name)) { goto error; } if (od_ldap_endpoint_find(&reader->rules->ldap_endpoints, ldap_current->name) != NULL) { od_config_reader_error(reader, NULL, "duplicate ldap endpoint definition: %s", ldap_current->name); goto error; } od_rules_ldap_endpoint_add(reader->rules, ldap_current); /* { */ if (!od_config_reader_symbol(reader, '{')) { goto error; } for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { goto init; } /* fall through */ case OD_PARSER_KEYWORD: break; default: od_config_reader_error(reader, &token, "unexpected symbol or token"); goto error; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); return NOT_OK_RESPONSE; } switch (keyword->id) { case OD_LLDAP_SERVER: { if (!od_config_reader_string(reader, &ldap_current->ldapserver)) goto error; } break; case OD_LLDAP_PORT: { if (!od_config_reader_number64(reader, &ldap_current->ldapport)) goto error; } break; case OD_LLDAP_PREFIX: { if (!od_config_reader_string(reader, &ldap_current->ldapprefix)) goto error; } break; case OD_LLDAP_SUFFIX: { if (!od_config_reader_string(reader, &ldap_current->ldapsuffix)) goto error; } break; case OD_LLDAP_SEARCH_ATTRIBUTE: { if (!od_config_reader_string( reader, &ldap_current->ldapsearchattribute)) goto error; } break; case OD_LLDAP_SCOPE: { if (!od_config_reader_string(reader, &ldap_current->ldapscope)) goto error; } break; case OD_LLDAP_SCHEME: { if (!od_config_reader_string(reader, &ldap_current->ldapscheme)) goto error; } break; case OD_LLDAP_BASEDN: { if (!od_config_reader_string(reader, &ldap_current->ldapbasedn)) goto error; } break; case OD_LLDAP_BINDDN: { if (!od_config_reader_string(reader, &ldap_current->ldapbinddn)) goto error; } break; case OD_LLDAP_BIND_PASSWD: { if (!od_config_reader_string( reader, &ldap_current->ldapbindpasswd)) goto error; } break; case OD_LLDAP_SEARCH_FILTER: { if (!od_config_reader_string( reader, &ldap_current->ldapsearchfilter)) goto error; } break; } } init: if (od_ldap_endpoint_prepare(ldap_current) != OK_RESPONSE) { od_config_reader_error(reader, NULL, "failed to initialize ldap endpoint"); goto error; } /* unreach */ return OK_RESPONSE; error: if (ldap_current) { od_ldap_endpoint_free(ldap_current); } return NOT_OK_RESPONSE; } #endif static inline od_retcode_t od_config_reader_module(od_config_reader_t *reader, od_extention_t *ext) { char *module_path = NULL; int rc; rc = od_config_reader_string(reader, &module_path); if (rc == -1) { return rc; } od_module_t *module = od_modules_find(ext->modules, module_path); if (module != NULL) { free(module_path); // skip all related conf /* { */ if (!od_config_reader_symbol(reader, '{')) return NOT_OK_RESPONSE; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { return 0; } /* fall through */ default: continue; } } return OK_RESPONSE; } if (od_target_module_add(NULL, ext->modules, module_path) == OD_MODULE_CB_FAIL_RETCODE) { goto error; } module = od_modules_find(ext->modules, module_path); assert(module != NULL); if (module->config_module_init_db == NULL) { goto error; } rc = module->config_module_init_db(reader); if (rc != OD_MODULE_CB_OK_RETCODE) { goto error; } return OK_RESPONSE; error: free(module_path); return NOT_OK_RESPONSE; } static int od_config_reader_database(od_config_reader_t *reader, od_extention_t *extentions) { char *db_name = NULL; int db_name_len = 0; int db_is_default = 0; /* name or default */ if (od_config_reader_is(reader, OD_PARSER_STRING)) { if (!od_config_reader_string(reader, &db_name)) return NOT_OK_RESPONSE; } else { if (!od_config_reader_keyword(reader, &od_config_keywords[OD_LDEFAULT])) return NOT_OK_RESPONSE; db_is_default = 1; db_name = strdup("default_db"); if (db_name == NULL) return NOT_OK_RESPONSE; } db_name_len = strlen(db_name); /* { */ if (!od_config_reader_symbol(reader, '{')) goto error; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_KEYWORD: break; case OD_PARSER_EOF: od_config_reader_error(reader, &token, "unexpected end of config file"); goto error; case OD_PARSER_SYMBOL: /* } */ if (token.value.num == '}') { free(db_name); return 0; } /* fall through */ default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); goto error; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); goto error; } switch (keyword->id) { /* user */ case OD_LUSER: rc = od_config_reader_route(reader, db_name, db_name_len, db_is_default, extentions); if (rc == -1) goto error; continue; default: od_config_reader_error(reader, &token, "unexpected parameter"); goto error; } } /* unreach */ return NOT_OK_RESPONSE; error: free(db_name); return NOT_OK_RESPONSE; } static int od_config_reader_hba_import(od_config_reader_t *config_reader) { od_config_reader_t reader; memset(&reader, 0, sizeof(reader)); reader.config = config_reader->config; reader.error = config_reader->error; reader.hba_rules = config_reader->hba_rules; int rc; rc = od_config_reader_open(&reader, config_reader->config->hba_file); if (rc == -1) return -1; rc = od_hba_reader_parse(&reader); od_config_reader_close(&reader); return rc; } static int od_config_reader_parse(od_config_reader_t *reader, od_extention_t *extentions) { od_config_t *config = reader->config; for (;;) { od_token_t token; int rc; rc = od_parser_next(&reader->parser, &token); switch (rc) { case OD_PARSER_EOF: goto success; case OD_PARSER_KEYWORD: break; default: od_config_reader_error( reader, &token, "incorrect or unexpected parameter"); goto error; } od_keyword_t *keyword; keyword = od_keyword_match(od_config_keywords, &token); if (keyword == NULL) { od_config_reader_error(reader, &token, "unknown parameter"); goto error; } switch (keyword->id) { /* include */ case OD_LINCLUDE: { char *config_file = NULL; if (!od_config_reader_string(reader, &config_file)) return NOT_OK_RESPONSE; rc = od_config_reader_import( reader->config, reader->rules, reader->error, extentions, reader->global, reader->hba_rules, config_file); free(config_file); if (rc == -1) { goto error; } continue; } /* daemonize */ case OD_LDAEMONIZE: if (!od_config_reader_yes_no(reader, &config->daemonize)) { goto error; } continue; /* priority */ case OD_LPRIORITY: if (!od_config_reader_number(reader, &config->priority)) { goto error; } continue; /* sequential_routing */ case OD_LSEQROUTING: if (!od_config_reader_yes_no( reader, &config->sequential_routing)) { goto error; } continue; /* pid_file */ case OD_LPID_FILE: if (!od_config_reader_string(reader, &config->pid_file)) { goto error; } continue; /* unix_socket_dir */ case OD_LUNIX_SOCKET_DIR: if (!od_config_reader_string( reader, &config->unix_socket_dir)) { goto error; } continue; /* unix_socket_mode */ case OD_LUNIX_SOCKET_MODE: if (!od_config_reader_string( reader, &config->unix_socket_mode)) { goto error; } continue; /* locks_dir */ case OD_LLOCKS_DIR: if (!od_config_reader_string(reader, &config->locks_dir)) { goto error; } continue; /* enable_online_restart */ case OD_LENABLE_ONLINE_RESTART: if (!od_config_reader_yes_no( reader, &config->enable_online_restart_feature)) { goto error; } continue; /* graceful_die_on_errors */ case OD_LGRACEFUL_DIE_ON_ERRORS: if (!od_config_reader_yes_no( reader, &config->graceful_die_on_errors)) { goto error; } continue; case OD_LBINDWITH_REUSEPORT: if (!od_config_reader_yes_no( reader, &config->bindwith_reuseport)) { goto error; } continue; /* log_debug */ case OD_LLOG_DEBUG: if (!od_config_reader_yes_no(reader, &config->log_debug)) { goto error; } continue; /* log_stdout */ case OD_LLOG_TO_STDOUT: if (!od_config_reader_yes_no(reader, &config->log_to_stdout)) { goto error; } continue; /* log_config */ case OD_LLOG_CONFIG: if (!od_config_reader_yes_no(reader, &config->log_config)) { goto error; } continue; /* log_session */ case OD_LLOG_SESSION: if (!od_config_reader_yes_no(reader, &config->log_session)) { goto error; } continue; /* log_query */ case OD_LLOG_QUERY: if (!od_config_reader_yes_no(reader, &config->log_query)) { goto error; } continue; /* log_stats */ case OD_LLOG_STATS: if (!od_config_reader_yes_no(reader, &config->log_stats)) { goto error; } continue; /* log_format */ case OD_LLOG_FORMAT: if (!od_config_reader_string(reader, &config->log_format)) { goto error; } continue; /* log_file */ case OD_LLOG_FILE: if (!od_config_reader_string(reader, &config->log_file)) { goto error; } continue; /* log_syslog */ case OD_LLOG_SYSLOG: if (!od_config_reader_yes_no(reader, &config->log_syslog)) { goto error; } continue; /* log_syslog_ident */ case OD_LLOG_SYSLOG_IDENT: if (!od_config_reader_string( reader, &config->log_syslog_ident)) { goto error; } continue; /* log_syslog_facility */ case OD_LLOG_SYSLOG_FACILITY: if (!od_config_reader_string( reader, &config->log_syslog_facility)) { goto error; } continue; /* stats_interval */ case OD_LSTATS_INTERVAL: if (!od_config_reader_number(reader, &config->stats_interval)) { goto error; } continue; /* client_max */ case OD_LCLIENT_MAX: if (!od_config_reader_number(reader, &config->client_max)) { goto error; } config->client_max_set = 1; continue; /* client_max_routing */ case OD_LCLIENT_MAX_ROUTING: if (!od_config_reader_number( reader, &config->client_max_routing)) { goto error; } continue; /* server_login_retry */ case OD_LSERVER_LOGIN_RETRY: if (!od_config_reader_number( reader, &config->server_login_retry)) { goto error; } continue; /* readahead */ case OD_LREADAHEAD: if (!od_config_reader_number(reader, &config->readahead)) { goto error; } continue; /* nodelay */ case OD_LNODELAY: if (!od_config_reader_yes_no(reader, &config->nodelay)) { goto error; } continue; /* keepalive */ case OD_LKEEPALIVE: if (!od_config_reader_number(reader, &config->keepalive)) { goto error; } continue; /* keepalive_keep_interval */ case OD_LKEEPALIVE_INTERVAL: if (!od_config_reader_number( reader, &config->keepalive_keep_interval)) { goto error; } continue; /* keepalive_probes */ case OD_LKEEPALIVE_PROBES: if (!od_config_reader_number( reader, &config->keepalive_probes)) { goto error; } continue; /* keepalive_usr_timeout */ case OD_LKEEPALIVE_USR_TIMEOUT: if (!od_config_reader_number( reader, &config->keepalive_usr_timeout)) { goto error; } continue; /* log_stats_prom */ case OD_LLOG_GENERAL_STATS_PROM: { if (!od_config_reader_yes_no( reader, &config->log_general_stats_prom)) goto error; continue; } case OD_LLOG_ROUTE_STATS_PROM: { if (!od_config_reader_yes_no( reader, &config->log_route_stats_prom)) goto error; continue; } case OD_LPROMHTTP_PORT: { int port; if (!od_config_reader_number(reader, &port)) goto error; #ifdef PROMHTTP_FOUND if (od_prom_set_port( port, ((od_cron_t *)(reader->global->cron)) ->metrics) != OK_RESPONSE) goto error; #endif continue; } /* workers */ case OD_LWORKERS: { od_token_t tok; int rc; rc = od_parser_next(&reader->parser, &tok); switch (rc) { case OD_PARSER_NUM: { config->workers = tok.value.num; } break; case OD_PARSER_STRING: { if (strncmp(tok.value.string.pointer, "auto", tok.value.string.size) == 0) { config->workers = (1 + od_get_ncpu()) >> 1; break; } } // fall through default: od_config_reader_error( reader, &tok, "expected 'number' or '\"auto\"'"); goto error; } } continue; /* resolvers */ case OD_LRESOLVERS: if (!od_config_reader_number(reader, &config->resolvers)) { goto error; } continue; /* pipeline */ case OD_LPIPELINE: /* fallthrough */ case OD_LCACHE: /* cache */ /* fallthrough */ case OD_LCACHE_CHUNK: /* cache_chunk */ /* fallthrough */ case OD_LPACKET_WRITE_QUEUE: /* packet write queue */ /* fallthrough */ case OD_LPACKET_READ_SIZE: { /* deprecated */ int unused; if (!od_config_reader_number(reader, &unused)) { goto error; } continue; } /* cache_msg_gc_size */ case OD_LCACHE_MSG_GC_SIZE: if (!od_config_reader_number( reader, &config->cache_msg_gc_size)) { goto error; } continue; /* cache_coroutine */ case OD_LCACHE_COROUTINE: if (!od_config_reader_number( reader, &config->cache_coroutine)) { goto error; } continue; /* coroutine_stack_size */ case OD_LCOROUTINE_STACK_SIZE: if (!od_config_reader_number( reader, &config->coroutine_stack_size)) { goto error; } continue; /* listen */ case OD_LLISTEN: rc = od_config_reader_listen(reader); if (rc == -1) { goto error; } continue; /* storage */ case OD_LSTORAGE: rc = od_config_reader_storage(reader, extentions); if (rc == -1) { goto error; } continue; /* database */ case OD_LDATABASE: rc = od_config_reader_database(reader, extentions); if (rc == -1) { goto error; } continue; /* ldap service */ case OD_LLDAP_ENDPOINT: { #ifdef LDAP_FOUND rc = od_config_reader_ldap_endpoint(reader); if (rc != OK_RESPONSE) { goto error; } continue; #else od_config_reader_error(reader, &token, "unexpected parameter"); goto error; #endif } /* module */ case OD_LMODULE: { rc = od_config_reader_module(reader, extentions); if (rc == -1) { goto error; } continue; } case OD_LHBA_FILE: { rc = od_config_reader_string(reader, &config->hba_file); if (rc == -1) goto error; rc = od_config_reader_hba_import(reader); if (rc == -1) goto error; continue; } default: od_config_reader_error(reader, &token, "unexpected parameter"); goto error; } } /* unreach */ return NOT_OK_RESPONSE; error: return NOT_OK_RESPONSE; success: if (!config->client_max_routing) { config->client_max_routing = config->workers * 16; } return 0; } int od_config_reader_import(od_config_t *config, od_rules_t *rules, od_error_t *error, od_extention_t *extentions, od_global_t *global, od_hba_rules_t *hba_rules, char *config_file) { od_config_reader_t reader; memset(&reader, 0, sizeof(reader)); reader.error = error; reader.config = config; reader.rules = rules; reader.hba_rules = hba_rules; reader.global = global; int rc; rc = od_config_reader_open(&reader, config_file); if (rc == -1) { return NOT_OK_RESPONSE; } rc = od_config_reader_parse(&reader, extentions); od_config_reader_close(&reader); return rc; }