static RETSIGTYPE wakeup_handler(int sig) { POOL_SETMASK(&BlockSig); wakeup_request = 1; write(pipe_fds[1], "\0", 1); POOL_SETMASK(&UnBlockSig); }
static void myexit(int code) { int i; if (getpid() != mypid) return; if (pids != NULL) { POOL_SETMASK(&AuthBlockSig); exiting = 1; for (i = 0; i < pool_config->num_init_children; i++) { pid_t pid = pids[i].pid; if (pid) { kill(pid, SIGTERM); } } while (wait(NULL) > 0) ; if (errno != ECHILD) pool_error("wait() failed. reason:%s", strerror(errno)); POOL_SETMASK(&UnBlockSig); } myunlink(un_addr.sun_path); myunlink(pcp_un_addr.sun_path); myunlink(pool_config->pid_file_name); write_status_file(); pool_shmem_exit(code); exit(code); }
/* * handle SIGHUP * */ static RETSIGTYPE reload_config_handler(int sig) { POOL_SETMASK(&BlockSig); reload_config_request = 1; write(pipe_fds[1], "\0", 1); POOL_SETMASK(&UnBlockSig); }
/* * sleep for seconds specified by "second". Unlike pool_pause(), this * function guarantees that it will sleep for specified seconds. This * function uses pool_pause() internally. If it informs that there is * a pending signal event, they are processed using CHECK_REQUEST * macro. Note that most of these processes are done while all signals * are blocked. */ static void pool_sleep(unsigned int second) { struct timeval current_time, sleep_time; gettimeofday(¤t_time, NULL); sleep_time.tv_sec = second + current_time.tv_sec; sleep_time.tv_usec = current_time.tv_usec; POOL_SETMASK(&UnBlockSig); while (sleep_time.tv_sec > current_time.tv_sec) { struct timeval timeout; int r; timeout.tv_sec = sleep_time.tv_sec - current_time.tv_sec; timeout.tv_usec = sleep_time.tv_usec - current_time.tv_usec; if (timeout.tv_usec < 0) { timeout.tv_sec--; timeout.tv_usec += 1000000; } r = pool_pause(&timeout); POOL_SETMASK(&BlockSig); if (r > 0) CHECK_REQUEST; POOL_SETMASK(&UnBlockSig); gettimeofday(¤t_time, NULL); } POOL_SETMASK(&BlockSig); }
/* * handle SIGUSR1 * */ static RETSIGTYPE failover_handler(int sig) { POOL_SETMASK(&BlockSig); failover_request = 1; write(pipe_fds[1], "\0", 1); POOL_SETMASK(&UnBlockSig); }
/* * find connection by user and database */ POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMajor, int check_socket) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif int i; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_get_cp: pool_connection_pool is not initialized"); return NULL; } POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config.max_pool;i++) { if (MASTER_CONNECTION(p) && MASTER_CONNECTION(p)->sp->major == protoMajor && MASTER_CONNECTION(p)->sp->user != NULL && strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 && strcmp(MASTER_CONNECTION(p)->sp->database, database) == 0) { /* mark this connection is under use */ MASTER_CONNECTION(p)->closetime = 0; POOL_SETMASK(&oldmask); if (check_socket && (check_socket_status(MASTER(p)->fd) < 0 || (DUAL_MODE && check_socket_status(MASTER(p)->fd) < 0))) { pool_log("connection closed. retry to create new connection pool."); pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); return NULL; } return p; } p++; } POOL_SETMASK(&oldmask); return NULL; }
void pool_debug(const char *fmt,...) { va_list ap; #ifdef HAVE_ASPRINTF char *fmt2; int len; #endif #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif if (run_as_pcp_child) { if (!debug) return; } else { if (pool_config->debug_level <= 0) return; } POOL_SETMASK2(&BlockSig, &oldmask); /* Write message to syslog */ if (pool_config->logsyslog == 1) { va_start(ap, fmt); vsyslog(pool_config->syslog_facility | LOG_DEBUG, fmt, ap); va_end(ap); POOL_SETMASK(&oldmask); return; } #ifdef HAVE_ASPRINTF len = asprintf(&fmt2, "%s %s\n", optstring(1), fmt); if (len >= 0 && fmt2) { va_start(ap, fmt); vfprintf(stderr, fmt2, ap); va_end(ap); fflush(stderr); free(fmt2); } #else fprintf(stderr, "%s %s", optstring(1)); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); #endif POOL_SETMASK(&oldmask); }
static RETSIGTYPE exit_handler(int sig) { int i; POOL_SETMASK(&AuthBlockSig); /* * this could happen in a child process if a signal has been sent * before resetting signal handler */ if (getpid() != mypid) { pool_debug("exit_handler: I am not parent"); POOL_SETMASK(&UnBlockSig); pool_shmem_exit(0); exit(0); } if (sig == SIGTERM) pool_log("received smart shutdown request"); else if (sig == SIGINT) pool_log("received fast shutdown request"); else if (sig == SIGQUIT) pool_log("received immediate shutdown request"); else { pool_error("exit_handler: unknown signal received %d", sig); POOL_SETMASK(&UnBlockSig); return; } exiting = 1; for (i = 0; i < pool_config->num_init_children; i++) { pid_t pid = pids[i].pid; if (pid) { kill(pid, sig); } } kill(pcp_pid, sig); POOL_SETMASK(&UnBlockSig); while (wait(NULL) > 0) ; if (errno != ECHILD) pool_error("wait() failed. reason:%s", strerror(errno)); pids = NULL; myexit(0); }
/* * send frontend exiting messages to all connections. this is called * in any case when child process exits, for example failover, child * life time expires or child max connections expires. */ static void send_frontend_exits(void) { int i; POOL_CONNECTION_POOL *p = pool_connection_pool; #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; pool_send_frontend_exits(p); } POOL_SETMASK(&oldmask); }
/* * fork a child for PCP */ pid_t pcp_fork_a_child(int unix_fd, int inet_fd, char *pcp_conf_file) { pid_t pid; pid = fork(); if (pid == 0) { close(pipe_fds[0]); close(pipe_fds[1]); myargv = save_ps_display_args(myargc, myargv); /* call PCP child main */ POOL_SETMASK(&UnBlockSig); reload_config_request = 0; pcp_do_child(unix_fd, inet_fd, pcp_conf_file); } else if (pid == -1) { pool_error("fork() failed. reason: %s", strerror(errno)); myexit(1); } return pid; }
/* fork lifecheck process*/ static pid_t fork_a_lifecheck(int fork_wait_time) { pid_t pid; pid = fork(); if (pid != 0) { if (pid == -1) pool_error("fork_a_lifecheck: fork() failed."); return pid; } if (fork_wait_time > 0) { sleep(fork_wait_time); } myargv = save_ps_display_args(myargc, myargv); POOL_SETMASK(&UnBlockSig); init_ps_display("", "", "", ""); signal(SIGTERM, wd_exit); signal(SIGINT, wd_exit); signal(SIGQUIT, wd_exit); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); set_ps_display("lifecheck",false); /* wait until ready to go */ while (WD_OK != is_wd_lifecheck_ready()) { sleep(pool_config->wd_interval * 10); } pool_log("watchdog: lifecheck started"); /* watchdog loop */ for (;;) { /* pgpool life check */ wd_lifecheck(); sleep(pool_config->wd_interval); } return pid; }
/* * Count up connection counter (from frontend to pgpool) * in shared memory */ static void connection_count_up(void) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif POOL_SETMASK2(&BlockSig, &oldmask); pool_semaphore_lock(CONN_COUNTER_SEM); Req_info->conn_counter++; pool_semaphore_unlock(CONN_COUNTER_SEM); POOL_SETMASK(&oldmask); }
static RETSIGTYPE my_signal_handler(int sig) { POOL_SETMASK(&BlockSig); switch (sig) { case SIGTERM: case SIGINT: case SIGQUIT: exit(0); break; /* Failback or new node added */ case SIGUSR1: restart_request = 1; break; default: exit(1); break; } POOL_SETMASK(&UnBlockSig); }
static void pcp_worker_will_go_down(int code, Datum arg) { if (processType != PT_PCP_WORKER) { /* should never happen */ ereport(WARNING, (errmsg("pcp_worker_will_go_down called from invalid process"))); return; } if(pcp_frontend) pcp_close(pcp_frontend); processState = EXITING; POOL_SETMASK(&UnBlockSig); }
void pool_error(const char *fmt,...) { va_list ap; #ifdef HAVE_ASPRINTF char *fmt2; int len; #endif #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif POOL_SETMASK2(&BlockSig, &oldmask); /* Write error message to syslog */ if (pool_config->logsyslog == 1) { va_start(ap, fmt); vsyslog(pool_config->syslog_facility | LOG_ERR, fmt, ap); va_end(ap); POOL_SETMASK(&oldmask); return; } if (pool_config->print_timestamp) #ifdef HAVE_ASPRINTF len = asprintf(&fmt2, "%s ERROR: pid %d: %s\n", nowsec(), (int)getpid(), fmt); else len = asprintf(&fmt2, "ERROR: pid %d: %s\n", (int)getpid(), fmt); if (len >= 0 && fmt2) { va_start(ap, fmt); vfprintf(stderr, fmt2, ap); va_end(ap); fflush(stderr); free(fmt2); } #else fprintf(stderr, "%s ERROR: pid %d: ", nowsec(), (int)getpid()); else
/* * Count down connection counter (from frontend to pgpool) * in shared memory */ static void connection_count_down(void) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif POOL_SETMASK2(&BlockSig, &oldmask); pool_semaphore_lock(CONN_COUNTER_SEM); /* * Make sure that we do not decrement too much. If failed to read * a start up packet, or receive cancel request etc., * connection_count_down() is called and goes back to the * connection accept loop. Problem is, at the very beginning of * the connection accept loop, if we have received a signal, we * call child_exit() which calls connection_count_down() again. */ if (Req_info->conn_counter > 0) Req_info->conn_counter--; pool_semaphore_unlock(CONN_COUNTER_SEM); POOL_SETMASK(&oldmask); }
/* * fork a child */ pid_t fork_a_child(int unix_fd, int inet_fd, int id) { pid_t pid; pid = fork(); if (pid == 0) { /* Before we unconditionally closed pipe_fds[0] and pipe_fds[1] * here, which is apparently wrong since in the start up of * pgpool, pipe(2) is not called yet and it mistakenly closes * fd 0. Now we check the fd > 0 before close(), expecting * pipe returns fds greater than 0. Note that we cannot * unconditionally remove close(2) calls since fork_a_child() * may be called *after* pgpool starting up. */ if (pipe_fds[0] > 0) { close(pipe_fds[0]); close(pipe_fds[1]); } myargv = save_ps_display_args(myargc, myargv); /* call child main */ POOL_SETMASK(&UnBlockSig); reload_config_request = 0; my_proc_id = id; do_child(unix_fd, inet_fd); } else if (pid == -1) { pool_error("fork() failed. reason: %s", strerror(errno)); myexit(1); } return pid; }
static RETSIGTYPE exit_handler(int sig) { int i; int status; pid_t pid; POOL_SETMASK(&AuthBlockSig); /* * this could happen in a child process if a signal has been sent * before resetting signal handler */ if (getpid() != mypid) { pool_debug("exit_handler: I am not parent"); POOL_SETMASK(&UnBlockSig); pool_shmem_exit(0); exit(0); } if (sig == SIGTERM) pool_log("received smart shutdown request"); else if (sig == SIGINT) pool_log("received fast shutdown request"); else if (sig == SIGQUIT) pool_log("received immediate shutdown request"); else { pool_error("exit_handler: unknown signal received %d", sig); POOL_SETMASK(&UnBlockSig); return; } exiting = 1; for (i = 0; i < pool_config->num_init_children; i++) { pid = pids[i].pid; if (pid) { pool_log("send signal(%d) to pid=%d (pgrp=%d), i=%d", sig, pid, getpgid(pid), i); kill(pid, sig); } } kill(pcp_pid, sig); pool_log("send signal(%d) to pcp_pid=%d", sig, pcp_pid); POOL_SETMASK(&UnBlockSig); i = 0; while (pid = wait(&status) > 0) { pool_log("got wait(): i=%d, pid=%d, status=0x%04x", i++, pid, status); if (!WIFEXITED(status)) { pool_error("wait(): pid=%d, status=0x%04x", pid, status); } } if (errno != ECHILD) pool_error("wait() failed. reason:%s", strerror(errno)); pids = NULL; myexit(0); }
static RETSIGTYPE reload_config_handler(int sig) { POOL_SETMASK(&BlockSig); reload_config_request = 1; POOL_SETMASK(&UnBlockSig); }
/* * fork escalation process */ pid_t fork_escalation_process(void) { pid_t pid; pid = fork(); if (pid != 0) { if (pid == -1) ereport(NOTICE, (errmsg("failed to fork a escalation process"))); return pid; } on_exit_reset(); processType = PT_WATCHDOG_UTILITY; POOL_SETMASK(&UnBlockSig); init_ps_display("", "", "", ""); pool_signal(SIGTERM, wd_exit); pool_signal(SIGINT, wd_exit); pool_signal(SIGQUIT, wd_exit); pool_signal(SIGCHLD, SIG_DFL); pool_signal(SIGHUP, SIG_IGN); pool_signal(SIGPIPE, SIG_IGN); MemoryContextSwitchTo(TopMemoryContext); set_ps_display("watchdog escalation", false); ereport(LOG, (errmsg("watchdog: escalation started"))); /* * STEP 1 clear shared memory cache */ if (pool_config->memory_cache_enabled && pool_is_shmem_cache() && pool_config->clear_memqcache_on_escalation) { ereport(LOG, (errmsg("watchdog escalation"), errdetail("clearing all the query cache on shared memory"))); pool_clear_memory_cache(); } /* * STEP 2 execute escalation command provided by user in pgpool conf file */ if (strlen(pool_config->wd_escalation_command)) { int r = system(pool_config->wd_escalation_command); if (WIFEXITED(r)) { if (WEXITSTATUS(r) == EXIT_SUCCESS) ereport(LOG, (errmsg("watchdog escalation successful"))); else { ereport(WARNING, (errmsg("watchdog escalation command failed with exit status: %d", WEXITSTATUS(r)))); } } else { ereport(WARNING, (errmsg("watchdog escalation command exit abnormally"))); } } /* * STEP 3 bring up the delegate IP */ if (strlen(pool_config->delegate_IP) != 0) { if (wd_IP_up() != WD_OK) ereport(WARNING, (errmsg("watchdog escalation failed to acquire delegate IP"))); } exit(0); }
/* * fork de-escalation process */ pid_t fork_plunging_process(void) { pid_t pid; pid = fork(); if (pid != 0) { if (pid == -1) ereport(NOTICE, (errmsg("failed to fork a de-escalation process"))); return pid; } on_exit_reset(); processType = PT_WATCHDOG_UTILITY; POOL_SETMASK(&UnBlockSig); init_ps_display("", "", "", ""); pool_signal(SIGTERM, wd_exit); pool_signal(SIGINT, wd_exit); pool_signal(SIGQUIT, wd_exit); pool_signal(SIGCHLD, SIG_DFL); pool_signal(SIGHUP, SIG_IGN); pool_signal(SIGPIPE, SIG_IGN); MemoryContextSwitchTo(TopMemoryContext); set_ps_display("watchdog de-escalation", false); ereport(LOG, (errmsg("watchdog: de-escalation started"))); /* * STEP 1 execute de-escalation command provided by user in pgpool conf * file */ if (strlen(pool_config->wd_de_escalation_command)) { int r = system(pool_config->wd_de_escalation_command); if (WIFEXITED(r)) { if (WEXITSTATUS(r) == EXIT_SUCCESS) ereport(LOG, (errmsg("watchdog de-escalation successful"))); else { ereport(WARNING, (errmsg("watchdog de-escalation command failed with exit status: %d", WEXITSTATUS(r)))); } } else { ereport(WARNING, (errmsg("watchdog de-escalation command exit abnormally"))); } } /* * STEP 2 bring down the delegate IP */ if (strlen(pool_config->delegate_IP) != 0) { if (wd_IP_down() != WD_OK) ereport(WARNING, (errmsg("watchdog de-escalation failed to bring down delegate IP"))); } exit(0); }
/* * pgpool main program */ int main(int argc, char **argv) { int opt; int i; int pid; int size; int retrycnt; int sys_retrycnt; myargc = argc; myargv = argv; snprintf(conf_file, sizeof(conf_file), "%s/%s", DEFAULT_CONFIGDIR, POOL_CONF_FILE_NAME); snprintf(pcp_conf_file, sizeof(pcp_conf_file), "%s/%s", DEFAULT_CONFIGDIR, PCP_PASSWD_FILE_NAME); snprintf(hba_file, sizeof(hba_file), "%s/%s", DEFAULT_CONFIGDIR, HBA_CONF_FILE_NAME); while ((opt = getopt(argc, argv, "a:cdf:F:hm:nv")) != -1) { switch (opt) { case 'a': /* specify hba configuration file */ if (!optarg) { usage(); exit(1); } strncpy(hba_file, optarg, sizeof(hba_file)); break; case 'c': /* clear cache option */ clear_cache = 1; break; case 'd': /* debug option */ debug = 1; break; case 'f': /* specify configuration file */ if (!optarg) { usage(); exit(1); } strncpy(conf_file, optarg, sizeof(conf_file)); break; case 'F': /* specify PCP password file */ if (!optarg) { usage(); exit(1); } strncpy(pcp_conf_file, optarg, sizeof(pcp_conf_file)); break; case 'h': usage(); exit(0); break; case 'm': /* stop mode */ if (!optarg) { usage(); exit(1); } if (*optarg == 's' || !strcmp("smart", optarg)) stop_sig = SIGTERM; /* smart shutdown */ else if (*optarg == 'f' || !strcmp("fast", optarg)) stop_sig = SIGINT; /* fast shutdown */ else if (*optarg == 'i' || !strcmp("immediate", optarg)) stop_sig = SIGQUIT; /* immediate shutdown */ else { usage(); exit(1); } break; case 'n': /* no detaching control ttys */ not_detach = 1; break; case 'v': show_version(); exit(0); default: usage(); exit(1); } } mypid = getpid(); if (pool_init_config()) exit(1); if (pool_get_config(conf_file, INIT_CONFIG)) { pool_error("Unable to get configuration. Exiting..."); exit(1); } if (pool_config->enable_pool_hba) load_hba(hba_file); /* * If a non-switch argument remains, then it should be either "reload" or "stop". */ if (optind == (argc - 1)) { if (!strcmp(argv[optind], "reload")) { pid_t pid; pid = read_pid_file(); if (pid < 0) { pool_error("could not read pid file"); pool_shmem_exit(1); exit(1); } if (kill(pid, SIGHUP) == -1) { pool_error("could not reload configuration file pid: %d. reason: %s", pid, strerror(errno)); pool_shmem_exit(1); exit(1); } pool_shmem_exit(0); exit(0); } if (!strcmp(argv[optind], "stop")) { stop_me(); pool_shmem_exit(0); exit(0); } else { usage(); pool_shmem_exit(1); exit(1); } } /* * else if no non-switch argument remains, then it should be a start request */ else if (optind == argc) { pid = read_pid_file(); if (pid > 0) { if (kill(pid, 0) == 0) { fprintf(stderr, "pid file found. is another pgpool(%d) is running?\n", pid); exit(1); } else fprintf(stderr, "pid file found but it seems bogus. Trying to start pgpool anyway...\n"); } } /* * otherwise an error... */ else { usage(); exit(1); } /* set signal masks */ poolinitmask(); if (not_detach) write_pid_file(); else daemonize(); if (pool_semaphore_create(MAX_NUM_SEMAPHORES)) { pool_error("Unable to create semaphores. Exiting..."); pool_shmem_exit(1); exit(1); } /* * Restore previous backend status if possible */ read_status_file(); /* clear cache */ if (clear_cache && pool_config->enable_query_cache && SYSDB_STATUS == CON_UP) { Interval interval[1]; interval[0].quantity = 0; interval[0].unit = second; pool_clear_cache_by_time(interval, 1); } /* set unix domain socket path */ snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/.s.PGSQL.%d", pool_config->socket_dir, pool_config->port); /* set up signal handlers */ pool_signal(SIGPIPE, SIG_IGN); /* create unix domain socket */ unix_fd = create_unix_domain_socket(un_addr); /* 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); } size = pool_config->num_init_children * pool_config->max_pool * sizeof(ConnectionInfo); con_info = pool_shared_memory_create(size); if (con_info == NULL) { pool_error("failed to allocate connection informations"); myexit(1); } memset(con_info, 0, size); size = pool_config->num_init_children * (sizeof(ProcessInfo)); pids = pool_shared_memory_create(size); if (pids == NULL) { pool_error("failed to allocate pids"); myexit(1); } memset(pids, 0, size); for (i = 0; i < pool_config->num_init_children; i++) { pids[i].connection_info = &con_info[i * pool_config->max_pool]; } /* create fail over/switch over event area */ Req_info = pool_shared_memory_create(sizeof(POOL_REQUEST_INFO)); if (Req_info == NULL) { pool_error("failed to allocate Req_info"); myexit(1); } /* initialize Req_info */ Req_info->kind = NODE_UP_REQUEST; memset(Req_info->node_id, -1, sizeof(int) * MAX_NUM_BACKENDS); Req_info->master_node_id = get_next_master_node(); Req_info->conn_counter = 0; InRecovery = pool_shared_memory_create(sizeof(int)); if (InRecovery == NULL) { pool_error("failed to allocate InRecovery"); myexit(1); } *InRecovery = 0; /* * We need to block signal here. Otherwise child might send some * signals, for example SIGUSR1(fail over). Children will inherit * signal blocking but they do unblock signals at the very beginning * of process. So this is harmless. */ POOL_SETMASK(&BlockSig); /* fork the children */ for (i=0;i<pool_config->num_init_children;i++) { pids[i].pid = fork_a_child(unix_fd, inet_fd, i); pids[i].start_time = time(NULL); } /* set up signal handlers */ pool_signal(SIGTERM, exit_handler); pool_signal(SIGINT, exit_handler); pool_signal(SIGQUIT, exit_handler); pool_signal(SIGCHLD, reap_handler); pool_signal(SIGUSR1, failover_handler); pool_signal(SIGUSR2, wakeup_handler); pool_signal(SIGHUP, reload_config_handler); /* create pipe for delivering event */ if (pipe(pipe_fds) < 0) { pool_error("failed to create pipe"); myexit(1); } pool_log("pgpool successfully started"); /* fork a child for PCP handling */ snprintf(pcp_un_addr.sun_path, sizeof(pcp_un_addr.sun_path), "%s/.s.PGSQL.%d", pool_config->pcp_socket_dir, pool_config->pcp_port); pcp_unix_fd = create_unix_domain_socket(pcp_un_addr); /* maybe change "*" to pool_config->pcp_listen_addresses */ pcp_inet_fd = create_inet_domain_socket("*", pool_config->pcp_port); pcp_pid = pcp_fork_a_child(pcp_unix_fd, pcp_inet_fd, pcp_conf_file); retrycnt = 0; /* reset health check retry counter */ sys_retrycnt = 0; /* reset SystemDB health check retry counter */ /* * This is the main loop */ for (;;) { CHECK_REQUEST; /* do we need health checking for PostgreSQL? */ if (pool_config->health_check_period > 0) { int sts; int sys_sts = 0; unsigned int sleep_time; if (retrycnt == 0) { pool_debug("starting health checking"); } else { pool_debug("retrying %d th health checking", retrycnt); } if (pool_config->health_check_timeout > 0) { /* * set health checker timeout. we want to detect * communication path failure much earlier before * TCP/IP stack detects it. */ pool_signal(SIGALRM, health_check_timer_handler); alarm(pool_config->health_check_timeout); } /* * do actual health check. trying to connect to the backend */ errno = 0; health_check_timer_expired = 0; POOL_SETMASK(&UnBlockSig); sts = health_check(); POOL_SETMASK(&BlockSig); if (pool_config->parallel_mode || pool_config->enable_query_cache) sys_sts = system_db_health_check(); if ((sts > 0 || sys_sts < 0) && (errno != EINTR || (errno == EINTR && health_check_timer_expired))) { if (sts > 0) { sts--; if (!pool_config->parallel_mode) { pool_log("set %d th backend down status", sts); Req_info->kind = NODE_DOWN_REQUEST; Req_info->node_id[0] = sts; failover(); /* need to distribute this info to children */ } else { retrycnt++; pool_signal(SIGALRM, SIG_IGN); /* Cancel timer */ if (retrycnt > NUM_BACKENDS) { /* retry count over */ pool_log("set %d th backend down status", sts); Req_info->kind = NODE_DOWN_REQUEST; Req_info->node_id[0] = sts; failover(); retrycnt = 0; } else { /* continue to retry */ sleep_time = pool_config->health_check_period/NUM_BACKENDS; pool_debug("retry sleep time: %d seconds", sleep_time); pool_sleep(sleep_time); continue; } } } if (sys_sts < 0) { sys_retrycnt++; pool_signal(SIGALRM, SIG_IGN); if (sys_retrycnt > NUM_BACKENDS) { pool_log("set SystemDB down status"); SYSDB_STATUS = CON_DOWN; sys_retrycnt = 0; } else if (sts == 0) /* goes to sleep only when SystemDB alone was down */ { sleep_time = pool_config->health_check_period/NUM_BACKENDS; pool_debug("retry sleep time: %d seconds", sleep_time); pool_sleep(sleep_time); continue; } } } if (pool_config->health_check_timeout > 0) { /* seems ok. cancel health check timer */ pool_signal(SIGALRM, SIG_IGN); } sleep_time = pool_config->health_check_period; pool_sleep(sleep_time); } else { for (;;) { int r; struct timeval t = {3, 0}; POOL_SETMASK(&UnBlockSig); r = pool_pause(&t); POOL_SETMASK(&BlockSig); if (r > 0) break; } } } pool_shmem_exit(0); }
/* * find connection by user and database */ POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int protoMajor, int check_socket) { #ifdef HAVE_SIGPROCMASK sigset_t oldmask; #else int oldmask; #endif int i, freed = 0; ConnectionInfo *info; POOL_CONNECTION_POOL *p = pool_connection_pool; if (p == NULL) { pool_error("pool_get_cp: pool_connection_pool is not initialized"); return NULL; } POOL_SETMASK2(&BlockSig, &oldmask); for (i=0;i<pool_config->max_pool;i++) { if (MASTER_CONNECTION(p) && MASTER_CONNECTION(p)->sp && MASTER_CONNECTION(p)->sp->major == protoMajor && MASTER_CONNECTION(p)->sp->user != NULL && strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 && strcmp(MASTER_CONNECTION(p)->sp->database, database) == 0) { int sock_broken = 0; int j; /* mark this connection is under use */ MASTER_CONNECTION(p)->closetime = 0; for (j=0;j<NUM_BACKENDS;j++) { p->info[j].counter++; } POOL_SETMASK(&oldmask); if (check_socket) { for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j)) continue; if (CONNECTION_SLOT(p, j)) { sock_broken = check_socket_status(CONNECTION(p, j)->fd); if (sock_broken < 0) break; } else { sock_broken = -1; break; } } if (sock_broken < 0) { pool_log("connection closed. retry to create new connection pool."); for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j) || (CONNECTION_SLOT(p, j) == NULL)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp); freed = 1; } pool_close(CONNECTION(p, j)); free(CONNECTION_SLOT(p, j)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL_SLOT)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); POOL_SETMASK(&oldmask); return NULL; } } POOL_SETMASK(&oldmask); pool_index = i; return p; } p++; } POOL_SETMASK(&oldmask); return NULL; }
void pool_backend_timer(void) { #define TMINTMAX 0x7fffffff POOL_CONNECTION_POOL *p = pool_connection_pool; int i; time_t now; time_t nearest = TMINTMAX; POOL_SETMASK(&BlockSig); now = time(NULL); pool_debug("pool_backend_timer_handler called at %d", now); for (i=0;i<pool_config.max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; /* timer expire? */ if (MASTER_CONNECTION(p)->closetime) { pool_debug("pool_backend_timer_handler: expire time: %d", MASTER_CONNECTION(p)->closetime+pool_config.connection_life_time); if (now >= (MASTER_CONNECTION(p)->closetime+pool_config.connection_life_time)) { /* discard expired connection */ pool_debug("pool_backend_timer_handler: expires user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); pool_free_startup_packet(MASTER_CONNECTION(p)->sp); pool_close(MASTER_CONNECTION(p)->con); free(MASTER_CONNECTION(p)); if (DUAL_MODE) { pool_close(SECONDARY_CONNECTION(p)->con); free(SECONDARY_CONNECTION(p)); } memset(p, 0, sizeof(POOL_CONNECTION_POOL)); } else { /* look for nearest timer */ if (MASTER_CONNECTION(p)->closetime < nearest) nearest = MASTER_CONNECTION(p)->closetime; } } } /* any remaining timer */ if (nearest != TMINTMAX) { nearest = pool_config.connection_life_time - (now - nearest); if (nearest <= 0) nearest = 1; pool_signal(SIGALRM, pool_backend_timer_handler); alarm(nearest); } POOL_SETMASK(&UnBlockSig); }
pid_t wd_hb_receiver(int fork_wait_time, WdHbIf hb_if) { int sock; pid_t pid = 0; WdHbPacket pkt; struct timeval tv; char from[WD_MAX_HOST_NAMELEN]; char buf[(MD5_PASSWD_LEN+1)*2]; char pack_str[WD_MAX_PACKET_STRING]; int pack_str_len; WdInfo * p; pid = fork(); if (pid != 0) { if (pid == -1) pool_error("wd_hb_receiver: fork() failed."); return pid; } if (fork_wait_time > 0) { sleep(fork_wait_time); } myargv = save_ps_display_args(myargc, myargv); POOL_SETMASK(&UnBlockSig); signal(SIGTERM, hb_receiver_exit); signal(SIGINT, hb_receiver_exit); signal(SIGQUIT, hb_receiver_exit); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); init_ps_display("", "", "", ""); if ( (sock = wd_create_hb_recv_socket(hb_if)) < 0) { pool_error("wd_hb_receiver: socket create failed"); hb_receiver_exit(SIGTERM); } set_ps_display("heartbeat receiver", false); for(;;) { if (wd_hb_recv(sock, &pkt) == WD_OK) { /* authentication */ if (strlen(pool_config->wd_authkey)) { /* calculate hash from packet */ pack_str_len = packet_to_string_hb(pkt, pack_str, sizeof(pack_str)); wd_calc_hash(pack_str, pack_str_len, buf); if (strcmp(pkt.hash, buf)) { pool_log("wd_hb_receiver: authentication failed"); continue; } } /* get current time */ gettimeofday(&tv, NULL); /* who send this packet? */ strlcpy(from, pkt.from, sizeof(from)); p = WD_List; while (p->status != WD_END) { if (!strcmp(p->hostname, from)) { /* this is the first packet or the latest packet */ if (!WD_TIME_ISSET(p->hb_send_time) || WD_TIME_BEFORE(p->hb_send_time, pkt.send_time)) { pool_debug("wd_hb_receiver: received heartbeat signal from %s", from); p->hb_send_time = pkt.send_time; p->hb_last_recv_time = tv; } break; } p++; } } } return pid; }
pid_t wd_hb_sender(int fork_wait_time, WdHbIf hb_if) { int sock; pid_t pid = 0; WdHbPacket pkt; WdInfo * p = WD_List; char pack_str[WD_MAX_PACKET_STRING]; int pack_str_len; pid = fork(); if (pid != 0) { if (pid == -1) pool_error("wd_hb_sender: fork() failed."); return pid; } if (fork_wait_time > 0) { sleep(fork_wait_time); } myargv = save_ps_display_args(myargc, myargv); POOL_SETMASK(&UnBlockSig); signal(SIGTERM, hb_sender_exit); signal(SIGINT, hb_sender_exit); signal(SIGQUIT, hb_sender_exit); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); init_ps_display("", "", "", ""); if ( (sock = wd_create_hb_send_socket(hb_if)) < 0) { pool_error("wd_hb_sender: socket create failed"); hb_sender_exit(SIGTERM); } set_ps_display("heartbeat sender", false); for(;;) { gettimeofday(&pkt.send_time, NULL); strlcpy(pkt.from, pool_config->wd_hostname, sizeof(pkt.from)); pkt.status = p->status; if (strlen(pool_config->wd_authkey)) { /* calculate hash from packet */ pack_str_len = packet_to_string_hb(pkt, pack_str, sizeof(pack_str)); wd_calc_hash(pack_str, pack_str_len, pkt.hash); } wd_hb_send(sock, &pkt, sizeof(pkt), hb_if.addr); pool_debug("wd_hb_sender: send heartbeat signal to %s", hb_if.addr); sleep(pool_config->wd_heartbeat_keepalive); } return pid; }
void pool_backend_timer(void) { #define TMINTMAX 0x7fffffff POOL_CONNECTION_POOL *p = pool_connection_pool; int i, j; time_t now; time_t nearest = TMINTMAX; ConnectionInfo *info; POOL_SETMASK(&BlockSig); now = time(NULL); pool_debug("pool_backend_timer_handler called at %ld", now); for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; /* timer expire? */ if (MASTER_CONNECTION(p)->closetime) { int freed = 0; pool_debug("pool_backend_timer_handler: expire time: %ld", MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time); if (now >= (MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time)) { /* discard expired connection */ pool_debug("pool_backend_timer_handler: expires user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp); freed = 1; } pool_close(CONNECTION(p, j)); free(CONNECTION_SLOT(p, j)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); /* prepare to shutdown connections to system db */ if(pool_config->system_db_dynamic_connection && (pool_config->parallel_mode || pool_config->enable_query_cache)) { if (system_db_info->pgconn) pool_close_libpq_connection(); if (pool_system_db_connection() && pool_system_db_connection()->con) { pool_send_frontend_exit(pool_system_db_connection()->con); pool_close(pool_system_db_connection()->con); } if( system_db_info->connection ) { free( system_db_info->connection ); memset(system_db_info->connection, 0, sizeof(POOL_CONNECTION_POOL_SLOT)); system_db_info->connection = NULL; } } } else { /* look for nearest timer */ if (MASTER_CONNECTION(p)->closetime < nearest) nearest = MASTER_CONNECTION(p)->closetime; } } } /* any remaining timer */ if (nearest != TMINTMAX) { nearest = pool_config->connection_life_time - (now - nearest); if (nearest <= 0) nearest = 1; pool_signal(SIGALRM, pool_backend_timer_handler); alarm(nearest); } POOL_SETMASK(&UnBlockSig); }
/* fork lifecheck process*/ static pid_t fork_a_lifecheck(int fork_wait_time) { pid_t pid; sigjmp_buf local_sigjmp_buf; pid = fork(); if (pid != 0) { if (pid == -1) ereport(ERROR, (errmsg("failed to fork a lifecheck process"))); return pid; } on_exit_reset(); processType = PT_LIFECHECK; if (fork_wait_time > 0) { sleep(fork_wait_time); } POOL_SETMASK(&UnBlockSig); init_ps_display("", "", "", ""); signal(SIGTERM, wd_exit); signal(SIGINT, wd_exit); signal(SIGQUIT, wd_exit); signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); /* Create per loop iteration memory context */ ProcessLoopContext = AllocSetContextCreate(TopMemoryContext, "wd_lifecheck_main_loop", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(TopMemoryContext); set_ps_display("lifecheck",false); /* wait until ready to go */ while (WD_OK != is_wd_lifecheck_ready()) { sleep(pool_config->wd_interval * 10); } ereport(LOG, (errmsg("watchdog: lifecheck started"))); if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; EmitErrorReport(); MemoryContextSwitchTo(TopMemoryContext); FlushErrorState(); sleep(pool_config->wd_heartbeat_keepalive); } /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; /* watchdog loop */ for (;;) { MemoryContextSwitchTo(ProcessLoopContext); MemoryContextResetAndDeleteChildren(ProcessLoopContext); /* pgpool life check */ wd_lifecheck(); sleep(pool_config->wd_interval); } return pid; }
pid_t wd_child(int fork_wait_time) { int sock; int fd; int rtn; pid_t pid = 0; pid = fork(); if (pid != 0) { if (pid == -1) pool_error("wd_child: fork() failed."); return pid; } if (fork_wait_time > 0) { sleep(fork_wait_time); } myargv = save_ps_display_args(myargc, myargv); POOL_SETMASK(&UnBlockSig); signal(SIGTERM, wd_child_exit); signal(SIGINT, wd_child_exit); signal(SIGQUIT, wd_child_exit); signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGALRM, SIG_IGN); init_ps_display("", "", "", ""); if (WD_List == NULL) { /* memory allocate is not ready */ wd_child_exit(15); } sock = wd_create_recv_socket(WD_MYSELF->wd_port); if (sock < 0) { /* socket create failed */ wd_child_exit(15); } set_ps_display("watchdog", false); /* child loop */ for(;;) { WdPacket buf; fd = wd_accept(sock); if (fd < 0) { continue; } rtn = wd_recv_packet(fd, &buf); if (rtn == WD_OK) { wd_send_response(fd, &buf); } close(fd); } return pid; }
/* * health check timer handler */ static RETSIGTYPE health_check_timer_handler(int sig) { POOL_SETMASK(&BlockSig); health_check_timer_expired = 1; POOL_SETMASK(&UnBlockSig); }