]> granicus.if.org Git - pgbouncer/commitdiff
connstr enhanchements
authorMarko Kreen <markokr@gmail.com>
Fri, 29 Jun 2007 15:55:34 +0000 (15:55 +0000)
committerMarko Kreen <markokr@gmail.com>
Fri, 29 Jun 2007 15:55:34 +0000 (15:55 +0000)
* allow to specify hosts by name - gethostbyname()
* allow to specify custom unix socket location for db
* allow quoted values

src/bouncer.h
src/loader.c
src/objects.c
src/sbuf.c
src/sbuf.h
src/system.h

index c586eb077ad58471ec23ef78363759c52778d8ca..cb930332f7a3cad65e43625cbd5294bafb5f0255 100644 (file)
@@ -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;
index 1e31b2c87a17f361439af1f0df24cdcb42ef541d..bc08d597198078b52ef9d75798b412b4d55a0e21 100644 (file)
 
 #include "bouncer.h"
 
+#include <netdb.h>
+
 /*
  * 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));
 
index 98ce561ee3ba5e3faa0854515d97d3743517bd4f..7385109d7f8820419e0a84cd18a4491830372182 100644 (file)
@@ -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 */
index 1e10be6bca1490d405b563ab744cf4a00f5f0fc6..f4fcfddfdc5fbefaa870d94689d7176817a426b2 100644 (file)
@@ -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;
index 4f8265150f4e807461f46d48853466e973c0a2fa..1d57b5bc0b008d132b3720fc44e53439d7700342 100644 (file)
@@ -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);
index 7edb8624d1ccef3106078c44352c4b9773c48830..bd33a2304c5972bc31efa4a67fe291baf8c8219f 100644 (file)
 #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)