# sources
SRCS = client.c loader.c objects.c pooler.c proto.c sbuf.c server.c util.c \
admin.c stats.c takeover.c janitor.c pktbuf.c system.c main.c \
- varcache.c
+ varcache.c dnslookup.c
HDRS = client.h loader.h objects.h pooler.h proto.h sbuf.h server.h util.h \
admin.h stats.h takeover.h janitor.h pktbuf.h system.h bouncer.h \
- varcache.h iobuf.h
+ varcache.h iobuf.h dnslookup.h
# data & dirs to include in tgz
DOCS = doc/overview.txt doc/usage.txt doc/config.txt doc/todo.txt
Default: 3600
+==== dns_max_ttl ====
+
+How long the DNS lookups can be cached. If a DNS lookup returns
+several answers, pgbouncer will robin-between them in the meantime.
+Actual DNS TTL is ignored. [seconds]
+
+Default: 15
+
=== Dangerous timeouts ===
Setting following timeouts cause unexpected errors.
#include "sbuf.h"
#include "pktbuf.h"
#include "varcache.h"
+#include "dnslookup.h"
#include "admin.h"
#include "loader.h"
PgUser *forced_user; /* if not NULL, the user/psw is forced */
- PgAddr addr; /* address prepared for connect() */
- char unix_socket_dir[UNIX_PATH_MAX]; /* custom unix socket dir */
+ const char *host; /* host or unix socket name */
+ int port;
int pool_size; /* max server connections in one pool */
int res_pool_size; /* additional server connections in case of trouble */
extern usec_t cf_client_login_timeout;
extern int cf_server_round_robin;
extern int cf_disable_pqexec;
+extern usec_t cf_dns_max_ttl;
extern int cf_auth_type;
extern char *cf_auth_file;
extern usec_t g_suspend_start;
+extern struct DNSContext *adns;
+
static inline PgSocket * _MUSTCHECK
pop_socket(struct StatList *slist)
{
--- /dev/null
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <usual/base.h>
+
+struct DNSContext;
+
+typedef void (*adns_callback_f)(void *arg, int af, const void *addr);
+
+struct DNSContext *adns_create_context(void);
+void adns_reload(struct DNSContext *ctx);
+void adns_free_context(struct DNSContext *ctx);
+
+void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *arg);
+
struct SBuf {
struct event ev; /* libevent handle */
- bool is_unix; /* is it unix socket */
uint8_t wait_type; /* track wait state */
uint8_t pkt_action; /* method for handling current pkt */
void sbuf_init(SBuf *sbuf, sbuf_cb_t proto_fn);
bool sbuf_accept(SBuf *sbuf, int read_sock, bool is_unix) _MUSTCHECK;
-bool sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec) _MUSTCHECK;
+bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec) _MUSTCHECK;
bool sbuf_pause(SBuf *sbuf) _MUSTCHECK;
void sbuf_continue(SBuf *sbuf);
{
PgDatabase *db;
struct List *item;
- char *host;
const char *f_user;
PktBuf *buf;
statlist_for_each(item, &database_list) {
db = container_of(item, PgDatabase, head);
- if (!db->addr.is_unix) {
- host = inet_ntoa(db->addr.ip_addr);
- } else
- host = NULL;
-
f_user = db->forced_user ? db->forced_user->name : NULL;
pktbuf_write_DataRow(buf, "ssissii",
- db->name, host, db->addr.port,
+ db->name, db->host, db->port,
db->dbname, f_user,
db->pool_size,
db->res_pool_size);
if (!db)
fatal("no memory for admin database");
- db->addr.port = cf_listen_port;
- db->addr.is_unix = 1;
+ db->port = cf_listen_port;
db->pool_size = 2;
db->admin = 1;
if (!force_user(db, "pgbouncer", ""))
--- /dev/null
+/*
+ * PgBouncer - Lightweight connection pooler for PostgreSQL.
+ *
+ * Copyright (c) 2007-2010 Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bouncer.h"
+
+/*
+ * libevent1 - returns TTL, ignores hosts file.
+ * libevent2 - does not return TTL, uses hosts file.
+ */
+
+/* do we have libevent2? */
+#ifdef EV_ET
+#define LIBEVENT2
+#endif
+
+#ifdef LIBEVENT2
+#include <event2/dns.h>
+#else
+#include <evdns.h>
+#endif
+
+
+struct UserCallback {
+ struct List node;
+ adns_callback_f cb_func;
+ void *cb_arg;
+};
+
+struct DNSRequest {
+ struct AANode node;
+ struct DNSContext *ctx;
+
+ struct List ucb_list;
+
+ const char *name;
+ int namelen;
+
+ bool done;
+
+ int res_af;
+ int res_count;
+ int res_pos;
+ void *res_list;
+ usec_t res_ttl;
+};
+
+struct DNSContext {
+ struct AATree req_tree;
+ void *edns;
+};
+
+static void deliver_info(struct DNSRequest *req);
+
+#undef log_debug
+#define log_debug log_warning
+
+
+#ifdef LIBEVENT2
+
+/*
+ * ADNS with libevent2 <event2/dns.h>
+ */
+
+static void got_result_gai(int result, struct evutil_addrinfo *res, void *arg)
+{
+ struct DNSRequest *req = arg;
+ struct evutil_addrinfo *ai;
+ int count = 0;
+ int af = 0;
+ int adrlen;
+ uint8_t *dst;
+
+ if (result != DNS_ERR_NONE) {
+ /* lookup failed */
+ log_warning("lookup failed: %s: result=%d", req->name, result);
+ goto failed;
+ }
+
+ for (ai = res; ai; ai = ai->ai_next) {
+ /* pick single family for this address */
+ if (!af) {
+ if (ai->ai_family == AF_INET) {
+ af = ai->ai_family;
+ req->res_af = af;
+ adrlen = 4;
+ } else {
+ continue;
+ }
+ }
+ if (ai->ai_family != af)
+ continue;
+ count++;
+ }
+
+ /* did not found usable entry */
+ if (!af) {
+ log_warning("dns(%s): no usable address", req->name);
+ evutil_freeaddrinfo(res);
+ goto failed;
+ }
+
+ log_debug("dns(%s): got_result_gai: count=%d, adrlen=%d", req->name, count, adrlen);
+
+ req->res_pos = 0;
+ req->done = true;
+ req->res_count = count;
+ req->res_list = malloc(adrlen * count);
+ if (!req->res_list) {
+ log_warning("req->res_list alloc failed");
+ goto failed;
+ }
+ req->res_ttl = get_cached_time() + cf_dns_max_ttl;
+
+ dst = req->res_list;
+ for (ai = res; ai; ai = ai->ai_next) {
+ struct sockaddr_in *in;
+ if (ai->ai_family != af)
+ continue;
+ in = (void*)ai->ai_addr;
+ log_debug("dns(%s) result: %s", req->name, inet_ntoa(in->sin_addr));
+ memcpy(dst, &in->sin_addr, adrlen);
+ dst += adrlen;
+ }
+
+ deliver_info(req);
+ return;
+failed:
+ req->res_af = 0;
+ req->res_list = NULL;
+ deliver_info(req);
+}
+
+static bool impl_init(struct DNSContext *ctx)
+{
+ ctx->edns = evdns_base_new(NULL, 1);
+ if (!ctx->edns) {
+ log_warning("evdns_base_new failed");
+ return false;
+ }
+ return true;
+}
+
+static void impl_launch_query(struct DNSRequest *req)
+{
+ struct evdns_getaddrinfo_request *gai_req;
+
+ gai_req = evdns_getaddrinfo(req->ctx->edns, req->name, NULL, NULL, got_result_gai, req);
+ log_noise("dns: evdns_getaddrinfo(%s)=%p", req->name, gai_req);
+}
+
+static void impl_release(struct DNSContext *ctx)
+{
+ struct evdns_base *dns = ctx->edns;
+ evdns_base_free(dns, 0);
+}
+
+#else
+
+/*
+ * ADNS with libevent 1.x <evdns.h>
+ */
+
+static void got_result_evdns(int result, char type, int count, int ttl, void *addresses, void *arg)
+{
+ struct DNSRequest *req = arg;
+ int adrlen = 4;
+
+ log_noise("dns: got_result_evdns: type=%d cnt=%d ttl=%d", type, count, ttl);
+
+ req->done = true;
+
+ if (result != DNS_ERR_NONE || count < 1) {
+ /* lookup failed */
+ goto failed;
+ } else if (type == DNS_IPv4_A) {
+ struct in_addr *a = addresses;
+ log_debug("dns(%s): got_result_evdns: %s", req->name, inet_ntoa(*a));
+ req->res_af = AF_INET;
+ adrlen = 4;
+ } else {
+ log_warning("dns(%s): got_result_evdns unknown result: %d", req->name, type);
+ /* unknown result */
+ goto failed;
+ }
+ req->res_pos = 0;
+ req->res_count = count;
+ req->res_list = malloc(adrlen * count);
+ if (!req->res_list)
+ goto failed;
+ memcpy(req->res_list, addresses, adrlen * count);
+ req->res_ttl = get_cached_time() + cf_dns_max_ttl;
+ deliver_info(req);
+ return;
+failed:
+ req->res_af = 0;
+ req->res_list = NULL;
+ deliver_info(req);
+}
+
+static bool impl_init(struct DNSContext *ctx)
+{
+ return evdns_init() == 0;
+}
+
+static void impl_launch_query(struct DNSRequest *req)
+{
+ int err;
+
+ err = evdns_resolve_ipv4(req->name, 0, got_result_evdns, req);
+ log_noise("dns(%s): evdns_resolve_ipv4 = %d", req->name, err);
+ if (err != 0 && !req->done) {
+ /* if callback was not yet called, do it now */
+ req->done = true;
+ req->res_af = 0;
+ deliver_info(req);
+ }
+}
+
+static void impl_release(struct DNSContext *ctx)
+{
+ evdns_shutdown(0);
+}
+
+#endif
+
+/*
+ * Generic framework
+ */
+
+static void deliver_info(struct DNSRequest *req)
+{
+ struct UserCallback *ucb;
+ struct List *el;
+ const uint8_t *res = req->res_list;
+ int adrlen = 0;
+
+ if (req->res_af == AF_INET)
+ adrlen = 4;
+ else if (req->res_af == AF_INET6)
+ adrlen = 16;
+loop:
+ /* get next req */
+ el = list_pop(&req->ucb_list);
+ if (!el)
+ return;
+ ucb = container_of(el, struct UserCallback, node);
+
+ /* launch callback */
+ log_noise("dns: deliver_info(%s) type=%d pos=%d", req->name, req->res_af, req->res_pos);
+ ucb->cb_func(ucb->cb_arg, req->res_af, res + req->res_pos * adrlen);
+
+ /* round-robin between results */
+ if (req->res_count > 1) {
+ req->res_pos++;
+ if (req->res_pos >= req->res_count)
+ req->res_pos = 0;
+ }
+
+ /* drop request */
+ list_del(&ucb->node);
+ free(ucb);
+
+ goto loop;
+}
+
+static int req_cmp(long arg, struct AANode *node)
+{
+ const char *s1 = (char *)arg;
+ struct DNSRequest *req = container_of(node, struct DNSRequest, node);
+ return strcmp(s1, req->name);
+}
+
+static void req_free(struct AANode *node, void *arg)
+{
+ struct UserCallback *ucb;
+ struct DNSRequest *req;
+ struct List *el;
+ req = container_of(node, struct DNSRequest, node);
+ while ((el = list_pop(&req->ucb_list)) != NULL) {
+ ucb = container_of(el, struct UserCallback, node);
+ free(ucb);
+ }
+ free(req->res_list);
+ free(req->name);
+ free(req);
+}
+
+struct DNSContext *adns_create_context(void)
+{
+ struct DNSContext *ctx = calloc(1, sizeof(*ctx));
+
+ aatree_init(&ctx->req_tree, req_cmp, req_free);
+ if (!impl_init(ctx)) {
+ adns_free_context(ctx);
+ return NULL;
+ }
+ return ctx;
+}
+
+void adns_free_context(struct DNSContext *ctx)
+{
+ if (ctx) {
+ impl_release(ctx);
+ aatree_destroy(&ctx->req_tree);
+ free(ctx);
+ }
+}
+
+void adns_resolve(struct DNSContext *ctx, const char *name, adns_callback_f cb_func, void *cb_arg)
+{
+ int namelen = strlen(name);
+ struct DNSRequest *req;
+ struct UserCallback *ucb;
+ struct AANode *node;
+
+ /* setup actual lookup */
+ node = aatree_search(&ctx->req_tree, (long)name);
+ if (node) {
+ req = container_of(node, struct DNSRequest, node);
+ } else {
+ log_debug("dns: new req: %s", name);
+ req = calloc(1, sizeof(*req));
+ if (!req)
+ goto nomem;
+ req->ctx = ctx;
+ req->name = name;
+ req->namelen = namelen;
+ list_init(&req->ucb_list);
+ aatree_insert(&ctx->req_tree, (long)req->name, &req->node);
+ impl_launch_query(req);
+ }
+
+ /* remember user callback */
+ ucb = calloc(1, sizeof(*ucb));
+ if (!ucb)
+ goto nomem;
+ list_init(&ucb->node);
+ ucb->cb_func = cb_func;
+ ucb->cb_arg = cb_arg;
+ list_append(&req->ucb_list, &ucb->node);
+
+ /* if already have final result, report it */
+ if (req->done) {
+ if (req->res_ttl < get_cached_time()) {
+ log_debug("dns: ttl over: %s", req->name);
+ req->done = false;
+ free(req->res_list);
+ req->res_list = NULL;
+ req->res_af = 0;
+
+ impl_launch_query(req);
+ } else
+ deliver_info(req);
+ }
+ return;
+nomem:
+ log_warning("dns(%s): req failed, no mem", name);
+ cb_func(cb_arg, 0, NULL);
+}
+
+
char *datestyle = NULL;
char *timezone = NULL;
char *connect_query = NULL;
- char *unix_dir = "";
char *appname = NULL;
- in_addr_t v_addr = INADDR_NONE;
int v_port;
if (strcmp(name, "*") == 0) {
}
}
- /* 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 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) {
return;
}
+ /* host= */
+ if (host) {
+ host = strdup(host);
+ if (!host) {
+ log_error("failed to allocate host=");
+ return;
+ }
+ }
+ if (!host) {
+ /* default unix socket dir */
+ if (!*cf_unix_socket_dir) {
+ log_error("skipping database %s because"
+ " unix socket not configured", name);
+ return;
+ }
+ }
+
/* tag the db as alive */
db->db_dead = 0;
/* assuming not an autodb */
bool changed = false;
if (strcmp(db->dbname, dbname) != 0)
changed = true;
- else if (host && db->addr.is_unix)
+ else if (!!host != !!db->host)
changed = true;
- else if (!host && !db->addr.is_unix)
+ else if (host && strcmp(host, db->host) != 0)
changed = true;
- else if (host && v_addr != db->addr.ip_addr.s_addr)
- changed = true;
- else if (v_port != db->addr.port)
+ else if (v_port != db->port)
changed = true;
else if (username && !db->forced_user)
changed = true;
changed = true;
else if (!username && db->forced_user)
changed = true;
- else if (strcmp(db->unix_socket_dir, unix_dir) != 0)
- changed = true;
else if ((db->connect_query && !connect_query)
|| (!db->connect_query && connect_query)
|| (connect_query && strcmp(connect_query, db->connect_query) != 0))
/* if pool_size < 0 it will be set later */
db->pool_size = pool_size;
db->res_pool_size = res_pool_size;
- db->addr.port = v_port;
- db->addr.ip_addr.s_addr = v_addr;
- db->addr.is_unix = host ? 0 : 1;
- safe_strcpy(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));
+ if (db->host)
+ free(db->host);
+ db->host = host;
+ db->port = v_port;
/* assign connect_query */
set_connect_query(db, connect_query);
exit(err);
}
+/* async dns handler */
+struct DNSContext *adns;
+
/*
* configuration storage
*/
usec_t cf_server_check_delay = 30 * USEC;
int cf_server_round_robin = 0;
int cf_disable_pqexec = 0;
+usec_t cf_dns_max_ttl = 15 * USEC;
char *cf_ignore_startup_params = "";
{"suspend_timeout", true, CF_TIME, &cf_suspend_timeout},
{"ignore_startup_parameters", true, CF_STR, &cf_ignore_startup_params},
{"disable_pqexec", false, CF_INT, &cf_disable_pqexec},
+{"dns_max_ttl", true, CF_TIME, &cf_dns_max_ttl},
{"pkt_buf", false, CF_INT, &cf_sbuf_len},
{"sbuf_loopcnt", true, CF_INT, &cf_sbuf_loopcnt},
event_base_free(evtmp);
}
+static void dns_setup(void)
+{
+ if (adns)
+ return;
+ adns = adns_create_context();
+ if (!adns)
+ fatal_perror("dns setup failed");
+}
+
/* boot everything */
int main(int argc, char *argv[])
{
srandom(time(NULL) ^ getpid());
if (!event_init())
fatal("event_init() failed");
+ dns_setup();
signal_setup();
janitor_setup();
stats_setup();
log_noise("sbuf_close failed, retry later");
}
+/*
+ * Connection creation utilities
+ */
+
+static void connect_server(struct PgSocket *server, struct sockaddr *sa, int salen)
+{
+ bool res;
+
+ /* fill remote_addr */
+ memset(&server->remote_addr, 0, sizeof(server->remote_addr));
+ if (sa->sa_family == AF_UNIX) {
+ server->remote_addr.port = server->pool->db->port;
+ server->remote_addr.is_unix = true;
+ } else if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *in = (struct sockaddr_in *)sa;
+ server->remote_addr.port = in->sin_port;
+ server->remote_addr.ip_addr = in->sin_addr;
+ }
+
+ /* start connecting */
+ res = sbuf_connect(&server->sbuf, sa, salen,
+ cf_server_connect_timeout / USEC);
+ if (!res)
+ log_noise("failed to launch new connection");
+}
+
+static void dns_callback(void *arg, int af, const void *addr)
+{
+ struct PgSocket *server = arg;
+ struct PgDatabase *db = server->pool->db;
+ struct sockaddr_in sa_in;
+ struct sockaddr *sa;
+ int salen;
+
+ if (af == AF_INET) {
+ char buf[64];
+ memset(&sa_in, 0, sizeof(sa_in));
+ sa_in.sin_family = af;
+ sa_in.sin_addr.s_addr = *(in_addr_t *)addr;
+ sa_in.sin_port = htons(db->port);
+ sa = (struct sockaddr *)&sa_in;
+ salen = sizeof(sa_in);
+ slog_debug(server, "dns_callback: inet4: %s",
+ sa2str(sa, buf, sizeof(buf)));
+ } else if (!af) {
+ disconnect_server(server, true, "server dns lookup failed");
+ return;
+ } else {
+ disconnect_server(server, true, "unknown dns type");
+ return;
+ }
+
+ connect_server(server, sa, salen);
+}
+
+static void dns_connect(struct PgSocket *server)
+{
+ struct sockaddr_un sa_un;
+ struct sockaddr_in sa_in;
+ struct sockaddr *sa;
+ int salen;
+ struct PgDatabase *db = server->pool->db;
+ const char *host = db->host;
+ const char *unix_dir;
+ int sa_len;
+
+ if (!host || host[0] == '/') {
+ slog_noise(server, "unix socket: %s", sa_un.sun_path);
+ memset(&sa_un, 0, sizeof(sa_un));
+ sa_un.sun_family = AF_UNIX;
+ unix_dir = host ? host : cf_unix_socket_dir;
+ snprintf(sa_un.sun_path, sizeof(sa_un.sun_path),
+ "%s/.s.PGSQL.%d", unix_dir, db->port);
+ sa = (struct sockaddr *)&sa_un;
+ sa_len = sizeof(sa_un);
+ } else if (host[0] >= '0' && host[0] <= '9') {
+ slog_noise(server, "inet socket: %s", db->host);
+ memset(&sa_in, 0, sizeof(sa_in));
+ sa_in.sin_family = AF_INET;
+ sa_in.sin_addr.s_addr = inet_addr(db->host);
+ sa_in.sin_port = htons(db->port);
+ sa = (struct sockaddr *)&sa_in;
+ sa_len = sizeof(sa_in);
+ } else {
+ slog_noise(server, "dns socket: %s", db->host);
+ /* launch dns lookup */
+ adns_resolve(adns, db->host, dns_callback, server);
+ return;
+ }
+
+ connect_server(server, sa, salen);
+}
+
/* the pool needs new connection, if possible */
void launch_new_connection(PgPool *pool)
{
PgSocket *server;
int total;
- const char *unix_dir = cf_unix_socket_dir;
- bool res;
/* allow only small number of connection attempts at a time */
if (!statlist_empty(&pool->new_server_list)) {
/* initialize it */
server->pool = pool;
server->auth_user = server->pool->user;
- server->remote_addr = server->pool->db->addr;
server->connect_time = get_cached_time();
pool->last_connect_time = get_cached_time();
change_server_state(server, SV_LOGIN);
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 */
- res = sbuf_connect(&server->sbuf, &server->remote_addr, unix_dir,
- cf_server_connect_timeout / USEC);
- if (!res)
- log_noise("failed to launch new connection");
+ dns_connect(server);
}
/* new client connection attempt */
tune_socket(sock, is_unix);
sbuf->sock = sock;
- sbuf->is_unix = is_unix;
if (!cf_reboot) {
res = sbuf_wait_for_data(sbuf);
}
/* need to connect() to get a socket */
-bool sbuf_connect(SBuf *sbuf, const PgAddr *addr, const char *unix_dir, int timeout_sec)
+bool sbuf_connect(SBuf *sbuf, const struct sockaddr *sa, int sa_len, int timeout_sec)
{
- int res, sock, domain;
- struct sockaddr_in sa_in;
- struct sockaddr_un sa_un;
- struct sockaddr *sa;
- socklen_t len;
+ int res, sock;
struct timeval timeout;
+ bool is_unix = sa->sa_family == AF_UNIX;
Assert(iobuf_empty(sbuf->io) && sbuf->sock == 0);
AssertSanity(sbuf);
- /* prepare sockaddr */
- if (addr->is_unix) {
- sa = (void*)&sa_un;
- len = sizeof(sa_un);
- memset(sa, 0, len);
- sa_un.sun_family = AF_UNIX;
- snprintf(sa_un.sun_path, sizeof(sa_un.sun_path),
- "%s/.s.PGSQL.%d", unix_dir, addr->port);
- domain = AF_UNIX;
- } else {
- sa = (void*)&sa_in;
- len = sizeof(sa_in);
- memset(sa, 0, len);
- sa_in.sin_family = AF_INET;
- sa_in.sin_addr = addr->ip_addr;
- sa_in.sin_port = htons(addr->port);
- domain = AF_INET;
- }
-
/*
* common stuff
*/
- sock = socket(domain, SOCK_STREAM, 0);
+ sock = socket(sa->sa_family, SOCK_STREAM, 0);
if (sock < 0)
/* probably fd limit */
goto failed;
- tune_socket(sock, addr->is_unix);
+ tune_socket(sock, is_unix);
- sbuf->is_unix = addr->is_unix;
sbuf->sock = sock;
timeout.tv_sec = timeout_sec;
timeout.tv_usec = 0;
/* launch connection */
- res = safe_connect(sock, sa, len);
+ res = safe_connect(sock, sa, sa_len);
if (res == 0) {
/* unix socket gives connection immidiately */
sbuf_connect_cb(sock, EV_WRITE, sbuf);