1 /*
2  * $Header$
3  *
4  * pgpool: a language independent connection pool server for PostgreSQL
5  * written by Tatsuo Ishii
6  *
7  * Copyright (c) 2003-2020	PgPool Global Development Group
8  *
9  * Permission to use, copy, modify, and distribute this software and
10  * its documentation for any purpose and without fee is hereby
11  * granted, provided that the above copyright notice appear in all
12  * copies and that both that copyright notice and this permission
13  * notice appear in supporting documentation, and that the name of the
14  * author not be used in advertising or publicity pertaining to
15  * distribution of the software without specific, written prior
16  * permission. The author makes no representations about the
17  * suitability of this software for any purpose.  It is provided "as
18  * is" without express or implied warranty.
19  *
20  * PCP client program to execute pcp commands.
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <string.h>
29 
30 #include "utils/fe_ports.h"
31 #include "utils/pool_path.h"
32 #include "pcp/pcp.h"
33 
34 #ifdef HAVE_GETOPT_H
35 #include <getopt.h>
36 #else
37 #include "utils/getopt_long.h"
38 #endif
39 
40 const char *progname = NULL;
41 const char *get_progname(const char *argv0);
42 char	   *last_dir_separator(const char *filename);
43 
44 static void usage(void);
45 static inline bool app_require_nodeID(void);
46 static inline bool app_support_cluster_mode(void);
47 static void output_watchdog_info_result(PCPResultInfo * pcpResInfo, bool verbose);
48 static void output_procinfo_result(PCPResultInfo * pcpResInfo, bool all, bool verbose);
49 static void output_proccount_result(PCPResultInfo * pcpResInfo, bool verbose);
50 static void output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose);
51 static void output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose);
52 static void output_health_check_stats_result(PCPResultInfo * pcpResInfo, bool verbose);
53 static void output_nodecount_result(PCPResultInfo * pcpResInfo, bool verbose);
54 static char *backend_status_to_string(BackendInfo * bi);
55 static char *format_titles(const char **titles, const char **types, int ntitles);
56 
57 typedef enum
58 {
59 	PCP_ATTACH_NODE,
60 	PCP_DETACH_NODE,
61 	PCP_NODE_COUNT,
62 	PCP_NODE_INFO,
63 	PCP_HEALTH_CHECK_STATS,
64 	PCP_POOL_STATUS,
65 	PCP_PROC_COUNT,
66 	PCP_PROC_INFO,
67 	PCP_PROMOTE_NODE,
68 	PCP_RECOVERY_NODE,
69 	PCP_STOP_PGPOOL,
70 	PCP_WATCHDOG_INFO,
71 	PCP_RELOAD_CONFIG,
72 	UNKNOWN,
73 }			PCP_UTILITIES;
74 
75 struct AppTypes
76 {
77 	const char *app_name;
78 	PCP_UTILITIES app_type;
79 	const char *allowed_options;
80 	const char *description;
81 };
82 
83 struct AppTypes AllAppTypes[] =
84 {
85 	{"pcp_attach_node", PCP_ATTACH_NODE, "n:h:p:U:wWvd", "attach a node from pgpool-II"},
86 	{"pcp_detach_node", PCP_DETACH_NODE, "n:h:p:U:gwWvd", "detach a node from pgpool-II"},
87 	{"pcp_node_count", PCP_NODE_COUNT, "h:p:U:wWvd", "display the total number of nodes under pgpool-II's control"},
88 	{"pcp_node_info", PCP_NODE_INFO, "n:h:p:U:wWvd", "display a pgpool-II node's information"},
89 	{"pcp_health_check_stats", PCP_HEALTH_CHECK_STATS, "n:h:p:U:wWvd", "display a pgpool-II health check stats data"},
90 	{"pcp_pool_status", PCP_POOL_STATUS, "h:p:U:wWvd", "display pgpool configuration and status"},
91 	{"pcp_proc_count", PCP_PROC_COUNT, "h:p:U:wWvd", "display the list of pgpool-II child process PIDs"},
92 	{"pcp_proc_info", PCP_PROC_INFO, "h:p:P:U:awWvd", "display a pgpool-II child process' information"},
93 	{"pcp_promote_node", PCP_PROMOTE_NODE, "n:h:p:U:gwWvd", "promote a node as new main from pgpool-II"},
94 	{"pcp_recovery_node", PCP_RECOVERY_NODE, "n:h:p:U:wWvd", "recover a node"},
95 	{"pcp_stop_pgpool", PCP_STOP_PGPOOL, "m:h:p:U:s:wWvda", "terminate pgpool-II"},
96 	{"pcp_watchdog_info", PCP_WATCHDOG_INFO, "n:h:p:U:wWvd", "display a pgpool-II watchdog's information"},
97 	{"pcp_reload_config",PCP_RELOAD_CONFIG,"h:p:U:s:wWvd", "reload a pgpool-II config file"},
98 	{NULL, UNKNOWN, NULL, NULL},
99 };
100 struct AppTypes *current_app_type;
101 
102 int
main(int argc,char ** argv)103 main(int argc, char **argv)
104 {
105 	char	   *host = NULL;
106 	int			port = 9898;
107 	char	   *user = NULL;
108 	char	   *pass = NULL;
109 	int			nodeID = -1;
110 	int			processID = 0;
111 	int			ch;
112 	char		shutdown_mode = 's';
113 	char		command_scope = 'l';
114 	int			optindex;
115 	int			i;
116 	bool		all = false;
117 	bool		debug = false;
118 	bool		need_password = true;
119 	bool		gracefully = false;
120 	bool		verbose = false;
121 	PCPConnInfo *pcpConn;
122 	PCPResultInfo *pcpResInfo;
123 
124 	/* here we put all the allowed long options for all utilities */
125 	static struct option long_options[] = {
126 		{"help", no_argument, NULL, '?'},
127 		{"debug", no_argument, NULL, 'd'},
128 		{"version", no_argument, NULL, 'V'},
129 		{"host", required_argument, NULL, 'h'},
130 		{"port", required_argument, NULL, 'p'},
131 		{"process-id", required_argument, NULL, 'P'},
132 		{"username", required_argument, NULL, 'U'},
133 		{"no-password", no_argument, NULL, 'w'},
134 		{"password", no_argument, NULL, 'W'},
135 		{"mode", required_argument, NULL, 'm'},
136 		{"scope", required_argument, NULL, 's'},
137 		{"gracefully", no_argument, NULL, 'g'},
138 		{"verbose", no_argument, NULL, 'v'},
139 		{"all", no_argument, NULL, 'a'},
140 		{"node-id", required_argument, NULL, 'n'},
141 		{"watchdog-id", required_argument, NULL, 'n'},
142 		{NULL, 0, NULL, 0}
143 	};
144 
145 	/* Identify the utility app */
146 	progname = get_progname(argv[0]);
147 	for (i = 0;; i++)
148 	{
149 		current_app_type = &AllAppTypes[i];
150 		if (current_app_type->app_type == UNKNOWN)
151 			break;
152 		if (strcmp(current_app_type->app_name, progname) == 0)
153 			break;
154 	}
155 
156 	if (current_app_type->app_type == UNKNOWN)
157 	{
158 		fprintf(stderr, "%s is a invalid PCP utility\n", progname);
159 		exit(1);
160 	}
161 
162 	if (argc > 1)
163 	{
164 		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
165 		{
166 			usage();
167 			exit(0);
168 		}
169 		else if (strcmp(argv[1], "-V") == 0
170 				 || strcmp(argv[1], "--version") == 0)
171 		{
172 			fprintf(stderr, "%s (%s) %s\n", progname, PACKAGE, VERSION);
173 			exit(0);
174 		}
175 	}
176 
177 	while ((ch = getopt_long(argc, argv, current_app_type->allowed_options, long_options, &optindex)) != -1)
178 	{
179 		switch (ch)
180 		{
181 			case 'd':
182 				debug = true;
183 				break;
184 
185 			case 'a':
186 				all = true;
187 				break;
188 
189 			case 'w':
190 				need_password = false;
191 				break;
192 
193 			case 'W':
194 				need_password = true;
195 				break;
196 
197 			case 'g':
198 				gracefully = true;
199 				break;
200 
201 			case 's':
202 				if (app_support_cluster_mode())
203 				{
204 					if (strcmp(optarg, "c") == 0 || strcmp(optarg, "cluster") == 0)
205 						command_scope = 'c';
206 					else if (strcmp(optarg, "l") == 0 || strcmp(optarg, "local") == 0)
207 						command_scope = 'l';
208 					else
209 					{
210 						fprintf(stderr, "%s: Invalid command socpe \"%s\", must be either \"cluster\" or \"local\" \n", progname, optarg);
211 						exit(1);
212 					}
213 				}
214 				else
215 				{
216 					fprintf(stderr, "Invalid argument \"%s\", Try \"%s --help\" for more information.\n", optarg, progname);
217 					exit(1);
218 				}
219 				break;
220 
221 			case 'm':
222 				if (current_app_type->app_type == PCP_STOP_PGPOOL)
223 				{
224 					if (strcmp(optarg, "s") == 0 || strcmp(optarg, "smart") == 0)
225 						shutdown_mode = 's';
226 					else if (strcmp(optarg, "f") == 0 || strcmp(optarg, "fast") == 0)
227 						shutdown_mode = 'f';
228 					else if (strcmp(optarg, "i") == 0 || strcmp(optarg, "immediate") == 0)
229 						shutdown_mode = 'i';
230 					else
231 					{
232 						fprintf(stderr, "%s: Invalid shutdown mode \"%s\", must be either \"smart\" \"immediate\" or \"fast\" \n", progname, optarg);
233 						exit(1);
234 					}
235 				}
236 				else
237 				{
238 					fprintf(stderr, "Invalid argument \"%s\", Try \"%s --help\" for more information.\n", optarg, progname);
239 					exit(1);
240 				}
241 				break;
242 
243 			case 'v':
244 				verbose = true;
245 				break;
246 
247 			case 'n':
248 				nodeID = atoi(optarg);
249 				if (current_app_type->app_type == PCP_WATCHDOG_INFO)
250 				{
251 					if (nodeID < 0)
252 					{
253 						fprintf(stderr, "%s: Invalid watchdog-id \"%s\", must be a positive number or zero for a local watchdog node\n", progname, optarg);
254 						exit(0);
255 					}
256 				}
257 				else
258 				{
259 					if (nodeID < 0 || nodeID > MAX_NUM_BACKENDS)
260 					{
261 						fprintf(stderr, "%s: Invalid node-id \"%s\", must be between 0 and %d\n", progname, optarg, MAX_NUM_BACKENDS);
262 						exit(0);
263 					}
264 				}
265 				break;
266 
267 			case 'p':
268 				port = atoi(optarg);
269 				if (port <= 1024 || port > 65535)
270 				{
271 					fprintf(stderr, "%s: Invalid port number \"%s\", must be between 1024 and 65535\n", progname, optarg);
272 					exit(0);
273 				}
274 				break;
275 
276 			case 'P':			/* PID */
277 				processID = atoi(optarg);
278 				if (processID <= 0)
279 				{
280 					fprintf(stderr, "%s: Invalid process-id \"%s\", must be greater than 0\n", progname, optarg);
281 					exit(0);
282 				}
283 				break;
284 
285 			case 'h':
286 				host = strdup(optarg);
287 				break;
288 
289 			case 'U':
290 				user = strdup(optarg);
291 				break;
292 
293 			case '?':
294 			default:
295 
296 				/*
297 				 * getopt_long whould already have emitted a complaint
298 				 */
299 				fprintf(stderr, "Try \"%s --help\" for more information.\n\n", progname);
300 				exit(1);
301 		}
302 	}
303 
304 	/*
305 	 * if we still have arguments, use it
306 	 */
307 	while (argc - optind >= 1)
308 	{
309 		if (current_app_type->app_type == PCP_PROC_INFO && processID <= 0)
310 		{
311 			processID = atoi(argv[optind]);
312 			if (processID <= 0)
313 			{
314 				fprintf(stderr, "%s: Invalid process-id \"%s\", must be greater than 0\n", progname, optarg);
315 				exit(0);
316 			}
317 		}
318 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO && nodeID < 0)
319 		{
320 			nodeID = atoi(argv[optind]);
321 			if (nodeID < 0)
322 			{
323 				fprintf(stderr, "%s: Invalid watchdog-id \"%s\", must be a positive number or zero for local watchdog node\n", progname, optarg);
324 				exit(0);
325 			}
326 		}
327 		else if (app_require_nodeID() && nodeID < 0)
328 		{
329 			nodeID = atoi(argv[optind]);
330 			if (nodeID < 0 || nodeID > MAX_NUM_BACKENDS)
331 			{
332 				fprintf(stderr, "%s: Invalid node-id \"%s\", must be between 0 and %d\n", progname, optarg, MAX_NUM_BACKENDS);
333 				exit(0);
334 			}
335 		}
336 		else
337 			fprintf(stderr, "%s: Warning: extra command-line argument \"%s\" ignored\n",
338 					progname, argv[optind]);
339 
340 		optind++;
341 	}
342 
343 	if (nodeID < 0)
344 	{
345 		if (app_require_nodeID())
346 		{
347 			fprintf(stderr, "%s: missing node-id\n", progname);
348 			fprintf(stderr, "Try \"%s --help\" for more information.\n\n", progname);
349 			exit(1);
350 		}
351 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
352 		{
353 			nodeID = -1;
354 		}
355 	}
356 
357 	/* Get a new password if appropriate */
358 	if (need_password)
359 	{
360 		pass = simple_prompt("Password: ", 100, false);
361 		need_password = false;
362 	}
363 
364 	pcpConn = pcp_connect(host, port, user, pass, debug ? stdout : NULL);
365 	if (PCPConnectionStatus(pcpConn) != PCP_CONNECTION_OK)
366 	{
367 		fprintf(stderr, "%s\n", pcp_get_last_error(pcpConn) ? pcp_get_last_error(pcpConn) : "Unknown Error");
368 		exit(1);
369 	}
370 
371 	/*
372 	 * Okay the connection is successful not call the actual PCP function
373 	 */
374 	if (current_app_type->app_type == PCP_ATTACH_NODE)
375 	{
376 		pcpResInfo = pcp_attach_node(pcpConn, nodeID);
377 	}
378 
379 	else if (current_app_type->app_type == PCP_DETACH_NODE)
380 	{
381 		if (gracefully)
382 			pcpResInfo = pcp_detach_node_gracefully(pcpConn, nodeID);
383 		else
384 			pcpResInfo = pcp_detach_node(pcpConn, nodeID);
385 	}
386 
387 	else if (current_app_type->app_type == PCP_NODE_COUNT)
388 	{
389 		pcpResInfo = pcp_node_count(pcpConn);
390 	}
391 
392 	else if (current_app_type->app_type == PCP_NODE_INFO)
393 	{
394 		pcpResInfo = pcp_node_info(pcpConn, nodeID);
395 	}
396 
397 	else if (current_app_type->app_type == PCP_HEALTH_CHECK_STATS)
398 	{
399 		pcpResInfo = pcp_health_check_stats(pcpConn, nodeID);
400 	}
401 
402 	else if (current_app_type->app_type == PCP_POOL_STATUS)
403 	{
404 		pcpResInfo = pcp_pool_status(pcpConn);
405 	}
406 
407 	else if (current_app_type->app_type == PCP_PROC_COUNT)
408 	{
409 		pcpResInfo = pcp_process_count(pcpConn);
410 	}
411 
412 	else if (current_app_type->app_type == PCP_PROC_INFO)
413 	{
414 		pcpResInfo = pcp_process_info(pcpConn, processID);
415 	}
416 
417 	else if (current_app_type->app_type == PCP_PROMOTE_NODE)
418 	{
419 		if (gracefully)
420 			pcpResInfo = pcp_promote_node_gracefully(pcpConn, nodeID);
421 		else
422 			pcpResInfo = pcp_promote_node(pcpConn, nodeID);
423 	}
424 
425 	else if (current_app_type->app_type == PCP_RECOVERY_NODE)
426 	{
427 		pcpResInfo = pcp_recovery_node(pcpConn, nodeID);
428 	}
429 
430 	else if (current_app_type->app_type == PCP_STOP_PGPOOL)
431 	{
432 		pcpResInfo = pcp_terminate_pgpool(pcpConn, shutdown_mode, command_scope);
433 	}
434 
435 	else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
436 	{
437 		pcpResInfo = pcp_watchdog_info(pcpConn, nodeID);
438 	}
439 
440 	else if (current_app_type->app_type == PCP_RELOAD_CONFIG)
441 	{
442 		pcpResInfo = pcp_reload_config(pcpConn,command_scope);
443 	}
444 
445 	else
446 	{
447 		/* should never happen */
448 		fprintf(stderr, "%s: Invalid pcp process\n", progname);
449 		goto DISCONNECT_AND_EXIT;
450 	}
451 
452 	if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
453 	{
454 		fprintf(stderr, "%s\n", pcp_get_last_error(pcpConn) ? pcp_get_last_error(pcpConn) : "Unknown Error");
455 		goto DISCONNECT_AND_EXIT;
456 	}
457 
458 	if (pcp_result_is_empty(pcpResInfo))
459 	{
460 		fprintf(stdout, "%s -- Command Successful\n", progname);
461 	}
462 	else
463 	{
464 		if (current_app_type->app_type == PCP_NODE_COUNT)
465 			output_nodecount_result(pcpResInfo, verbose);
466 
467 		if (current_app_type->app_type == PCP_NODE_INFO)
468 			output_nodeinfo_result(pcpResInfo, verbose);
469 
470 		if (current_app_type->app_type == PCP_HEALTH_CHECK_STATS)
471 			output_health_check_stats_result(pcpResInfo, verbose);
472 
473 		if (current_app_type->app_type == PCP_POOL_STATUS)
474 			output_poolstatus_result(pcpResInfo, verbose);
475 
476 		if (current_app_type->app_type == PCP_PROC_COUNT)
477 			output_proccount_result(pcpResInfo, verbose);
478 
479 		if (current_app_type->app_type == PCP_PROC_INFO)
480 			output_procinfo_result(pcpResInfo, all, verbose);
481 
482 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
483 			output_watchdog_info_result(pcpResInfo, verbose);
484 	}
485 
486 DISCONNECT_AND_EXIT:
487 
488 	pcp_disconnect(pcpConn);
489 	pcp_free_connection(pcpConn);
490 
491 	return 0;
492 }
493 
494 static void
output_nodecount_result(PCPResultInfo * pcpResInfo,bool verbose)495 output_nodecount_result(PCPResultInfo * pcpResInfo, bool verbose)
496 {
497 	if (verbose)
498 	{
499 		printf("Node Count\n");
500 		printf("____________\n");
501 		printf(" %d\n", pcp_get_int_data(pcpResInfo, 0));
502 	}
503 	else
504 		printf("%d\n", pcp_get_int_data(pcpResInfo, 0));
505 }
506 
507 static void
output_nodeinfo_result(PCPResultInfo * pcpResInfo,bool verbose)508 output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose)
509 {
510 	BackendInfo *backend_info = (BackendInfo *) pcp_get_binary_data(pcpResInfo, 0);
511 	char		last_status_change[20];
512 	struct tm	tm;
513 
514 	localtime_r(&backend_info->status_changed_time, &tm);
515 	strftime(last_status_change, sizeof(last_status_change), "%F %T", &tm);
516 
517 	if (verbose)
518 	{
519 		const char *titles[] = {"Hostname", "Port", "Status", "Weight", "Status Name", "Role", "Replication Delay", "Replication State", "Replication Sync State", "Last Status Change"};
520 		const char *types[] = {"s", "d", "d", "f", "s", "s", "lu", "s", "s", "s"};
521 		char *format_string;
522 
523 		format_string = format_titles(titles, types, sizeof(titles)/sizeof(char *));
524 		printf(format_string,
525 			   backend_info->backend_hostname,
526 			   backend_info->backend_port,
527 			   backend_info->backend_status,
528 			   backend_info->backend_weight / RAND_MAX,
529 			   backend_status_to_string(backend_info),
530 			   role_to_str(backend_info->role),
531 			   backend_info->standby_delay,
532 			   backend_info->replication_state,
533 			   backend_info->replication_sync_state,
534 			   last_status_change);
535 	}
536 	else
537 	{
538 		printf("%s %d %d %f %s %s %lu %s %s %s\n",
539 			   backend_info->backend_hostname,
540 			   backend_info->backend_port,
541 			   backend_info->backend_status,
542 			   backend_info->backend_weight / RAND_MAX,
543 			   backend_status_to_string(backend_info),
544 			   role_to_str(backend_info->role),
545 			   backend_info->standby_delay,
546 			   backend_info->replication_state,
547 			   backend_info->replication_sync_state,
548 			   last_status_change);
549 	}
550 }
551 
552 /*
553  * Format and output health check stats
554  */
555 static void
output_health_check_stats_result(PCPResultInfo * pcpResInfo,bool verbose)556 output_health_check_stats_result(PCPResultInfo * pcpResInfo, bool verbose)
557 {
558 	POOL_HEALTH_CHECK_STATS *stats = (POOL_HEALTH_CHECK_STATS *)pcp_get_binary_data(pcpResInfo, 0);
559 
560 	if (verbose)
561 	{
562 		const char *titles[] = {"Node Id", "Host Name", "Port", "Status", "Role", "Last Status Change",
563 								"Total Count", "Success Count", "Fail Count", "Skip Count", "Retry Count",
564 								"Average Retry Count", "Max Retry Count", "Max Health Check Duration",
565 								"Minimum Health Check Duration", "Average Health Check Duration",
566 								"Last Health Check", "Last Successful Health Check",
567 								"Last Skip Health Check", "Last Failed Health Check"};
568 		const char *types[] = {"s", "s", "s", "s", "s", "s", "s", "s", "s", "s",
569 							   "s", "s", "s", "s", "s", "s", "s", "s", "s", "s"};
570 		char *format_string;
571 
572 		format_string = format_titles(titles, types, sizeof(titles)/sizeof(char *));
573 		printf(format_string,
574 			   stats->node_id,
575 			   stats->hostname,
576 			   stats->port,
577 			   stats->status,
578 			   stats->role,
579 			   stats->last_status_change,
580 			   stats->total_count,
581 			   stats->success_count,
582 			   stats->fail_count,
583 			   stats->skip_count,
584 			   stats->retry_count,
585 			   stats->average_retry_count,
586 			   stats->max_retry_count,
587 			   stats->max_health_check_duration,
588 			   stats->min_health_check_duration,
589 			   stats->average_health_check_duration,
590 			   stats->last_health_check,
591 			   stats->last_successful_health_check,
592 			   stats->last_skip_health_check,
593 			   stats->last_failed_health_check);
594 	}
595 	else
596 	{
597 		printf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n",
598 			   stats->node_id,
599 			   stats->hostname,
600 			   stats->port,
601 			   stats->status,
602 			   stats->role,
603 			   stats->last_status_change,
604 			   stats->total_count,
605 			   stats->success_count,
606 			   stats->fail_count,
607 			   stats->skip_count,
608 			   stats->retry_count,
609 			   stats->average_retry_count,
610 			   stats->max_retry_count,
611 			   stats->max_health_check_duration,
612 			   stats->min_health_check_duration,
613 			   stats->average_health_check_duration,
614 			   stats->last_health_check,
615 			   stats->last_successful_health_check,
616 			   stats->last_skip_health_check,
617 			   stats->last_failed_health_check);
618 	}
619 }
620 
621 static void
output_poolstatus_result(PCPResultInfo * pcpResInfo,bool verbose)622 output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose)
623 {
624 	POOL_REPORT_CONFIG *status;
625 	int			i;
626 	int			array_size = pcp_result_slot_count(pcpResInfo);
627 
628 	if (verbose)
629 	{
630 		for (i = 0; i < array_size; i++)
631 		{
632 			status = (POOL_REPORT_CONFIG *) pcp_get_binary_data(pcpResInfo, i);
633 			printf("Name [%3d]:\t%s\n", i, status ? status->name : "NULL");
634 			printf("Value:      \t%s\n", status ? status->value : "NULL");
635 			printf("Description:\t%s\n\n", status ? status->desc : "NULL");
636 		}
637 	}
638 	else
639 	{
640 		for (i = 0; i < array_size; i++)
641 		{
642 			status = (POOL_REPORT_CONFIG *) pcp_get_binary_data(pcpResInfo, i);
643 			if (status == NULL)
644 			{
645 				printf("****Data at %d slot is NULL\n", i);
646 				continue;
647 			}
648 			printf("name : %s\nvalue: %s\ndesc : %s\n\n", status->name, status->value, status->desc);
649 		}
650 	}
651 }
652 
653 static void
output_proccount_result(PCPResultInfo * pcpResInfo,bool verbose)654 output_proccount_result(PCPResultInfo * pcpResInfo, bool verbose)
655 {
656 	int			i;
657 	int			process_count = pcp_get_data_length(pcpResInfo, 0) / sizeof(int);
658 	int		   *process_list = (int *) pcp_get_binary_data(pcpResInfo, 0);
659 
660 	if (verbose)
661 	{
662 		printf("No \t | \t PID\n");
663 		printf("_____________________\n");
664 		for (i = 0; i < process_count; i++)
665 			printf("%d \t | \t %d\n", i, process_list[i]);
666 		printf("\nTotal Processes:%d\n", process_count);
667 	}
668 	else
669 	{
670 		for (i = 0; i < process_count; i++)
671 			printf("%d ", process_list[i]);
672 		printf("\n");
673 	}
674 }
675 
676 static void
output_procinfo_result(PCPResultInfo * pcpResInfo,bool all,bool verbose)677 output_procinfo_result(PCPResultInfo * pcpResInfo, bool all, bool verbose)
678 {
679 	bool		printed = false;
680 	int			i;
681 	char	   *frmt;
682 	char		strcreatetime[128];
683 	char		strstarttime[128];
684 	int			array_size = pcp_result_slot_count(pcpResInfo);
685 
686 	if (verbose)
687 	{
688 		frmt = "Database     : %s\n"
689 			"Username     : %s\n"
690 			"Start time   : %s\n"
691 			"Creation time: %s\n"
692 			"Major        : %d\n"
693 			"Minor        : %d\n"
694 			"Counter      : %d\n"
695 			"Backend PID  : %d\n"
696 			"Connected    : %d\n"
697 			"PID          : %d\n"
698 			"Backend ID   : %d\n";
699 	}
700 	else
701 	{
702 		frmt = "%s %s %s %s %d %d %d %d %d %d %d\n";
703 	}
704 
705 	for (i = 0; i < array_size; i++)
706 	{
707 
708 		ProcessInfo *process_info = (ProcessInfo *) pcp_get_binary_data(pcpResInfo, i);
709 
710 		if (process_info == NULL)
711 			break;
712 		if ((!all) && (process_info->connection_info->database[0] == '\0'))
713 			continue;
714 		printed = true;
715 		*strcreatetime = *strstarttime = '\0';
716 
717 		if (process_info->start_time)
718 			strftime(strstarttime, 128, "%Y-%m-%d %H:%M:%S", localtime(&process_info->start_time));
719 		if (process_info->connection_info->create_time)
720 			strftime(strcreatetime, 128, "%Y-%m-%d %H:%M:%S", localtime(&process_info->connection_info->create_time));
721 
722 		printf(frmt,
723 			   process_info->connection_info->database,
724 			   process_info->connection_info->user,
725 			   strstarttime,
726 			   strcreatetime,
727 			   process_info->connection_info->major,
728 			   process_info->connection_info->minor,
729 			   process_info->connection_info->counter,
730 			   process_info->connection_info->pid,
731 			   process_info->connection_info->connected,
732 			   process_info->pid,
733 			   process_info->connection_info->backend_id);
734 	}
735 	if (printed == false)
736 		printf("No process information available\n\n");
737 }
738 
739 static void
output_watchdog_info_result(PCPResultInfo * pcpResInfo,bool verbose)740 output_watchdog_info_result(PCPResultInfo * pcpResInfo, bool verbose)
741 {
742 	int			i;
743 	PCPWDClusterInfo *cluster = (PCPWDClusterInfo *) pcp_get_binary_data(pcpResInfo, 0);
744 
745 	if (verbose)
746 	{
747 		char	   *quorumStatus;
748 
749 		if (cluster->quorumStatus == 0)
750 			quorumStatus = "QUORUM IS ON THE EDGE";
751 		else if (cluster->quorumStatus == 1)
752 			quorumStatus = "QUORUM EXIST";
753 		else if (cluster->quorumStatus == -1)
754 			quorumStatus = "QUORUM ABSENT";
755 		else if (cluster->quorumStatus == -2)
756 			quorumStatus = "NO LEADER NODE";
757 		else
758 			quorumStatus = "UNKNOWN";
759 
760 		printf("Watchdog Cluster Information \n");
761 		printf("Total Nodes          : %d\n", cluster->remoteNodeCount + 1);
762 		printf("Remote Nodes         : %d\n", cluster->remoteNodeCount);
763 		printf("Quorum state         : %s\n", quorumStatus);
764 		printf("Alive Remote Nodes   : %d\n", cluster->aliveNodeCount);
765 		printf("VIP up on local node : %s\n", cluster->escalated ? "YES" : "NO");
766 		printf("Leader Node Name     : %s\n", cluster->leaderNodeName);
767 		printf("Leader Host Name     : %s\n\n", cluster->leaderHostName);
768 
769 		printf("Watchdog Node Information \n");
770 		for (i = 0; i < cluster->nodeCount; i++)
771 		{
772 			PCPWDNodeInfo *watchdog_info = &cluster->nodeList[i];
773 
774 			printf("Node Name      : %s\n", watchdog_info->nodeName);
775 			printf("Host Name      : %s\n", watchdog_info->hostName);
776 			printf("Delegate IP    : %s\n", watchdog_info->delegate_ip);
777 			printf("Pgpool port    : %d\n", watchdog_info->pgpool_port);
778 			printf("Watchdog port  : %d\n", watchdog_info->wd_port);
779 			printf("Node priority  : %d\n", watchdog_info->wd_priority);
780 			printf("Status         : %d\n", watchdog_info->state);
781 			printf("Status Name    : %s\n\n", watchdog_info->stateName);
782 		}
783 	}
784 	else
785 	{
786 		printf("%d %s %s %s\n\n",
787 			   cluster->remoteNodeCount + 1,
788 			   cluster->escalated ? "YES" : "NO",
789 			   cluster->leaderNodeName,
790 			   cluster->leaderHostName);
791 
792 		for (i = 0; i < cluster->nodeCount; i++)
793 		{
794 			PCPWDNodeInfo *watchdog_info = &cluster->nodeList[i];
795 
796 			printf("%s %s %d %d %d %s\n",
797 				   watchdog_info->nodeName,
798 				   watchdog_info->hostName,
799 				   watchdog_info->pgpool_port,
800 				   watchdog_info->wd_port,
801 				   watchdog_info->state,
802 				   watchdog_info->stateName);
803 		}
804 	}
805 }
806 
807 /* returns true if the current application requires node id argument */
808 static inline bool
app_require_nodeID(void)809 app_require_nodeID(void)
810 {
811 	return (current_app_type->app_type == PCP_ATTACH_NODE ||
812 			current_app_type->app_type == PCP_DETACH_NODE ||
813 			current_app_type->app_type == PCP_NODE_INFO ||
814 			current_app_type->app_type == PCP_HEALTH_CHECK_STATS ||
815 			current_app_type->app_type == PCP_PROMOTE_NODE ||
816 			current_app_type->app_type == PCP_RECOVERY_NODE);
817 }
818 
819 static inline bool
app_support_cluster_mode(void)820 app_support_cluster_mode(void)
821 {
822 	return (current_app_type->app_type == PCP_STOP_PGPOOL ||
823 			current_app_type->app_type == PCP_RELOAD_CONFIG);
824 }
825 
826 static void
usage(void)827 usage(void)
828 {
829 	fprintf(stderr, "%s - %s\n", progname, current_app_type->description);
830 	fprintf(stderr, "Usage:\n");
831 	fprintf(stderr, "%s [OPTION...] %s\n", progname,
832 			app_require_nodeID() ? "[node-id]" :
833 			(current_app_type->app_type == PCP_WATCHDOG_INFO) ? "[watchdog-id]" :
834 			(current_app_type->app_type == PCP_PROC_INFO) ? "[process-id]" : "");
835 
836 	fprintf(stderr, "Options:\n");
837 
838 	/*
839 	 * print the command options
840 	 */
841 	fprintf(stderr, "  -U, --username=NAME    username for PCP authentication\n");
842 	fprintf(stderr, "  -h, --host=HOSTNAME    pgpool-II host\n");
843 	fprintf(stderr, "  -p, --port=PORT        PCP port number\n");
844 	fprintf(stderr, "  -w, --no-password      never prompt for password\n");
845 	fprintf(stderr, "  -W, --password         force password prompt (should happen automatically)\n");
846 
847 	/*
848 	 * Now the options not available for all utilities
849 	 */
850 	if (app_require_nodeID())
851 	{
852 		fprintf(stderr, "  -n, --node-id=NODEID   ID of a backend node\n");
853 	}
854 	if (current_app_type->app_type == PCP_STOP_PGPOOL)
855 	{
856 		fprintf(stderr, "  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n");
857 	}
858 
859 	if (app_support_cluster_mode())
860 	{
861 		fprintf(stderr, "  -s, --scope=SCOPE      SCOPE can be \"cluster\", or \"local\"\n");
862 		fprintf(stderr, "                         cluster scope do operations on all Pgpool-II nodes\n");
863 		fprintf(stderr, "                         part of the watchdog cluster\n");
864 	}
865 	if (current_app_type->app_type == PCP_PROMOTE_NODE ||
866 		current_app_type->app_type == PCP_DETACH_NODE)
867 	{
868 		fprintf(stderr, "  -g, --gracefully       promote gracefully(optional)\n");
869 	}
870 
871 	if (current_app_type->app_type == PCP_WATCHDOG_INFO)
872 	{
873 		fprintf(stderr, "  -n, --watchdog-id=ID   ID of a other pgpool to get information for\n");
874 		fprintf(stderr, "                         ID 0 for the local watchdog\n");
875 		fprintf(stderr, "                         If omitted then get information of all watchdog nodes\n");
876 	}
877 	if (current_app_type->app_type == PCP_PROC_INFO)
878 	{
879 		fprintf(stderr, "  -P, --process-id=PID   PID of the child process to get information for (optional)\n");
880 		fprintf(stderr, "  -a, --all              display all child processes and their available connection slots\n");
881 	}
882 
883 	fprintf(stderr, "  -d, --debug            enable debug message (optional)\n");
884 	fprintf(stderr, "  -v, --verbose          output verbose messages\n");
885 	fprintf(stderr, "  -?, --help             print this help\n\n");
886 }
887 
888 
889 /*
890  * Extracts the actual name of the program as called -
891  * stripped of .exe suffix if any
892  */
893 const char *
get_progname(const char * argv0)894 get_progname(const char *argv0)
895 {
896 	const char *nodir_name;
897 	char	   *progname;
898 
899 	nodir_name = last_dir_separator(argv0);
900 	if (nodir_name)
901 		nodir_name++;
902 	else
903 		nodir_name = argv0;
904 
905 	/*
906 	 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
907 	 * called only once.
908 	 */
909 	progname = strdup(nodir_name);
910 	if (progname == NULL)
911 	{
912 		fprintf(stderr, "%s: out of memory\n", nodir_name);
913 		exit(1);				/* This could exit the postmaster */
914 	}
915 
916 	return progname;
917 }
918 
919 /*
920  * Translate the BACKEND_STATUS enum value to string.
921  * the function returns the constant string so should not be freed
922  */
923 static char *
backend_status_to_string(BackendInfo * bi)924 backend_status_to_string(BackendInfo * bi)
925 {
926 	char	   *statusName;
927 
928 	switch (bi->backend_status)
929 	{
930 
931 		case CON_UNUSED:
932 			statusName = BACKEND_STATUS_CON_UNUSED;
933 			break;
934 
935 		case CON_CONNECT_WAIT:
936 			statusName = BACKEND_STATUS_CON_CONNECT_WAIT;
937 			break;
938 
939 		case CON_UP:
940 			statusName = BACKEND_STATUS_CON_UP;
941 			break;
942 
943 		case CON_DOWN:
944 			{
945 				if (bi->quarantine)
946 					statusName = BACKEND_STATUS_QUARANTINE;
947 				else
948 					statusName = BACKEND_STATUS_CON_DOWN;
949 			}
950 			break;
951 
952 		default:
953 			statusName = "unknown";
954 			break;
955 	}
956 	return statusName;
957 }
958 
959 /* Convert enum role to string */
960 char *
role_to_str(SERVER_ROLE role)961 role_to_str(SERVER_ROLE role)
962 {
963 	static char *role_str[] = {"main", "replica", "primary", "standby"};
964 
965 	if (role < ROLE_MAIN || role > ROLE_STANDBY)
966 		return "unknown";
967 	return role_str[role];
968 }
969 
970 /*
971  * Build format string for -v output mode.
972  *
973  * titles: title string array
974  * types:  printf format type string array (example: "d")
975  * ntitles: size of the arrary
976  */
977 static char *
format_titles(const char ** titles,const char ** types,int ntitles)978 format_titles(const char **titles, const char **types, int ntitles)
979 {
980 	int	i;
981 	int	maxlen = 0;
982 	static char	formatbuf[8192];
983 
984 	for(i = 0; i < ntitles; i++)
985 	{
986 		int l = strlen(titles[i]);
987 		maxlen = (l > maxlen)? l : maxlen;
988 	}
989 
990 	*formatbuf = '\0';
991 
992 	for(i = 0; i < ntitles; i++)
993 	{
994 		char buf[64];
995 		char buf2[64];
996 
997 		snprintf(buf, sizeof(buf), "%%-%ds : %%%%%s", maxlen, types[i]);
998 		snprintf(buf2, sizeof(buf2), buf, titles[i], types[i]);
999 		strncat(formatbuf, buf2, sizeof(formatbuf) - strlen(formatbuf) - 2);
1000 		strcat(formatbuf, "\n");
1001 	}
1002 	return formatbuf;
1003 }
1004