pgpool2.git / summary / log / commit / refs
commit 7bebb17816e473e29bf510b2a4de6d3449fca1c2
Author: Tatsuo Ishii <ishii@postgresql.org>
Date: Sun Aug 31 13:28:27 2014 +0000
Allow pgpool-II bind to IPv6 address.
Note that pcp still binds to IPv4 and UNIX domain socket.
Patch contributed by Michael Stapelberg.
src/auth/pool_hba.c | 4 +-
src/include/pool.h | 2 +-
src/include/utils/pool_ip.h | 2 -
src/main/pgpool_main.c | 168 +++++++++++++++++++++++++++++++++++++---
src/protocol/child.c | 61 +++++++--------
src/sample/pool_hba.conf.sample | 1 +
src/utils/pool_ip.c | 17 +---
7 files changed, 189 insertions(+), 66 deletions(-)
diff --git a/src/auth/pool_hba.c b/src/auth/pool_hba.c
index a9868e446..679285dbe 100644
--- a/src/auth/pool_hba.c
+++ b/src/auth/pool_hba.c
@@ -684,14 +684,12 @@ static void parse_hba(List *line, int line_num, POOL_CONNECTION *frontend,
* has IPv4 and the port is IPv6, promote the file address to
* IPv6 and try to match that way.
*/
-#ifdef HAVE_IPV6
if (addr.ss_family == AF_INET && frontend->raddr.addr.ss_family == AF_INET6)
{
promote_v4_to_v6_addr(&addr);
promote_v4_to_v6_mask(&mask);
}
else
-#endif /* HAVE_IPV6 */
{
/* Line doesn't match client port, so ignore it. */
return;
@@ -1285,7 +1283,7 @@ static int pam_passwd_conv_proc(int num_msg, const struct pam_message ** msg,
/*
* PAM will free this memory in * pam_end()
*/
- *resp = palloc0(num_msg, sizeof(struct pam_response));
+ *resp = palloc0(num_msg* sizeof(struct pam_response));
(*resp)[0].resp = pstrdup((char *) appdata_ptr);
(*resp)[0].resp_retcode = 0;
diff --git a/src/include/pool.h b/src/include/pool.h
index 277d33f43..73d58a03c 100644
--- a/src/include/pool.h
+++ b/src/include/pool.h
@@ -491,7 +491,7 @@ extern void pool_error(const char *fmt,...)
#else
extern void pool_error(const char *fmt,...);
#endif
-extern void do_child(int unix_fd, int inet_fd);
+extern void do_child(int *fds);
extern void pcp_do_child(int unix_fd, int inet_fd, char *pcp_conf_file);
extern int select_load_balancing_node(void);
extern int pool_init_cp(void);
diff --git a/src/include/utils/pool_ip.h b/src/include/utils/pool_ip.h
index e97d16343..e2fc4fc37 100644
--- a/src/include/utils/pool_ip.h
+++ b/src/include/utils/pool_ip.h
@@ -54,10 +54,8 @@ extern int SockAddr_cidr_mask(struct sockaddr_storage * mask,
extern const char * gai_strerror(int errcode);
#endif /* HAVE_GAI_STRERROR */
-#ifdef HAVE_IPV6
extern void promote_v4_to_v6_addr(struct sockaddr_storage * addr);
extern void promote_v4_to_v6_mask(struct sockaddr_storage * addr);
-#endif
#define IS_AF_INET(fam) ((fam) == AF_INET)
#define IS_AF_UNIX(fam) ((fam) == AF_UNIX)
diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c
index 97b6f7403..6a6be9817 100644
--- a/src/main/pgpool_main.c
+++ b/src/main/pgpool_main.c
@@ -99,10 +99,11 @@ static bool do_health_check(bool use_template_db, volatile int *health_check_nod
static void FileUnlink(int code, Datum path);
static int write_status_file();
static pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file);
-static pid_t fork_a_child(int unix_fd, int inet_fd, int id);
+static pid_t fork_a_child(int *fds, int id);
static pid_t worker_fork_a_child(void);
static int create_unix_domain_socket(struct sockaddr_un un_addr_tmp);
static int create_inet_domain_socket(const char *hostname, const int port);
+static int *create_inet_domain_sockets(const char *hostname, const int port);
static void failover(void);
static void reaper(void);
static void wakeup_children(void);
@@ -145,8 +146,7 @@ BACKEND_STATUS private_backend_status[MAX_NUM_BACKENDS];
*/
ConnectionInfo *con_info;
-static int unix_fd; /* unix domain socket fd */
-static int inet_fd; /* inet domain socket fd */
+static int *fds; /* listening file descriptors (UNIX socket, inet domain sockets) */
static int pcp_unix_fd; /* unix domain socket fd for PCP (not used) */
static int pcp_inet_fd; /* inet domain socket fd for PCP */
@@ -219,12 +219,27 @@ int PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
pool_signal(SIGPIPE, SIG_IGN);
/* create unix domain socket */
- unix_fd = create_unix_domain_socket(un_addr);
+ fds = malloc(sizeof(int));
+ fds[0] = create_unix_domain_socket(un_addr);
on_proc_exit(FileUnlink, (Datum) un_addr.sun_path);
/* create inet domain socket if any */
if (pool_config->listen_addresses[0])
- inet_fd = create_inet_domain_socket(pool_config->listen_addresses, pool_config->port);
+ {
+ int *inet_fds, *walk;
+ inet_fds = create_inet_domain_sockets(pool_config->listen_addresses, pool_config->port);
+ int n = 1;
+ for (walk = inet_fds; *walk != -1; walk++)
+ n++;
+ fds = realloc(fds, sizeof(int) * (n+1));
+ n = 1;
+ for (walk = inet_fds; *walk != -1; walk++)
+ {
+ fds[n] = inet_fds[n-1];
+ n++;
+ }
+ fds[n] = -1;
+ }
initialize_shared_mem_objects();
@@ -248,7 +263,7 @@ int PgpoolMain(bool discard_status, bool clear_memcache_oidmaps)
/* fork the children */
for (i=0;i<pool_config->num_init_children;i++)
{
- process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].pid = fork_a_child(fds, i);
process_info[i].start_time = time(NULL);
}
@@ -542,7 +557,7 @@ pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file)
/*
* fork a child
*/
-pid_t fork_a_child(int unix_fd, int inet_fd, int id)
+pid_t fork_a_child(int *fds, int id)
{
pid_t pid;
@@ -576,7 +591,7 @@ pid_t fork_a_child(int unix_fd, int inet_fd, int id)
reload_config_request = 0;
my_proc_id = id;
run_as_pcp_child = false;
- do_child(unix_fd, inet_fd);
+ do_child(fds);
}
else if (pid == -1)
{
@@ -635,6 +650,134 @@ pid_t worker_fork_a_child()
return pid;
}
+static int *create_inet_domain_sockets(const char *hostname, const int port)
+{
+ int ret;
+ int fd;
+ int one = 1;
+ int status;
+ int backlog;
+ int n = 0;
+ int *sockfds;
+ char *portstr;
+ struct addrinfo *walk;
+ struct addrinfo *res;
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ /* getaddrinfo() requires a string because it also accepts service names, such as "http". */
+ if (asprintf(&portstr, "%d", port) == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("asprintf() failed: %s", strerror(errno))));
+ }
+
+ if ((ret = getaddrinfo(hostname, portstr, &hints, &res)) != 0)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("getaddrinfo() failed: %s", gai_strerror(ret))));
+ }
+
+ free(portstr);
+
+ for (walk = res; walk != NULL; walk = walk->ai_next)
+ n++;
+
+ sockfds = malloc(sizeof(int) * (n+1));
+ n = 0;
+ for (walk = res; walk != NULL; walk = walk->ai_next)
+ sockfds[n++] = -1;
+ /* We always terminate the list of sockets with a -1 entry */
+ sockfds[n] = -1;
+
+ n = 0;
+
+ for (walk = res; walk != NULL; walk = walk->ai_next)
+ {
+ char buf[INET6_ADDRSTRLEN+1];
+ memset(buf, 0, sizeof(buf));
+ if ((ret = getnameinfo((struct sockaddr*)walk->ai_addr, walk->ai_addrlen,
+ buf, sizeof(buf), NULL, 0, NI_NUMERICHOST)) != 0)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("getnameinfo() failed: \"%s\"", gai_strerror(ret))));
+ }
+
+ ereport(LOG,
+ (errmsg("Setting up socket for %s:%d", buf, port)));
+
+ if ((fd = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol)) == -1)
+ {
+ /* A single failure is not necessarily a problem (machines without
+ * proper dual stack setups), but if we cannot create any socket at
+ * all, we report a FATAL error. */
+ ereport(LOG,
+ (errmsg("perhaps failed to create INET domain socket"),
+ errdetail("socket(%s) failed: \"%s\"", buf, strerror(errno))));
+ continue;
+ }
+
+ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
+ sizeof(one))) == -1)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("socket error \"%s\"",strerror(errno))));
+ }
+
+ if (walk->ai_family == AF_INET6)
+ {
+ /* On some machines, depending on the default value in
+ * /proc/sys/net/ipv6/bindv6only, sockets will listen on both IPv6
+ * and IPv4 at the same time. Since we are creating one socket per
+ * address family, disable that option specifically to be sure it
+ * is off. */
+ if ((setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one))) == -1) {
+ ereport(LOG,
+ (errmsg("perhaps failed to create INET domain socket"),
+ errdetail("setsockopt(%s, IPV6_V6ONLY) failed: \"%s\"", buf, strerror(errno))));
+ }
+ }
+
+ if (bind(fd, walk->ai_addr, walk->ai_addrlen) != 0)
+ {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("bind on socket failed with error \"%s\"",strerror(errno))));
+ }
+
+ backlog = pool_config->num_init_children * pool_config->listen_backlog_multiplier;
+
+ if (backlog > PGPOOLMAXLITSENQUEUELENGTH)
+ backlog = PGPOOLMAXLITSENQUEUELENGTH;
+
+ status = listen(fd, backlog);
+ if (status < 0)
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("listen on socket failed with error \"%s\"",strerror(errno))));
+
+ sockfds[n++] = fd;
+ }
+
+ freeaddrinfo(res);
+
+ if (n == 0) {
+ ereport(FATAL,
+ (errmsg("failed to create INET domain socket"),
+ errdetail("Failed to create any sockets. See the earlier LOG messages.")));
+ }
+
+ return sockfds;
+}
+
/*
* create inet domain socket
*/
@@ -991,8 +1134,9 @@ static RETSIGTYPE exit_handler(int sig)
ereport(LOG,
(errmsg("shutdown request. closing listen socket")));
- close(inet_fd);
- close(unix_fd);
+ int *walk;
+ for (walk = fds; *walk != -1; walk++)
+ close(*walk);
for (i = 0; i < pool_config->num_init_children; i++)
{
@@ -1532,7 +1676,7 @@ static void failover(void)
*/
kill(process_info[i].pid, SIGQUIT);
- process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].pid = fork_a_child(fds, i);
process_info[i].start_time = time(NULL);
}
}
@@ -1825,7 +1969,7 @@ static void reaper(void)
/* if found, fork a new child */
if (!switching && !exiting && status)
{
- process_info[i].pid = fork_a_child(unix_fd, inet_fd, i);
+ process_info[i].pid = fork_a_child(fds, i);
process_info[i].start_time = time(NULL);
ereport(DEBUG1,
(errmsg("reaper handler: forked a new child with PID:%d", process_info[i].pid)));
diff --git a/src/protocol/child.c b/src/protocol/child.c
index 666f8e1fd..154c97bbd 100644
--- a/src/protocol/child.c
+++ b/src/protocol/child.c
@@ -79,7 +79,7 @@ static bool connect_using_existing_connection(POOL_CONNECTION *frontend,
static void check_restart_request(void);
static void enable_authentication_timeout(void);
static void disable_authentication_timeout(void);
-static int wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, SockAddr *saddr);
+static int wait_for_new_connections(int *fds, struct timeval *timeout, SockAddr *saddr);
static void check_config_reload(void);
static void get_backends_status(unsigned int *valid_backends, unsigned int *down_backends);
static void validate_backend_connectivity(int front_end_fd);
@@ -119,7 +119,7 @@ char remote_port[NI_MAXSERV]; /* client port */
/*
* child main loop
*/
-void do_child(int unix_fd, int inet_fd)
+void do_child(int *fds)
{
sigjmp_buf local_sigjmp_buf;
@@ -148,23 +148,21 @@ void do_child(int unix_fd, int inet_fd)
signal(SIGUSR2, wakeup_handler);
signal(SIGPIPE, SIG_IGN);
+ int *walk;
#ifdef NONE_BLOCK
/* set listen fds to none-blocking */
- pool_set_nonblock(unix_fd);
- if (inet_fd)
+ for (walk = fds; *walk != -1; walk++)
+ pool_set_nonblock(*walk);
+#endif
+ for (walk = fds; *walk != -1; walk++)
{
- pool_set_nonblock(inet_fd);
+ if (*walk > nsocks)
+ nsocks = *walk;
}
-#endif
- nsocks =Max(unix_fd, inet_fd)+1;
+ nsocks++;
FD_ZERO(&readmask);
- FD_SET(unix_fd, &readmask);
- if (inet_fd)
- {
- FD_SET(inet_fd, &readmask);
- child_inet_fd = inet_fd;
- }
- child_unix_fd = unix_fd;
+ for (walk = fds; *walk != -1; walk++)
+ FD_SET(*walk, &readmask);
/* Create per loop iteration memory context */
ProcessLoopContext = AllocSetContextCreate(TopMemoryContext,
@@ -286,7 +284,7 @@ void do_child(int unix_fd, int inet_fd)
/* Destroy session context for just in case... */
pool_session_context_destroy();
- front_end_fd = wait_for_new_connections(unix_fd, inet_fd, &timeout, &saddr);
+ front_end_fd = wait_for_new_connections(fds, &timeout, &saddr);
if(front_end_fd == OPERATION_TIMEOUT)
{
if(pool_config->child_life_time > 0 && connected)
@@ -1821,14 +1819,15 @@ static void check_restart_request(void)
*/
static int
-wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, SockAddr *saddr)
+wait_for_new_connections(int *fds, struct timeval *timeout, SockAddr *saddr)
{
fd_set rmask;
- int fds;
+ int numfds;
int save_errno;
int fd = 0;
int afd;
+ int *walk;
#ifdef ACCEPT_PERFORMANCE
struct timeval now1, now2;
@@ -1839,11 +1838,8 @@ wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, Sock
struct timeval *timeoutval;
struct timeval tv1, tv2, tmback = {0, 0};
- pool_set_nonblock(unix_fd);
- if (inet_fd)
- {
- pool_set_nonblock(inet_fd);
- }
+ for (walk = fds; *walk != -1; walk++)
+ pool_set_nonblock(*walk);
set_ps_display("wait for connection request", false);
@@ -1866,7 +1862,7 @@ wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, Sock
#endif
}
- fds = select(nsocks, &rmask, NULL, NULL, timeoutval);
+ numfds = select(nsocks, &rmask, NULL, NULL, timeoutval);
save_errno = errno;
/* check backend timer is expired */
@@ -1912,7 +1908,7 @@ wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, Sock
errno = save_errno;
- if (fds == -1)
+ if (numfds == -1)
{
if (errno == EAGAIN || errno == EINTR)
return RETRY;
@@ -1922,19 +1918,18 @@ wait_for_new_connections(int unix_fd, int inet_fd, struct timeval *timeout, Sock
}
/* timeout */
- if (fds == 0)
+ if (numfds == 0)
{
return OPERATION_TIMEOUT;
}
- /* check unix domain socket */
- if (FD_ISSET(unix_fd, &rmask))
- {
- fd = unix_fd;
- }
- /* check inet socket */
- if (FD_ISSET(inet_fd, &rmask))
+
+ for (walk = fds; *walk != -1; walk++)
{
- fd = inet_fd;
+ if (FD_ISSET(*walk, &rmask))
+ {
+ fd = *walk;
+ break;
+ }
}
/*
* Note that some SysV systems do not work here. For those
diff --git a/src/sample/pool_hba.conf.sample b/src/sample/pool_hba.conf.sample
index 6bd34636e..6e37e2736 100644
--- a/src/sample/pool_hba.conf.sample
+++ b/src/sample/pool_hba.conf.sample
@@ -65,3 +65,4 @@
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
+host all all ::1/128 trust
diff --git a/src/utils/pool_ip.c b/src/utils/pool_ip.c
index b83824223..cc9b7ec7e 100644
--- a/src/utils/pool_ip.c
+++ b/src/utils/pool_ip.c
@@ -56,11 +56,9 @@ static int rangeSockAddrAF_INET(const struct sockaddr_in * addr,
const struct sockaddr_in * netaddr,
const struct sockaddr_in * netmask);
-#ifdef HAVE_IPV6
static int rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
const struct sockaddr_in6 * netaddr,
const struct sockaddr_in6 * netmask);
-#endif
static int getaddrinfo_unix(const char *path,
const struct addrinfo * hintsp,
@@ -381,12 +379,10 @@ rangeSockAddr(const struct sockaddr_storage * addr,
return rangeSockAddrAF_INET((struct sockaddr_in *) addr,
(struct sockaddr_in *) netaddr,
(struct sockaddr_in *) netmask);
-#ifdef HAVE_IPV6
else if (addr->ss_family == AF_INET6)
return rangeSockAddrAF_INET6((struct sockaddr_in6 *) addr,
(struct sockaddr_in6 *) netaddr,
(struct sockaddr_in6 *) netmask);
-#endif
else
return 0;
}
@@ -404,7 +400,6 @@ rangeSockAddrAF_INET(const struct sockaddr_in * addr,
}
-#ifdef HAVE_IPV6
static int
rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
const struct sockaddr_in6 * netaddr,
@@ -421,7 +416,6 @@ rangeSockAddrAF_INET6(const struct sockaddr_in6 * addr,
return 1;
}
-#endif
/*
* SockAddr_cidr_mask - make a network mask of the appropriate family
@@ -463,7 +457,6 @@ SockAddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
break;
}
-#ifdef HAVE_IPV6
case AF_INET6:
{
struct sockaddr_in6 mask6;
@@ -487,7 +480,6 @@ SockAddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
memcpy(mask, &mask6, sizeof(mask6));
break;
}
-#endif
default:
return -1;
}
@@ -497,8 +489,6 @@ SockAddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
}
-#ifdef HAVE_IPV6
-
/*
* promote_v4_to_v6_addr --- convert an AF_INET addr to AF_INET6, using
* the standard convention for IPv4 addresses mapped into IPv6 world
@@ -507,7 +497,7 @@ SockAddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
* hold the result! Note that we only worry about setting the fields
* that rangeSockAddr will look at.
*/
-static void
+void
promote_v4_to_v6_addr(struct sockaddr_storage * addr)
{
struct sockaddr_in addr4;
@@ -542,7 +532,7 @@ promote_v4_to_v6_addr(struct sockaddr_storage * addr)
* hold the result! Note that we only worry about setting the fields
* that rangeSockAddr will look at.
*/
-static void
+void
promote_v4_to_v6_mask(struct sockaddr_storage * addr)
{
struct sockaddr_in addr4;
@@ -567,6 +557,3 @@ promote_v4_to_v6_mask(struct sockaddr_storage * addr)
memcpy(addr, &addr6, sizeof(addr6));
}
-
-#endif /* HAVE_IPV6 */
-
[parent: ca2b7d3d9c14]