/* * $Header$ * * Handles watchdog connection, and protocol communication with pgpool-II * * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * * Copyright (c) 2003-2019 PgPool Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that copyright notice and this permission * notice appear in supporting documentation, and that the name of the * author not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. The author makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pool.h" #ifndef POOL_PRIVATE #include "utils/elog.h" #else #include "utils/fe_ports.h" #endif #include "utils/socket_stream.h" #include "pool_config.h" #include "watchdog/wd_ipc_conn.h" #include "watchdog/wd_ipc_defines.h" static int open_wd_command_sock(bool throw_error); /* shared memory variables */ char *watchdog_ipc_address = NULL; void wd_set_ipc_address(char *socket_dir, int port) { if (watchdog_ipc_address == NULL) { char wd_ipc_sock_addr[255]; snprintf(wd_ipc_sock_addr, sizeof(wd_ipc_sock_addr), "%s/.s.PGPOOLWD_CMD.%d", socket_dir, port); #ifndef POOL_PRIVATE watchdog_ipc_address = pool_shared_memory_segment_get_chunk(strlen(wd_ipc_sock_addr) + 1); strcpy(watchdog_ipc_address, wd_ipc_sock_addr); #else watchdog_ipc_address = pstrdup(wd_ipc_sock_addr); #endif } } void wd_ipc_conn_initialize(void) { if (watchdog_ipc_address == NULL) { wd_set_ipc_address(pool_config->wd_ipc_socket_dir, pool_config->wd_nodes.wd_node_info[pool_config->pgpool_node_id].wd_port); } } size_t estimate_ipc_socket_addr_len(void) { return strlen(pool_config->wd_ipc_socket_dir) + 25; /* wd_ipc_socket_dir/.s.PGPOOLWD_CMD.port*/ } char * get_watchdog_ipc_address(void) { return watchdog_ipc_address; } /* * function issues the command to watchdog process over the watchdog * IPC command socket. * type: command type to send. valid command * types are defined in wd_ipc_defines.h * timeout_sec: number of seconds to wait for the command response * from watchdog * data: command data * data_len: length of data * blocking: send true if caller wants to wait for the results * when blocking is false the timeout_sec is ignored */ WDIPCCmdResult * issue_command_to_watchdog(char type, int timeout_sec, char *data, int data_len, bool blocking) { struct timeval start_time, tv; int sock; WDIPCCmdResult *result = NULL; char res_type = 'P'; int res_length, len; gettimeofday(&start_time, NULL); /* open the watchdog command socket for IPC */ sock = open_wd_command_sock(false); if (sock < 0) return NULL; len = htonl(data_len); if (socket_write(sock, &type, sizeof(char)) <= 0) { close(sock); return NULL; } if (socket_write(sock, &len, sizeof(int)) <= 0) { close(sock); return NULL; } if (data && data_len > 0) { if (socket_write(sock, data, data_len) <= 0) { close(sock); return NULL; } } if (blocking) { /* if we are asked to wait for results */ fd_set fds; struct timeval *timeout_st = NULL; if (timeout_sec > 0) { tv.tv_sec = timeout_sec; tv.tv_usec = 0; timeout_st = &tv; } FD_ZERO(&fds); FD_SET(sock, &fds); for (;;) { int select_res; select_res = select(sock + 1, &fds, NULL, NULL, timeout_st); if (select_res == 0) { close(sock); result = palloc(sizeof(WDIPCCmdResult)); result->type = WD_IPC_CMD_TIMEOUT; result->length = 0; result->data = NULL; return result; } if (select_res < 0) { if (errno == EAGAIN || errno == EINTR) continue; ereport(WARNING, (errmsg("error reading from IPC command socket for ipc command %c", type), errdetail("select system call failed with error \"%m\""))); close(sock); return NULL; } if (select_res > 0) { /* read the result type char */ if (socket_read(sock, &res_type, 1, 0) <= 0) { ereport(WARNING, (errmsg("error reading from IPC command socket for ipc command %c", type), errdetail("read from socket failed with error \"%m\""))); close(sock); return result; } /* read the result data length */ if (socket_read(sock, &res_length, sizeof(int), 0) <= 0) { ereport(WARNING, (errmsg("error reading from IPC command socket for ipc command %c", type), errdetail("read from socket failed with error \"%m\""))); close(sock); return result; } result = palloc(sizeof(WDIPCCmdResult)); result->type = res_type; result->length = ntohl(res_length); result->data = NULL; if (result->length > 0) { result->data = palloc(result->length); if (socket_read(sock, result->data, result->length, 0) <= 0) { pfree(result->data); pfree(result); ereport(DEBUG1, (errmsg("error reading from IPC command socket for ipc command %c", type), errdetail("read from socket failed with error \"%m\""))); close(sock); return NULL; } } break; } } } else { /* * For non blocking mode if we are successful in sending the command * that means the command is success */ result = palloc0(sizeof(WDIPCCmdResult)); result->type = WD_IPC_CMD_RESULT_OK; } close(sock); return result; } static int open_wd_command_sock(bool throw_error) { size_t len; struct sockaddr_un addr; int sock = -1; /* We use unix domain stream sockets for the purpose */ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { /* socket create failed */ ereport(throw_error ? ERROR : LOG, (errmsg("failed to connect to watchdog command server socket"), errdetail("connect on \"%s\" failed with reason: \"%m\"", addr.sun_path))); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", watchdog_ipc_address); len = sizeof(struct sockaddr_un); if (connect(sock, (struct sockaddr *) &addr, len) == -1) { close(sock); ereport(throw_error ? ERROR : LOG, (errmsg("failed to connect to watchdog command server socket"), errdetail("connect on \"%s\" failed with reason: \"%m\"", addr.sun_path))); return -1; } return sock; } void FreeCmdResult(WDIPCCmdResult * res) { if (res == NULL) return; if (res->data) pfree(res->data); pfree(res); }