diff --git a/src/config/pool_config_variables.c b/src/config/pool_config_variables.c index 90446bd7..fe1bfd71 100644 --- a/src/config/pool_config_variables.c +++ b/src/config/pool_config_variables.c @@ -51,6 +51,7 @@ #define default_reset_query_list "ABORT;DISCARD ALL" #define default_listen_addresses_list "localhost" #define default_pcp_listen_addresses_list "localhost" +#define default_unix_socket_directories_list "/tmp" #define default_read_only_function_list "" #define default_write_function_list "" @@ -107,6 +108,8 @@ static char **get_list_from_string_regex_delim(const char *str, const char *deli /*show functions */ static const char *IntValueShowFunc(int value); +static const char *FilePermissionShowFunc(int value); +static const char *UnixSockPermissionsShowFunc(void); static const char *HBDestinationPortShowFunc(int index); static const char *HBDeviceShowFunc(int index); static const char *HBHostnameShowFunc(int index); @@ -815,12 +818,12 @@ static struct config_string ConfigureNamesString[] = }, { - {"socket_dir", CFGCXT_INIT, CONNECTION_CONFIG, - "The directory to create the UNIX domain socket for accepting pgpool-II client connections.", + {"unix_socket_group", CFGCXT_INIT, CONNECTION_CONFIG, + "The owning user of the sockets that always starts the server.", CONFIG_VAR_TYPE_STRING, false, 0 }, - &g_pool_config.socket_dir, - DEFAULT_SOCKET_DIR, + &g_pool_config.unix_socket_group, + "", NULL, NULL, NULL, NULL }, @@ -1353,6 +1356,19 @@ static struct config_string_list ConfigureNamesStringList[] = NULL, NULL, NULL }, + { + {"unix_socket_directories", CFGCXT_INIT, CONNECTION_CONFIG, + "The directories to create the UNIX domain sockets for accepting pgpool-II client connections.", + CONFIG_VAR_TYPE_STRING_LIST, false, 0 + }, + &g_pool_config.unix_socket_directories, + &g_pool_config.num_unix_socket_directories, + (const char *) default_unix_socket_directories_list, + ",", + false, + NULL, NULL, NULL + }, + { {"read_only_function_list", CFGCXT_RELOAD, CONNECTION_POOL_CONFIG, "list of functions that does not writes to database.", @@ -1857,6 +1873,17 @@ static struct config_int ConfigureNamesInt[] = NULL, NULL, NULL }, + { + {"unix_socket_permissions", CFGCXT_INIT, CONNECTION_CONFIG, + "The access permissions of the Unix domain sockets.", + CONFIG_VAR_TYPE_INT, false, 0 + }, + &g_pool_config.unix_socket_permissions, + 0777, + 0000, 0777, + NULL, NULL, UnixSockPermissionsShowFunc + }, + { {"num_init_children", CFGCXT_INIT, CONNECTION_POOL_CONFIG, "Number of children pre-forked for client connections.", @@ -4320,6 +4347,21 @@ IntValueShowFunc(int value) return buffer; } +static const char * +FilePermissionShowFunc(int value) +{ + static char buffer[10]; + + snprintf(buffer, sizeof(buffer), "%04o", value); + return buffer; +} + +static const char * +UnixSockPermissionsShowFunc(void) +{ + return FilePermissionShowFunc(g_pool_config.unix_socket_permissions); +} + static const char * BackendWeightShowFunc(int index) { diff --git a/src/include/pool_config.h b/src/include/pool_config.h index 13b3aa3d..b6967cf2 100644 --- a/src/include/pool_config.h +++ b/src/include/pool_config.h @@ -207,7 +207,9 @@ typedef struct int port; /* port # to bind */ char **pcp_listen_addresses; /* PCP listen address to listen on */ int pcp_port; /* PCP port # to bind */ - char *socket_dir; /* pgpool socket directory */ + char **unix_socket_directories; /* pgpool socket directories */ + char *unix_socket_group; /* owner group of pgpool sockets */ + int unix_socket_permissions; /* pgpool sockets permissions */ char *wd_ipc_socket_dir; /* watchdog command IPC socket directory */ char *pcp_socket_dir; /* PCP socket directory */ int num_init_children; /* # of children initially pre-forked */ @@ -393,6 +395,7 @@ typedef struct 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_unix_socket_directories; /* number of entries in unix_socket_directories */ int num_read_only_function_list; /* number of functions in * read_only_function_list */ int num_write_function_list; /* number of functions in diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c index 88ec3c87..fc551b66 100644 --- a/src/main/pgpool_main.c +++ b/src/main/pgpool_main.c @@ -60,7 +60,7 @@ #include "watchdog/wd_lifecheck.h" #include "watchdog/watchdog.h" #include "pcp/pcp_worker.h" - +#include /* * Reasons for signalling a pgpool-II main process @@ -147,7 +147,8 @@ static void FileUnlink(int code, Datum path); 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_unix_domain_socket(struct sockaddr_un un_addr_tmp, const char *group, const int permissions); +static int *create_unix_domain_sockets_by_list(struct sockaddr_un *un_addrs, char *group, int permissions, int n_sockets); 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); @@ -195,7 +196,7 @@ static void exec_notice_pcp_child(FAILOVER_CONTEXT *failover_context); static void check_requests(void); static void print_signal_member(sigset_t *sig); -static struct sockaddr_un un_addr; /* unix domain socket path */ +static struct sockaddr_un *un_addrs; /* unix domain socket path */ static struct sockaddr_un pcp_un_addr; /* unix domain socket path for PCP */ ProcessInfo *process_info = NULL; /* Per child info table on shmem */ volatile User1SignalSlot *user1SignalSlot = NULL; /* User 1 signal slot on @@ -304,9 +305,17 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) on_system_exit(system_will_go_down, (Datum) NULL); /* set unix domain socket path for connections to pgpool */ - snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/.s.PGSQL.%d", - pool_config->socket_dir, - pool_config->port); + un_addrs = malloc(sizeof(struct sockaddr_un) * pool_config->num_unix_socket_directories); + if (un_addrs == NULL) + ereport(FATAL, + (errmsg("failed to allocate memory in startup process"))); + + for (i = 0; i < pool_config->num_unix_socket_directories; i++) + { + snprintf(un_addrs[i].sun_path, sizeof(un_addrs[i].sun_path), "%s/.s.PGSQL.%d", + pool_config->unix_socket_directories[i], + pool_config->port); + } /* set unix domain socket path for pgpool PCP communication */ snprintf(pcp_un_addr.sun_path, sizeof(pcp_un_addr.sun_path), "%s/.s.PGSQL.%d", pool_config->pcp_socket_dir, @@ -406,14 +415,20 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) } /* create unix domain socket */ - fds = malloc(sizeof(int) * (num_fds + 2)); + fds = malloc(sizeof(int) * (pool_config->num_unix_socket_directories + 1)); if (fds == NULL) ereport(FATAL, (errmsg("failed to allocate memory in startup process"))); - fds[0] = create_unix_domain_socket(un_addr); - fds[1] = -1; - on_proc_exit(FileUnlink, (Datum) un_addr.sun_path); + fds = create_unix_domain_sockets_by_list(un_addrs, + pool_config->unix_socket_group, + pool_config->unix_socket_permissions, + pool_config->num_unix_socket_directories); + fds[pool_config->num_unix_socket_directories] = -1; + for (i = 0; i < pool_config->num_unix_socket_directories; i++) + { + on_proc_exit(FileUnlink, (Datum) un_addrs[i].sun_path); + } /* create inet domain socket if any */ inet_fds = create_inet_domain_sockets_by_list(pool_config->listen_addresses, pool_config->num_listen_addresses, @@ -422,8 +437,8 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) /* copy inet domain sockets if any */ if (num_fds > 0) { - memcpy(&fds[1], inet_fds, sizeof(int) * num_fds); - fds[num_fds + 1] = -1; + memcpy(&fds[pool_config->num_unix_socket_directories], inet_fds, sizeof(int) * num_fds); + fds[pool_config->num_unix_socket_directories + num_fds] = -1; free(inet_fds); } @@ -470,7 +485,7 @@ PgpoolMain(bool discard_status, bool clear_memcache_oidmaps) ereport(FATAL, (errmsg("failed to allocate memory in startup process"))); - pcp_fds[0] = create_unix_domain_socket(pcp_un_addr); + pcp_fds[0] = create_unix_domain_socket(pcp_un_addr, "", 0777); pcp_fds[1] = -1; on_proc_exit(FileUnlink, (Datum) pcp_un_addr.sun_path); @@ -951,7 +966,7 @@ create_inet_domain_sockets(const char *hostname, const int port) * create UNIX domain socket */ static int -create_unix_domain_socket(struct sockaddr_un un_addr_tmp) +create_unix_domain_socket(struct sockaddr_un un_addr_tmp, const char *group, const int permissions) { struct sockaddr_un addr; int fd; @@ -980,7 +995,39 @@ create_unix_domain_socket(struct sockaddr_un un_addr_tmp) errdetail("bind socket failed with error: \"%m\""))); } - if (chmod(un_addr_tmp.sun_path, 0777) == -1) + if (*group != '\0') + { + char *endptr; + gid_t gid; + unsigned long val; + + /* check group*/ + val = strtoul(group, &endptr, 10); + if (*endptr == '\0') + { + gid = val; + } + else + { + struct group *gr; + gr = getgrnam(group); + if(!gr) + { + ereport(FATAL, + (errmsg("unix_socket_group \"%s\" does not exist", group))); + } + gid = gr->gr_gid; + } + + if (chown(un_addr_tmp.sun_path, -1, gid) == -1) + { + ereport(FATAL, + (errmsg("failed to bind a socket: \"%s\"", un_addr_tmp.sun_path), + errdetail("system call chown failed with error: \"%m\""))); + } + } + + if (chmod(un_addr_tmp.sun_path, permissions) == -1) { ereport(FATAL, (errmsg("failed to bind a socket: \"%s\"", un_addr_tmp.sun_path), @@ -4621,6 +4668,38 @@ exec_notice_pcp_child(FAILOVER_CONTEXT *failover_context) */ +/* + * Create UNIX domain sockets by unix_socket_directories, which is an array of + string. The number of elements is + * "n_listen_addresses". "port" is the port number. A socket array is returned. + * The number of elements in the socket array is "n_sockets". + */ +static int * +create_unix_domain_sockets_by_list(struct sockaddr_un *un_addrs, + char *group, int permissions, int n_sockets) +{ + int i; + int *sockets = NULL; + + if (un_addrs == NULL) + return NULL; + + sockets = malloc(sizeof(int) * n_sockets); + if (sockets == NULL) + ereport(FATAL, + (errmsg("failed to allocate memory in startup process"))); + + for (i = 0; i < n_sockets; i++) + { + ereport(LOG, + (errmsg("unix_socket_directories[%d]: %s", i, un_addrs[i].sun_path))); + + sockets[i] = create_unix_domain_socket(un_addrs[i], group, permissions); + } + + return sockets; +} + /* * Create INET domain sockets by specified listen address list * "listen_addresses", which is an array of string. The number of elements is diff --git a/src/protocol/child.c b/src/protocol/child.c index 962f1e4c..9d1126d0 100644 --- a/src/protocol/child.c +++ b/src/protocol/child.c @@ -98,6 +98,8 @@ static bool backend_cleanup(POOL_CONNECTION * volatile *frontend, POOL_CONNECTIO static void child_will_go_down(int code, Datum arg); static int opt_sort(const void *a, const void *b); +static bool unix_fds_not_isset(int* fds, int num_unix_fds, fd_set* opt); + /* * Non 0 means SIGTERM (smart shutdown) or SIGINT (fast shutdown) has arrived */ @@ -1664,7 +1669,7 @@ retry_accept: * 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 */ + if (unix_fds_not_isset(fds, pool_config->num_unix_socket_directories, &rmask)) { on = 1; if (setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, @@ -1695,6 +1700,20 @@ retry_accept: return afd; } +static bool +unix_fds_not_isset(int* fds, int num_unix_fds, fd_set* opt) +{ + int i; + for (i = 0; i < num_unix_fds; i++) + { + if (!FD_ISSET(fds[i], opt)) + continue; + + return false; + } + return true; +} + static void check_config_reload(void) { diff --git a/src/sample/pgpool.conf.sample-stream b/src/sample/pgpool.conf.sample-stream index 312014a4..3f4f46e1 100644 --- a/src/sample/pgpool.conf.sample-stream +++ b/src/sample/pgpool.conf.sample-stream @@ -39,11 +39,14 @@ backend_clustering_mode = 'streaming_replication' #port = 9999 # Port number # (change requires restart) -#socket_dir = '/tmp' +#unix_socket_directories = '/tmp' # Unix domain socket path # The Debian package defaults to # /var/run/postgresql # (change requires restart) +#unix_socket_group = '' +#unix_socket_premissions = 0777 + #reserved_connections = 0 # Number of reserved connections. # Pgpool-II does not accept connections if over diff --git a/src/test/pgpool_setup.in b/src/test/pgpool_setup.in index 8c47750b..b04b42ad 100644 --- a/src/test/pgpool_setup.in +++ b/src/test/pgpool_setup.in @@ -904,7 +904,7 @@ function set_pgpool_conf { echo "failover_command = '$FAILOVER_SCRIPT %d %h %p %D %m %H %M %P %r %R %N %S'" >> $CONF fi - echo "socket_dir = '$PGSOCKET_DIR'" >> $CONF + echo "unix_socket_directories = '$PGSOCKET_DIR'" >> $CONF echo "pcp_socket_dir = '$PGSOCKET_DIR'" >> $CONF echo "logging_collector = off" >> $CONF echo "log_line_prefix = '%m: %a pid %p: '" >> $CONF diff --git a/src/test/watchdog_setup.in b/src/test/watchdog_setup.in index 966205d6..42ba2f75 100644 --- a/src/test/watchdog_setup.in +++ b/src/test/watchdog_setup.in @@ -124,7 +124,7 @@ function set_pgpool_conf { echo "failover_command = '$FAILOVER_SCRIPT %d %h %p %D %m %M %H %P %r %R'" >> $CONF fi - echo "socket_dir = '$PGSOCKET_DIR'" >> $CONF + echo "unix_socket_directories = '$PGSOCKET_DIR'" >> $CONF echo "pcp_socket_dir = '$PGSOCKET_DIR'" >> $CONF } diff --git a/src/utils/pool_process_reporting.c b/src/utils/pool_process_reporting.c index 2f9157a4..1dad43a9 100644 --- a/src/utils/pool_process_reporting.c +++ b/src/utils/pool_process_reporting.c @@ -208,10 +208,27 @@ get_config(int *nrows) StrNCpy(status[i].desc, "pgpool accepting port number", POOLCONFIG_MAXDESCLEN); i++; - /* - pgpool Communication Manager Connection Settings - */ - StrNCpy(status[i].name, "socket_dir", POOLCONFIG_MAXNAMELEN); - snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->socket_dir); - StrNCpy(status[i].desc, "pgpool socket directory", POOLCONFIG_MAXDESCLEN); + StrNCpy(status[i].name, "unix_socket_directories", POOLCONFIG_MAXNAMELEN); + *(status[i].value) = '\0'; + for (j = 0; j < pool_config->num_unix_socket_directories; j++) + { + len = POOLCONFIG_MAXVALLEN - strlen(status[i].value); + strncat(status[i].value, pool_config->unix_socket_directories[j], len); + len = POOLCONFIG_MAXVALLEN - strlen(status[i].value); + if (j != pool_config->num_unix_socket_directories - 1) + strncat(status[i].value, ",", len); + } + StrNCpy(status[i].desc, "pgpool socket directories", POOLCONFIG_MAXDESCLEN); + i++; + + StrNCpy(status[i].name, "unix_socket_group", POOLCONFIG_MAXNAMELEN); + snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->unix_socket_group); + StrNCpy(status[i].desc, "owning user of the unix sockets", POOLCONFIG_MAXDESCLEN); + i++; + + StrNCpy(status[i].name, "unix_socket_permissions", POOLCONFIG_MAXNAMELEN); + snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%04o", pool_config->unix_socket_permissions); + StrNCpy(status[i].desc, "access permissions of the unix sockets.", POOLCONFIG_MAXDESCLEN); i++; StrNCpy(status[i].name, "pcp_listen_addresses", POOLCONFIG_MAXNAMELEN);