<listitem>
<para>
<!--
- Specifies the hostname or IP address, on which PCP process
- will accept TCP/IP connections. <literal>*</literal>
- accepts all incoming connections. <literal>''</literal>
- disables TCP/IP connections. Default
- is <literal>localhost</literal>. Connections via UNIX domain
- socket are always accepted.
- -->
- PCPプロセスがTCP/IP接続を受け付けるホスト名またはIPアドレスを指定します。
- <literal>*</literal>を指定すると入ってくる全ての接続を受け付けます。
- <literal>''</literal>を指定するとTCP/IP接続を受け付けません。
- デフォルト値は<literal>localhost</literal>です。
- UNIXドメインソケット経由のコネクションは常に受け付けます。
+ Specifies the TCP/IP address(es) on which the pcp server is
+ to listen for connections from client applications.
+ The value takes the form of a comma-separated list of host names
+ and/or numeric IP addresses. The special entry <literal>*</literal>
+ corresponds to all available IP interfaces. The entry
+ <literal>0.0.0.0</literal> allows listening for all IPv4 addresses and
+ <literal>::</literal> allows listening for all IPv6 addresses.
+ If the list is empty, the server does not listen on any IP interface
+ at all, in which case only Unix-domain sockets can be used to connect
+ to it.
+ The default value
+ is <systemitem class="systemname">localhost</systemitem>, which
+ allows only local TCP/IP <quote>loopback</quote> connections to
+ be made. While client authentication
+ (<xref linkend="auth-pool-hba-conf">) allows fine-grained
+ control over who can access the
+ server, <varname>pcp_listen_addresses</varname> controls which
+ interfaces accept connection attempts, which can help prevent
+ repeated malicious connection requests on insecure network
+ interfaces. This parameter can only be set at server start.
+ -->
+ クライアントアプリケーションからの接続をPCPサーバが監視する TCP/IP アドレスを指定します。
+ この値は、ホスト名をコンマで区切ったリスト、そして/もしくは、数値によるIPアドレスです。
+ <literal>*</literal>という特別なエントリは利用可能な全てのIPインタフェースに対応します。
+ エントリ<literal>0.0.0.0</literal>は全てのIPv4アドレスの監視を、そしてエントリ<literal>::</literal>は全てのIPv6アドレスの監視を許容します。
+ リストが空の場合、サーバはいかなるIPインタフェースも全く監視しないで、Unixドメインソケットのみを使用して接続が行われます。
+ デフォルトの値は<systemitem class="systemname">localhost</systemitem>で、ローカルなTCP/IP <quote>loopback</quote>接続のみ許可します。
+ クライアント認証(<xref linkend="auth-pool-hba-conf">)は誰がサーバにアクセス可能かをきめ細かく制御するのに対し、<varname>pcp_listen_addresses</varname>はどのインタフェースが接続を試みるかを制御します。
+ これにより、安全でないネットワークインタフェース上において繰り返して行われる悪意のある接続要求の防止に役立ちます。
</para>
<para>
<!--
</term>
<listitem>
<para>
- Specifies the hostname or IP address, on which pcp process
- will accept TCP/IP connections. <literal>*</literal>
- accepts all incoming connections. <literal>"" </literal>
- disables TCP/IP connections. Default
- is <literal>localhost</literal>. Connections via UNIX domain
- socket are always accepted.
- </para>
- <para>
- This parameter can only be set at server start.
+ Specifies the TCP/IP address(es) on which the pcp server is
+ to listen for connections from client applications.
+ The value takes the form of a comma-separated list of host names
+ and/or numeric IP addresses. The special entry <literal>*</literal>
+ corresponds to all available IP interfaces. The entry
+ <literal>0.0.0.0</literal> allows listening for all IPv4 addresses and
+ <literal>::</literal> allows listening for all IPv6 addresses.
+ If the list is empty, the server does not listen on any IP interface
+ at all, in which case only Unix-domain sockets can be used to connect
+ to it.
+ The default value
+ is <systemitem class="systemname">localhost</systemitem>, which
+ allows only local TCP/IP <quote>loopback</quote> connections to
+ be made. While client authentication
+ (<xref linkend="auth-pool-hba-conf">) allows fine-grained
+ control over who can access the
+ server, <varname>pcp_listen_addresses</varname> controls which
+ interfaces accept connection attempts, which can help prevent
+ repeated malicious connection requests on insecure network
+ interfaces. This parameter can only be set at server start.
</para>
</listitem>
</varlistentry>
#define default_reset_query_list "ABORT;DISCARD ALL"
#define default_listen_addresses_list "localhost"
+#define default_pcp_listen_addresses_list "localhost"
#define default_read_only_function_list ""
#define default_write_function_list ""
MakeDMLAdaptiveObjectRelationList, NULL
},
- {
- {"pcp_listen_addresses", CFGCXT_INIT, CONNECTION_CONFIG,
- "hostname(s) or IP address(es) on which pcp will listen on.",
- CONFIG_VAR_TYPE_STRING, false, 0
- },
- &g_pool_config.pcp_listen_addresses,
- "localhost",
- NULL, NULL, NULL, NULL
- },
-
{
{"socket_dir", CFGCXT_INIT, CONNECTION_CONFIG,
"The directory to create the UNIX domain socket for accepting pgpool-II client connections.",
NULL, NULL, NULL
},
+ {
+ {"pcp_listen_addresses", CFGCXT_INIT, CONNECTION_CONFIG,
+ "hostname(s) or IP address(es) on which pcp will listen on.",
+ CONFIG_VAR_TYPE_STRING_LIST, false, 0
+ },
+ &g_pool_config.pcp_listen_addresses,
+ &g_pool_config.num_pcp_listen_addresses,
+ (const char *) default_pcp_listen_addresses_list,
+ ",",
+ false,
+ NULL, NULL, NULL
+ },
+
{
{"read_only_function_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG,
"list of functions that does not writes to database.",
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
*
- * Copyright (c) 2003-2020 PgPool Global Development Group
+ * Copyright (c) 2003-2022 PgPool Global Development Group
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
/*pcp_child.c*/
-extern void pcp_main(int unix_fd, int inet_fd);
+extern void pcp_main(int *fds);
ClusteringModes backend_clustering_mode; /* Backend clustering mode */
char **listen_addresses; /* hostnames/IP addresses to listen on */
int port; /* port # to bind */
- char *pcp_listen_addresses; /* PCP listen address to listen on */
+ char **pcp_listen_addresses; /* PCP listen address to listen on */
int pcp_port; /* PCP port # to bind */
char *socket_dir; /* pgpool socket directory */
char *wd_ipc_socket_dir; /* watchdog command IPC socket directory */
/* followings till syslog, does not exist in the configuration file */
int num_reset_queries; /* number of queries in reset_query_list */
int num_listen_addresses; /* number of entries in listen_addresses */
+ int num_pcp_listen_addresses; /* number of entries in pcp_listen_addresses */
int num_read_only_function_list; /* number of functions in
* read_only_function_list */
int num_write_function_list; /* number of functions in
static void signal_user1_to_parent_with_reason(User1SignalReason reason);
static void FileUnlink(int code, Datum path);
-static pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file);
+static pid_t pcp_fork_a_child(int *fds, char *pcp_conf_file);
static pid_t fork_a_child(int *fds, int id);
static pid_t worker_fork_a_child(ProcessType type, void (*func) (), void *params);
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 int *create_inet_domain_sockets_by_list(char **listen_addresses, int n_listen_addresses, int port, int *n_sockets);
static void failover(void);
static int *fds = NULL; /* 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 */
+static int *pcp_fds = NULL; /* listening file descriptors for pcp (UNIX socket,
+ * inet domain sockets) */
+
extern char *pcp_conf_file; /* path for pcp.conf */
extern char *conf_file;
extern char *hba_file;
{
int num_fds = 0;
int *inet_fds;
+ int *pcp_inet_fds;
int i;
sigjmp_buf local_sigjmp_buf;
Req_info->primary_node_id = find_primary_node_repeatedly();
}
- /* fork a child for PCP handling */
- pcp_unix_fd = create_unix_domain_socket(pcp_un_addr);
- /* Add onproc exit to clean up the unix domain socket at exit */
+ /* create pcp unix domain socket */
+ num_fds = 0;
+ pcp_fds = malloc(sizeof(int) * (num_fds + 2));
+ if (pcp_fds == NULL)
+ ereport(FATAL,
+ (errmsg("failed to allocate memory in startup process")));
+
+ pcp_fds[0] = create_unix_domain_socket(pcp_un_addr);
+ fds[1] = -1;
on_proc_exit(FileUnlink, (Datum) pcp_un_addr.sun_path);
- if (pool_config->pcp_listen_addresses[0])
+ /* create inet domain socket if any */
+ pcp_inet_fds = create_inet_domain_sockets_by_list(pool_config->pcp_listen_addresses, pool_config->num_pcp_listen_addresses,
+ pool_config->pcp_port, &num_fds);
+
+ if (num_fds > 0)
{
- pcp_inet_fd = create_inet_domain_socket(pool_config->pcp_listen_addresses, pool_config->pcp_port);
+ memcpy(&pcp_fds[1], pcp_inet_fds, sizeof(int) * num_fds);
+ pcp_fds[num_fds + 1] = -1;
+ free(pcp_inet_fds);
}
- pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+
+ pcp_pid = pcp_fork_a_child(pcp_fds, pcp_conf_file);
/* Fork worker process */
worker_pid = worker_fork_a_child(PT_WORKER, do_worker_child, NULL);
* fork a child for PCP
*/
static pid_t
-pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file)
+pcp_fork_a_child(int *fds, char *pcp_conf_file)
{
pid_t pid;
POOL_SETMASK(&UnBlockSig);
health_check_timer_expired = 0;
reload_config_request = 0;
- pcp_main(unix_fd, inet_fd);
+ pcp_main(fds);
}
else if (pid == -1)
{
return sockfds;
}
-/*
-* create inet domain socket
-*/
-static int
-create_inet_domain_socket(const char *hostname, const int port)
-{
- struct sockaddr_in addr;
- int fd;
- int status;
- int one = 1;
- int len;
- int backlog;
-
- fd = socket(AF_INET, SOCK_STREAM, 0);
- if (fd == -1)
- {
- ereport(FATAL,
- (errmsg("failed to create INET domain socket"),
- errdetail("%m")));
- }
- if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
- sizeof(one))) == -1)
- {
- ereport(FATAL,
- (errmsg("failed to create INET domain socket"),
- errdetail("%m")));
- }
-
- memset((char *) &addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
-
- if (strcmp(hostname, "*") == 0)
- {
- addr.sin_addr.s_addr = htonl(INADDR_ANY);
- }
- else
- {
- struct hostent *hostinfo;
-
- hostinfo = gethostbyname(hostname);
- if (!hostinfo)
- {
- ereport(FATAL,
- (errmsg("failed to create INET domain socket"),
- errdetail("could not resolve hostname \"%s\": error \"%s\"", hostname, hstrerror(h_errno))));
-
- }
- addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
- }
-
- addr.sin_port = htons(port);
- len = sizeof(struct sockaddr_in);
-
- status = bind(fd, (struct sockaddr *) &addr, len);
- if (status == -1)
- {
- int saved_errno = errno;
- char host[NI_MAXHOST],
- servname[NI_MAXSERV];
-
- if ((status = getnameinfo((struct sockaddr *) &addr, len, host, sizeof(host), servname, sizeof(servname), 0)))
- {
- ereport(NOTICE,
- (errmsg("getnameinfo failed while creating INET domain socket"),
- errdetail("getnameinfo failed with reason: \"%s\"", gai_strerror(status))));
-
- snprintf(servname, sizeof(servname), "%d", port);
- snprintf(host, sizeof(host), "%s", hostname);
- }
- ereport(FATAL,
- (errmsg("failed to create INET domain socket"),
- errdetail("bind on host:\"%s\" server:\"%s\" failed with error \"%s\"", host, servname, strerror(saved_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 \"%m\"")));
-
- return fd;
-}
-
/*
* create UNIX domain socket
*/
found = true;
if (restart_child)
{
- pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+ pcp_pid = pcp_fork_a_child(pcp_fds, pcp_conf_file);
new_pid = pcp_pid;
}
else
ereport(LOG,
(errmsg("PCP child %d exits with status %d in failover()", pcp_pid, status)));
- pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file);
+ pcp_pid = pcp_fork_a_child(pcp_fds, pcp_conf_file);
ereport(LOG,
(errmsg("fork a new PCP child pid %d in failover()", pcp_pid)));
}
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
*
- * Copyright (c) 2003-2021 PgPool Global Development Group
+ * Copyright (c) 2003-2022 PgPool Global Development Group
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
static RETSIGTYPE restart_handler(int sig);
static RETSIGTYPE reap_handler(int sig);
-static int pcp_do_accept(int unix_fd, int inet_fd);
-static void start_pcp_command_processor_process(int port);
+static int pcp_do_accept(int *fds);
+static void start_pcp_command_processor_process(int port, int *fds);
static void pcp_child_will_die(int code, Datum arg);
static void pcp_kill_all_children(int sig);
static void reaper(void);
* main entry point for pcp child process
*/
void
-pcp_main(int unix_fd, int inet_fd)
+pcp_main(int *fds)
{
sigjmp_buf local_sigjmp_buf;
struct timeval uptime;
init_ps_display("", "", "", "");
gettimeofday(&uptime, NULL);
- pcp_unix_fd = unix_fd;
- pcp_inet_fd = inet_fd;
pcp_recovery_in_progress = pool_shared_memory_create(sizeof(bool));
*pcp_recovery_in_progress = false;
errno = 0;
CHECK_RESTART_REQUEST;
- port = pcp_do_accept(unix_fd, inet_fd);
+ port = pcp_do_accept(fds);
if (port > 0)
{
- start_pcp_command_processor_process(port);
+ start_pcp_command_processor_process(port, fds);
}
}
}
+/*
+ * Accept connection to pcp port
+ */
static int
-pcp_do_accept(int unix_fd, int inet_fd)
+pcp_do_accept(int *fds)
{
fd_set readmask;
- int fds;
- struct sockaddr addr;
- socklen_t addrlen;
+ fd_set rmask;
+ int rfds;
int fd = 0;
int afd;
- int inet = 0;
+ int *walk;
+ int nsocks = 0;
+ SockAddr saddr;
set_ps_display("PCP: wait for connection request", false);
+ for (walk = fds; *walk != -1; walk++)
+ {
+ if (*walk > nsocks)
+ nsocks = *walk;
+ }
+ nsocks++;
FD_ZERO(&readmask);
- FD_SET(unix_fd, &readmask);
- if (inet_fd)
- FD_SET(inet_fd, &readmask);
+ for (walk = fds; *walk != -1; walk++)
+ FD_SET(*walk, &readmask);
- fds = select(Max(unix_fd, inet_fd) + 1, &readmask, NULL, NULL, NULL);
- if (fds == -1)
+ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set));
+ rfds = select(nsocks, &rmask, NULL, NULL, NULL);
+ if (rfds == -1)
{
if (errno == EAGAIN || errno == EINTR)
return -1;
(errmsg("unable to accept new pcp connection"),
errdetail("select system call failed with error : \"%m\"")));
}
- if (FD_ISSET(unix_fd, &readmask))
+ for (walk = fds; *walk != -1; walk++)
{
- fd = unix_fd;
- }
- if (FD_ISSET(inet_fd, &readmask))
- {
- fd = inet_fd;
- inet ++;
+ if (FD_ISSET(*walk, &rmask))
+ {
+ fd = *walk;
+ break;
+ }
}
- addrlen = sizeof(addr);
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.salen = sizeof(saddr.addr);
- afd = accept(fd, &addr, &addrlen);
+ afd = accept(fd, (struct sockaddr *) &saddr.addr, &saddr.salen);
if (afd < 0)
{
/*
}
ereport(DEBUG2,
(errmsg("I am PCP child with PID:%d and accept fd:%d", getpid(), afd)));
- if (inet)
+
+ /*
+ * Set no delay if AF_INET socket. Not sure if this is really necessary
+ * but PostgreSQL does this.
+ */
+ if (!FD_ISSET(fds[0], &rmask)) /* fds[0] is UNIX domain socket */
{
- int on = 1;
+ int on;
+ on = 1;
if (setsockopt(afd, IPPROTO_TCP, TCP_NODELAY,
(char *) &on,
sizeof(on)) < 0)
{
+ ereport(WARNING,
+ (errmsg("wait_for_new_connections: setsockopt failed with error \"%m\"")));
close(afd);
- ereport(ERROR,
- (errmsg("unable to accept new pcp connection"),
- errdetail("setsockopt system call failed with error : \"%m\"")));
- }
- if (setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE,
- (char *) &on,
- sizeof(on)) < 0)
- {
- close(afd);
- ereport(ERROR,
- (errmsg("unable to accept new pcp connection"),
- errdetail("setsockopt system call failed with error : \"%m\"")));
+ return -1;
}
}
+
return afd;
}
* forks a new pcp worker child
*/
static void
-start_pcp_command_processor_process(int port)
+start_pcp_command_processor_process(int port, int *fds)
{
pid_t pid = fork();
+ int *walk;
if (pid == 0) /* child */
{
SetProcessGlobalVariables(PT_PCP_WORKER);
on_exit_reset();
+
/* Close the listen sockets sockets */
- close(pcp_unix_fd);
- close(pcp_inet_fd);
+ for (walk = fds; *walk != -1; walk++)
+ close(*walk);
+
/* call PCP child main */
if (pcp_worker_children)
list_free(pcp_worker_children);
i++;
StrNCpy(status[i].name, "pcp_listen_addresses", POOLCONFIG_MAXNAMELEN);
- snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->pcp_listen_addresses);
+ *(status[i].value) = '\0';
+ for (j = 0; j < pool_config->num_pcp_listen_addresses; j++)
+ {
+ len = POOLCONFIG_MAXVALLEN - strlen(status[i].value);
+ strncat(status[i].value, pool_config->pcp_listen_addresses[j], len);
+ len = POOLCONFIG_MAXVALLEN - strlen(status[i].value);
+ if (j != pool_config->num_pcp_listen_addresses - 1)
+ strncat(status[i].value, ",", len);
+ }
StrNCpy(status[i].desc, "host name(s) or IP address(es) for pcp process to listen on", POOLCONFIG_MAXDESCLEN);
i++;
+
StrNCpy(status[i].name, "pcp_port", POOLCONFIG_MAXNAMELEN);
snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->pcp_port);
StrNCpy(status[i].desc, "PCP port # to bind", POOLCONFIG_MAXDESCLEN);