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]