From b8bc34457e36e7c1f479869369c37d41e0ac946e Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Fri, 29 Jun 2007 15:55:34 +0000 Subject: [PATCH] connstr enhanchements * allow to specify hosts by name - gethostbyname() * allow to specify custom unix socket location for db * allow quoted values --- src/bouncer.h | 1 + src/loader.c | 151 +++++++++++++++++++++++++++++++++++++++++++------- src/objects.c | 8 ++- src/sbuf.c | 4 +- src/sbuf.h | 2 +- src/system.h | 5 ++ 6 files changed, 147 insertions(+), 24 deletions(-) diff --git a/src/bouncer.h b/src/bouncer.h index c586eb0..cb93033 100644 --- a/src/bouncer.h +++ b/src/bouncer.h @@ -193,6 +193,7 @@ struct PgDatabase { /* address prepared for connect() */ PgAddr addr; + char unix_socket_dir[UNIX_PATH_MAX]; /* max server connections in one pool */ int pool_size; diff --git a/src/loader.c b/src/loader.c index 1e31b2c..bc08d59 100644 --- a/src/loader.c +++ b/src/loader.c @@ -22,38 +22,115 @@ #include "bouncer.h" +#include + /* * ConnString parsing */ -/* get key=val pair from connstring */ -static char * getpair(char *p, - char **key_p, int *key_len, - char **val_p, int *val_len) +/* just skip whitespace */ +static char *cstr_skip_ws(char *p) { while (*p && *p == ' ') p++; - *key_p = p; + return p; +} + +/* parse paramenter name before '=' */ +static char *cstr_get_key(char *p, char **dst_p) +{ + char *end; + p = cstr_skip_ws(p); + *dst_p = p; while (*p && *p != '=' && *p != ' ') p++; - *key_len = p - *key_p; - if (*p == '=') - p++; - *val_p = p; - while (*p && *p != ' ') - p++; - *val_len = p - *val_p; + end = p; + p = cstr_skip_ws(p); + /* fail if no '=' or empty name */ + if (*p != '=' || *dst_p == end) + return NULL; + *end = 0; + return p + 1; +} - while (*p && *p == ' ') +/* unquote the quoted value after first quote */ +static char *cstr_unquote_value(char *p) +{ + char *s = p; + while (1) { + if (!*p) + return NULL; + if (p[0] == '\'') { + if (p[1] == '\'') + p++; + else + break; + } + *s++ = *p++; + } + /* terminate actual value */ + *s = 0; + /* return position after quote */ + return p + 1; +} + +/* parse value, possibly quoted */ +static char *cstr_get_value(char *p, char **dst_p) +{ + p = cstr_skip_ws(p); + if (*p == '\'') { + *dst_p = ++p; + p = cstr_unquote_value(p); + if (!p) + return NULL; + } else { + *dst_p = p; + while (*p && *p != ' ') + p++; + } + if (*p) { + /* if not EOL, cut value */ + *p = 0; p++; + } + /* disallow empty values */ + if (*dst_p[0] == 0) + return NULL; return p; } +/* + * Get key=val pair from connstring. returns position it stopped + * or NULL on error. EOF is signaled by *key = 0. + */ +static char * cstr_get_pair(char *p, + char **key_p, + char **val_p) +{ + p = cstr_skip_ws(p); + *key_p = *val_p = p; + if (*p == 0) + return p; + + /* read key */ + p = cstr_get_key(p, key_p); + if (!p) + return NULL; + + /* read value */ + p = cstr_get_value(p, val_p); + if (!p) + return NULL; + + log_noise("cstr_get_pair: \"%s\"=\"%s\"", *key_p, *val_p); + + return cstr_skip_ws(p); +} + /* fill PgDatabase from connstr */ void parse_database(char *name, char *connstr) { char *p, *key, *val; - int klen, vlen; PktBuf buf; PgDatabase *db; int pool_size = -1; @@ -65,17 +142,19 @@ void parse_database(char *name, char *connstr) char *password = ""; char *client_encoding = NULL; char *datestyle = NULL; + char *unix_dir = ""; in_addr_t v_addr = INADDR_NONE; int v_port; p = connstr; while (*p) { - p = getpair(p, &key, &klen, &val, &vlen); - if (*key == 0 || *val == 0 || klen == 0 || vlen == 0) + p = cstr_get_pair(p, &key, &val); + if (p == NULL) { + log_error("%s: syntax error in connstring", name); + return; + } else if (!key[0]) break; - key[klen] = 0; - val[vlen] = 0; if (strcmp("dbname", key) == 0) dbname = val; @@ -95,25 +174,50 @@ void parse_database(char *name, char *connstr) pool_size = atoi(val); else { log_error("skipping database %s because" - " of bad connstring: %s", name, connstr); + " of unknown parameter in connstring: %s", name, key); return; } } + /* host= */ if (!host) { + /* default unix socket dir */ if (!cf_unix_socket_dir) { log_error("skipping database %s because" " unix socket not configured", name); return; } - } else { + } else if (host[0] == '/') { + /* custom unix socket dir */ + unix_dir = host; + host = NULL; + } else if (host[0] >= '0' && host[0] <= '9') { + /* ip-address */ v_addr = inet_addr(host); if (v_addr == INADDR_NONE) { log_error("skipping database %s because" " of bad host: %s", name, host); return; } + } else { + /* resolve host by name */ + struct hostent *h = gethostbyname(host); + if (h == NULL || h->h_addr_list[0] == NULL) { + log_error("%s: resolving host=%s failed: %s", + name, host, hstrerror(h_errno)); + return; + } + if (h->h_addrtype != AF_INET || h->h_length != 4) { + log_error("%s: host=%s has unknown addr type", + name, host); + return; + } + + /* result should be already in correct endianess */ + memcpy(&v_addr, h->h_addr_list[0], 4); } + + /* port= */ v_port = atoi(port); if (v_port == 0) { log_error("skipping database %s because" @@ -127,6 +231,7 @@ void parse_database(char *name, char *connstr) return; } + /* if updating old db, check if anything changed */ if (db->dbname) { bool changed = false; if (strcmp(db->dbname, dbname) != 0) @@ -145,6 +250,8 @@ void parse_database(char *name, char *connstr) changed = true; else if (!username && db->forced_user) changed = true; + else if (strcmp(db->unix_socket_dir, unix_dir) != 0) + changed = true; if (changed) tag_database_dirty(db); @@ -155,6 +262,10 @@ void parse_database(char *name, char *connstr) db->addr.port = v_port; db->addr.ip_addr.s_addr = v_addr; db->addr.is_unix = host ? 0 : 1; + strlcpy(db->unix_socket_dir, unix_dir, sizeof(db->unix_socket_dir)); + + if (host) + log_debug("%s: host=%s/%s", name, host, inet_ntoa(db->addr.ip_addr)); pktbuf_static(&buf, db->startup_params, sizeof(db->startup_params)); diff --git a/src/objects.c b/src/objects.c index 98ce561..7385109 100644 --- a/src/objects.c +++ b/src/objects.c @@ -695,6 +695,7 @@ void launch_new_connection(PgPool *pool) { PgSocket *server; int total; + const char *unix_dir = cf_unix_socket_dir; /* allow only small number of connection attempts at a time */ if (!statlist_empty(&pool->new_server_list)) { @@ -737,8 +738,13 @@ void launch_new_connection(PgPool *pool) if (cf_log_connections) slog_info(server, "new connection to server"); + /* override socket location if requested */ + if (server->pool->db->unix_socket_dir[0]) + unix_dir = server->pool->db->unix_socket_dir; + /* start connecting */ - sbuf_connect(&server->sbuf, &server->addr, cf_server_connect_timeout / USEC); + sbuf_connect(&server->sbuf, &server->addr, unix_dir, + cf_server_connect_timeout / USEC); } /* new client connection attempt */ diff --git a/src/sbuf.c b/src/sbuf.c index 1e10be6..f4fcfdd 100644 --- a/src/sbuf.c +++ b/src/sbuf.c @@ -94,7 +94,7 @@ void sbuf_accept(SBuf *sbuf, int sock, bool is_unix) } /* need to connect() to get a socket */ -void sbuf_connect(SBuf *sbuf, const PgAddr *addr, int timeout_sec) +void sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec) { int res, sock, domain; struct sockaddr_in sa_in; @@ -113,7 +113,7 @@ void sbuf_connect(SBuf *sbuf, const PgAddr *addr, int timeout_sec) memset(sa, 0, len); sa_un.sun_family = AF_UNIX; snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), - "%s/.s.PGSQL.%d", cf_unix_socket_dir, addr->port); + "%s/.s.PGSQL.%d", unix_dir, addr->port); domain = AF_UNIX; } else { sa = (void*)&sa_in; diff --git a/src/sbuf.h b/src/sbuf.h index 4f82651..1d57b5b 100644 --- a/src/sbuf.h +++ b/src/sbuf.h @@ -70,7 +70,7 @@ struct SBuf { void sbuf_init(SBuf *sbuf, sbuf_proto_cb_t proto_fn, void *arg); void sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix); -void sbuf_connect(SBuf *sbuf, const PgAddr *addr, int timeout_sec); +void sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec); void sbuf_pause(SBuf *sbuf); void sbuf_continue(SBuf *sbuf); diff --git a/src/system.h b/src/system.h index 7edb862..bd33a23 100644 --- a/src/system.h +++ b/src/system.h @@ -67,6 +67,11 @@ #define OPEN_MAX sysconf(_SC_OPEN_MAX) #endif +#ifndef UNIX_PATH_MAX +/* #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)0)->sun_path)) */ +#define UNIX_PATH_MAX 128 /* actual sizeof() will be applied later anyway */ +#endif + /* how many microseconds in a second */ #define USEC (1000000LL) -- 2.40.0