1 /*
2  * $Header$
3  *
4  * pgpool: a language independent connection pool server for PostgreSQL
5  * written by Tatsuo Ishii
6  *
7  * Copyright (c) 2003-2018	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 
29 #include "utils/fe_ports.h"
30 #include "utils/pool_path.h"
31 #include "pcp/pcp.h"
32 
33 #ifdef HAVE_GETOPT_H
34 #include <getopt.h>
35 #else
36 #include "utils/getopt_long.h"
37 #endif
38 
39 const char *progname = NULL;
40 const char *get_progname(const char *argv0);
41 char	   *last_dir_separator(const char *filename);
42 
43 static void usage(void);
44 static inline bool app_require_nodeID(void);
45 static void output_watchdog_info_result(PCPResultInfo * pcpResInfo, bool verbose);
46 static void output_procinfo_result(PCPResultInfo * pcpResInfo, bool all, bool verbose);
47 static void output_proccount_result(PCPResultInfo * pcpResInfo, bool verbose);
48 static void output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose);
49 static void output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose);
50 static void output_nodecount_result(PCPResultInfo * pcpResInfo, bool verbose);
51 static char *backend_status_to_string(BackendInfo * bi);
52 
53 typedef enum
54 {
55 	PCP_ATTACH_NODE,
56 	PCP_DETACH_NODE,
57 	PCP_NODE_COUNT,
58 	PCP_NODE_INFO,
59 	PCP_POOL_STATUS,
60 	PCP_PROC_COUNT,
61 	PCP_PROC_INFO,
62 	PCP_PROMOTE_NODE,
63 	PCP_RECOVERY_NODE,
64 	PCP_STOP_PGPOOL,
65 	PCP_WATCHDOG_INFO,
66 	UNKNOWN,
67 }			PCP_UTILITIES;
68 
69 struct AppTypes
70 {
71 	const char *app_name;
72 	PCP_UTILITIES app_type;
73 	const char *allowed_options;
74 	const char *description;
75 };
76 
77 struct AppTypes AllAppTypes[] =
78 {
79 	{"pcp_attach_node", PCP_ATTACH_NODE, "n:h:p:U:wWvd", "attach a node from pgpool-II"},
80 	{"pcp_detach_node", PCP_DETACH_NODE, "n:h:p:U:gwWvd", "detach a node from pgpool-II"},
81 	{"pcp_node_count", PCP_NODE_COUNT, "h:p:U:wWvd", "display the total number of nodes under pgpool-II's control"},
82 	{"pcp_node_info", PCP_NODE_INFO, "n:h:p:U:wWvd", "display a pgpool-II node's information"},
83 	{"pcp_pool_status", PCP_POOL_STATUS, "h:p:U:wWvd", "display pgpool configuration and status"},
84 	{"pcp_proc_count", PCP_PROC_COUNT, "h:p:U:wWvd", "display the list of pgpool-II child process PIDs"},
85 	{"pcp_proc_info", PCP_PROC_INFO, "h:p:P:U:awWvd", "display a pgpool-II child process' information"},
86 	{"pcp_promote_node", PCP_PROMOTE_NODE, "n:h:p:U:gwWvd", "promote a node as new master from pgpool-II"},
87 	{"pcp_recovery_node", PCP_RECOVERY_NODE, "n:h:p:U:wWvd", "recover a node"},
88 	{"pcp_stop_pgpool", PCP_STOP_PGPOOL, "m:h:p:U:wWvd", "terminate pgpool-II"},
89 	{"pcp_watchdog_info", PCP_WATCHDOG_INFO, "n:h:p:U:wWvd", "display a pgpool-II watchdog's information"},
90 	{NULL, UNKNOWN, NULL, NULL},
91 };
92 struct AppTypes *current_app_type;
93 
94 int
main(int argc,char ** argv)95 main(int argc, char **argv)
96 {
97 	char	   *host = NULL;
98 	int			port = 9898;
99 	char	   *user = NULL;
100 	char	   *pass = NULL;
101 	int			nodeID = -1;
102 	int			processID = 0;
103 	int			ch;
104 	char		shutdown_mode = 's';
105 	int			optindex;
106 	int			i;
107 	bool		all = false;
108 	bool		debug = false;
109 	bool		need_password = true;
110 	bool		gracefully = false;
111 	bool		verbose = false;
112 	PCPConnInfo *pcpConn;
113 	PCPResultInfo *pcpResInfo;
114 
115 	/* here we put all the allowed long options for all utilities */
116 	static struct option long_options[] = {
117 		{"help", no_argument, NULL, '?'},
118 		{"debug", no_argument, NULL, 'd'},
119 		{"version", no_argument, NULL, 'V'},
120 		{"host", required_argument, NULL, 'h'},
121 		{"port", required_argument, NULL, 'p'},
122 		{"process-id", required_argument, NULL, 'P'},
123 		{"username", required_argument, NULL, 'U'},
124 		{"no-password", no_argument, NULL, 'w'},
125 		{"password", no_argument, NULL, 'W'},
126 		{"mode", required_argument, NULL, 'm'},
127 		{"gracefully", no_argument, NULL, 'g'},
128 		{"verbose", no_argument, NULL, 'v'},
129 		{"all", no_argument, NULL, 'a'},
130 		{"node-id", required_argument, NULL, 'n'},
131 		{"watchdog-id", required_argument, NULL, 'n'},
132 		{NULL, 0, NULL, 0}
133 	};
134 
135 	/* Identify the utility app */
136 	progname = get_progname(argv[0]);
137 	for (i = 0;; i++)
138 	{
139 		current_app_type = &AllAppTypes[i];
140 		if (current_app_type->app_type == UNKNOWN)
141 			break;
142 		if (strcmp(current_app_type->app_name, progname) == 0)
143 			break;
144 	}
145 
146 	if (current_app_type->app_type == UNKNOWN)
147 	{
148 		fprintf(stderr, "%s is a invalid PCP utility\n", progname);
149 		exit(1);
150 	}
151 
152 	if (argc > 1)
153 	{
154 		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
155 		{
156 			usage();
157 			exit(0);
158 		}
159 		else if (strcmp(argv[1], "-V") == 0
160 				 || strcmp(argv[1], "--version") == 0)
161 		{
162 			fprintf(stderr, "%s (%s) %s\n", progname, PACKAGE, VERSION);
163 			exit(0);
164 		}
165 	}
166 
167 	while ((ch = getopt_long(argc, argv, current_app_type->allowed_options, long_options, &optindex)) != -1)
168 	{
169 		switch (ch)
170 		{
171 			case 'd':
172 				debug = true;
173 				break;
174 
175 			case 'a':
176 				all = true;
177 				break;
178 
179 			case 'w':
180 				need_password = false;
181 				break;
182 
183 			case 'W':
184 				need_password = true;
185 				break;
186 
187 			case 'g':
188 				gracefully = true;
189 				break;
190 
191 			case 'm':
192 				if (current_app_type->app_type == PCP_STOP_PGPOOL)
193 				{
194 					if (strcmp(optarg, "s") == 0 || strcmp(optarg, "smart") == 0)
195 						shutdown_mode = 's';
196 					else if (strcmp(optarg, "f") == 0 || strcmp(optarg, "fast") == 0)
197 						shutdown_mode = 'f';
198 					else if (strcmp(optarg, "i") == 0 || strcmp(optarg, "immediate") == 0)
199 						shutdown_mode = 'i';
200 					else
201 					{
202 						fprintf(stderr, "%s: Invalid shutdown mode \"%s\", must be either \"smart\" \"immediate\" or \"fast\" \n", progname, optarg);
203 						exit(1);
204 					}
205 				}
206 				else
207 				{
208 					fprintf(stderr, "Invalid argument \"%s\", Try \"%s --help\" for more information.\n", optarg, progname);
209 					exit(1);
210 				}
211 				break;
212 
213 			case 'v':
214 				verbose = true;
215 				break;
216 
217 			case 'n':
218 				nodeID = atoi(optarg);
219 				if (current_app_type->app_type == PCP_WATCHDOG_INFO)
220 				{
221 					if (nodeID < 0)
222 					{
223 						fprintf(stderr, "%s: Invalid watchdog-id \"%s\", must be a positive number or zero for a local watchdog node\n", progname, optarg);
224 						exit(0);
225 					}
226 				}
227 				else
228 				{
229 					if (nodeID < 0 || nodeID > MAX_NUM_BACKENDS)
230 					{
231 						fprintf(stderr, "%s: Invalid node-id \"%s\", must be between 0 and %d\n", progname, optarg, MAX_NUM_BACKENDS);
232 						exit(0);
233 					}
234 				}
235 				break;
236 
237 			case 'p':
238 				port = atoi(optarg);
239 				if (port <= 1024 || port > 65535)
240 				{
241 					fprintf(stderr, "%s: Invalid port number \"%s\", must be between 1024 and 65535\n", progname, optarg);
242 					exit(0);
243 				}
244 				break;
245 
246 			case 'P':			/* PID */
247 				processID = atoi(optarg);
248 				if (processID <= 0)
249 				{
250 					fprintf(stderr, "%s: Invalid process-id \"%s\", must be greater than 0\n", progname, optarg);
251 					exit(0);
252 				}
253 				break;
254 
255 			case 'h':
256 				host = strdup(optarg);
257 				break;
258 
259 			case 'U':
260 				user = strdup(optarg);
261 				break;
262 
263 			case '?':
264 			default:
265 
266 				/*
267 				 * getopt_long whould already have emitted a complaint
268 				 */
269 				fprintf(stderr, "Try \"%s --help\" for more information.\n\n", progname);
270 				exit(1);
271 		}
272 	}
273 
274 	/*
275 	 * if we still have arguments, use it
276 	 */
277 	while (argc - optind >= 1)
278 	{
279 		if (current_app_type->app_type == PCP_PROC_INFO && processID <= 0)
280 		{
281 			processID = atoi(argv[optind]);
282 			if (processID <= 0)
283 			{
284 				fprintf(stderr, "%s: Invalid process-id \"%s\", must be greater than 0\n", progname, optarg);
285 				exit(0);
286 			}
287 		}
288 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO && nodeID < 0)
289 		{
290 			nodeID = atoi(argv[optind]);
291 			if (nodeID < 0)
292 			{
293 				fprintf(stderr, "%s: Invalid watchdog-id \"%s\", must be a positive number or zero for local watchdog node\n", progname, optarg);
294 				exit(0);
295 			}
296 		}
297 		else if (app_require_nodeID() && nodeID < 0)
298 		{
299 			nodeID = atoi(argv[optind]);
300 			if (nodeID < 0 || nodeID > MAX_NUM_BACKENDS)
301 			{
302 				fprintf(stderr, "%s: Invalid node-id \"%s\", must be between 0 and %d\n", progname, optarg, MAX_NUM_BACKENDS);
303 				exit(0);
304 			}
305 		}
306 		else
307 			fprintf(stderr, "%s: Warning: extra command-line argument \"%s\" ignored\n",
308 					progname, argv[optind]);
309 
310 		optind++;
311 	}
312 
313 	if (nodeID < 0)
314 	{
315 		if (app_require_nodeID())
316 		{
317 			fprintf(stderr, "%s: missing node-id\n", progname);
318 			fprintf(stderr, "Try \"%s --help\" for more information.\n\n", progname);
319 			exit(1);
320 		}
321 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
322 		{
323 			nodeID = -1;
324 		}
325 	}
326 
327 	/* Get a new password if appropriate */
328 	if (need_password)
329 	{
330 		pass = simple_prompt("Password: ", 100, false);
331 		need_password = false;
332 	}
333 
334 	pcpConn = pcp_connect(host, port, user, pass, debug ? stdout : NULL);
335 	if (PCPConnectionStatus(pcpConn) != PCP_CONNECTION_OK)
336 	{
337 		fprintf(stderr, "%s\n", pcp_get_last_error(pcpConn) ? pcp_get_last_error(pcpConn) : "Unknown Error");
338 		exit(1);
339 	}
340 
341 	/*
342 	 * Okay the connection is successful not call the actual PCP function
343 	 */
344 	if (current_app_type->app_type == PCP_ATTACH_NODE)
345 	{
346 		pcpResInfo = pcp_attach_node(pcpConn, nodeID);
347 	}
348 
349 	else if (current_app_type->app_type == PCP_DETACH_NODE)
350 	{
351 		if (gracefully)
352 			pcpResInfo = pcp_detach_node_gracefully(pcpConn, nodeID);
353 		else
354 			pcpResInfo = pcp_detach_node(pcpConn, nodeID);
355 	}
356 
357 	else if (current_app_type->app_type == PCP_NODE_COUNT)
358 	{
359 		pcpResInfo = pcp_node_count(pcpConn);
360 	}
361 
362 	else if (current_app_type->app_type == PCP_NODE_INFO)
363 	{
364 		pcpResInfo = pcp_node_info(pcpConn, nodeID);
365 	}
366 
367 	else if (current_app_type->app_type == PCP_POOL_STATUS)
368 	{
369 		pcpResInfo = pcp_pool_status(pcpConn);
370 	}
371 
372 	else if (current_app_type->app_type == PCP_PROC_COUNT)
373 	{
374 		pcpResInfo = pcp_process_count(pcpConn);
375 	}
376 
377 	else if (current_app_type->app_type == PCP_PROC_INFO)
378 	{
379 		pcpResInfo = pcp_process_info(pcpConn, processID);
380 	}
381 
382 	else if (current_app_type->app_type == PCP_PROMOTE_NODE)
383 	{
384 		if (gracefully)
385 			pcpResInfo = pcp_promote_node_gracefully(pcpConn, nodeID);
386 		else
387 			pcpResInfo = pcp_promote_node(pcpConn, nodeID);
388 	}
389 
390 	else if (current_app_type->app_type == PCP_RECOVERY_NODE)
391 	{
392 		pcpResInfo = pcp_recovery_node(pcpConn, nodeID);
393 	}
394 
395 	else if (current_app_type->app_type == PCP_STOP_PGPOOL)
396 	{
397 		pcpResInfo = pcp_terminate_pgpool(pcpConn, shutdown_mode);
398 	}
399 
400 	else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
401 	{
402 		pcpResInfo = pcp_watchdog_info(pcpConn, nodeID);
403 	}
404 
405 	else
406 	{
407 		/* should never happen */
408 		fprintf(stderr, "%s: Invalid pcp process\n", progname);
409 		goto DISCONNECT_AND_EXIT;
410 	}
411 
412 	if (pcpResInfo == NULL || PCPResultStatus(pcpResInfo) != PCP_RES_COMMAND_OK)
413 	{
414 		fprintf(stderr, "%s\n", pcp_get_last_error(pcpConn) ? pcp_get_last_error(pcpConn) : "Unknown Error");
415 		goto DISCONNECT_AND_EXIT;
416 	}
417 
418 	if (pcp_result_is_empty(pcpResInfo))
419 	{
420 		fprintf(stdout, "%s -- Command Successful\n", progname);
421 	}
422 	else
423 	{
424 		if (current_app_type->app_type == PCP_NODE_COUNT)
425 			output_nodecount_result(pcpResInfo, verbose);
426 
427 		if (current_app_type->app_type == PCP_NODE_INFO)
428 			output_nodeinfo_result(pcpResInfo, verbose);
429 
430 		if (current_app_type->app_type == PCP_POOL_STATUS)
431 			output_poolstatus_result(pcpResInfo, verbose);
432 
433 		if (current_app_type->app_type == PCP_PROC_COUNT)
434 			output_proccount_result(pcpResInfo, verbose);
435 
436 		if (current_app_type->app_type == PCP_PROC_INFO)
437 			output_procinfo_result(pcpResInfo, all, verbose);
438 
439 		else if (current_app_type->app_type == PCP_WATCHDOG_INFO)
440 			output_watchdog_info_result(pcpResInfo, verbose);
441 	}
442 
443 DISCONNECT_AND_EXIT:
444 
445 	pcp_disconnect(pcpConn);
446 	pcp_free_connection(pcpConn);
447 
448 	return 0;
449 }
450 
451 static void
output_nodecount_result(PCPResultInfo * pcpResInfo,bool verbose)452 output_nodecount_result(PCPResultInfo * pcpResInfo, bool verbose)
453 {
454 	if (verbose)
455 	{
456 		printf("Node Count\n");
457 		printf("____________\n");
458 		printf(" %d\n", pcp_get_int_data(pcpResInfo, 0));
459 	}
460 	else
461 		printf("%d\n", pcp_get_int_data(pcpResInfo, 0));
462 }
463 
464 static void
output_nodeinfo_result(PCPResultInfo * pcpResInfo,bool verbose)465 output_nodeinfo_result(PCPResultInfo * pcpResInfo, bool verbose)
466 {
467 	BackendInfo *backend_info = (BackendInfo *) pcp_get_binary_data(pcpResInfo, 0);
468 	char		last_status_change[20];
469 	struct tm	tm;
470 
471 	localtime_r(&backend_info->status_changed_time, &tm);
472 	strftime(last_status_change, sizeof(last_status_change), "%F %T", &tm);
473 
474 	if (verbose)
475 	{
476 		printf("Hostname          : %s\nPort              : %d\nStatus            : %d\nWeight            : %f\nStatus Name       : %s\nRole              : %s\nReplication Delay : %lu\nLast Status Change: %s\n",
477 			   backend_info->backend_hostname,
478 			   backend_info->backend_port,
479 			   backend_info->backend_status,
480 			   backend_info->backend_weight / RAND_MAX,
481 			   backend_status_to_string(backend_info),
482 			   role_to_str(backend_info->role),
483 			   backend_info->standby_delay,
484 			   last_status_change);
485 	}
486 	else
487 	{
488 		printf("%s %d %d %f %s %s %lu %s\n",
489 			   backend_info->backend_hostname,
490 			   backend_info->backend_port,
491 			   backend_info->backend_status,
492 			   backend_info->backend_weight / RAND_MAX,
493 			   backend_status_to_string(backend_info),
494 			   role_to_str(backend_info->role),
495 			   backend_info->standby_delay,
496 			   last_status_change);
497 	}
498 }
499 
500 static void
output_poolstatus_result(PCPResultInfo * pcpResInfo,bool verbose)501 output_poolstatus_result(PCPResultInfo * pcpResInfo, bool verbose)
502 {
503 	POOL_REPORT_CONFIG *status;
504 	int			i;
505 	int			array_size = pcp_result_slot_count(pcpResInfo);
506 
507 	if (verbose)
508 	{
509 		for (i = 0; i < array_size; i++)
510 		{
511 			status = (POOL_REPORT_CONFIG *) pcp_get_binary_data(pcpResInfo, i);
512 			printf("Name [%3d]:\t%s\n", i, status ? status->name : "NULL");
513 			printf("Value:      \t%s\n", status ? status->value : "NULL");
514 			printf("Description:\t%s\n\n", status ? status->desc : "NULL");
515 		}
516 	}
517 	else
518 	{
519 		for (i = 0; i < array_size; i++)
520 		{
521 			status = (POOL_REPORT_CONFIG *) pcp_get_binary_data(pcpResInfo, i);
522 			if (status == NULL)
523 			{
524 				printf("****Data at %d slot is NULL\n", i);
525 				continue;
526 			}
527 			printf("name : %s\nvalue: %s\ndesc : %s\n\n", status->name, status->value, status->desc);
528 		}
529 	}
530 }
531 
532 static void
output_proccount_result(PCPResultInfo * pcpResInfo,bool verbose)533 output_proccount_result(PCPResultInfo * pcpResInfo, bool verbose)
534 {
535 	int			i;
536 	int			process_count = pcp_get_data_length(pcpResInfo, 0) / sizeof(int);
537 	int		   *process_list = (int *) pcp_get_binary_data(pcpResInfo, 0);
538 
539 	if (verbose)
540 	{
541 		printf("No \t | \t PID\n");
542 		printf("_____________________\n");
543 		for (i = 0; i < process_count; i++)
544 			printf("%d \t | \t %d\n", i, process_list[i]);
545 		printf("\nTotal Processes:%d\n", process_count);
546 	}
547 	else
548 	{
549 		for (i = 0; i < process_count; i++)
550 			printf("%d ", process_list[i]);
551 		printf("\n");
552 	}
553 }
554 
555 static void
output_procinfo_result(PCPResultInfo * pcpResInfo,bool all,bool verbose)556 output_procinfo_result(PCPResultInfo * pcpResInfo, bool all, bool verbose)
557 {
558 	bool		printed = false;
559 	int			i;
560 	char	   *frmt;
561 	char		strcreatetime[128];
562 	char		strstarttime[128];
563 	int			array_size = pcp_result_slot_count(pcpResInfo);
564 
565 	if (verbose)
566 	{
567 		frmt = "Database     : %s\n"
568 			"Username     : %s\n"
569 			"Start time   : %s\n"
570 			"Creation time: %s\n"
571 			"Major        : %d\n"
572 			"Minor        : %d\n"
573 			"Counter      : %d\n"
574 			"Backend PID  : %d\n"
575 			"Connected    : %d\n"
576 			"PID          : %d\n"
577 			"Backend ID   : %d\n";
578 	}
579 	else
580 	{
581 		frmt = "%s %s %s %s %d %d %d %d %d %d %d\n";
582 	}
583 
584 	for (i = 0; i < array_size; i++)
585 	{
586 
587 		ProcessInfo *process_info = (ProcessInfo *) pcp_get_binary_data(pcpResInfo, i);
588 
589 		if (process_info == NULL)
590 			break;
591 		if ((!all) && (process_info->connection_info->database[0] == '\0'))
592 			continue;
593 		printed = true;
594 		*strcreatetime = *strstarttime = '\0';
595 
596 		if (process_info->start_time)
597 			strftime(strstarttime, 128, "%Y-%m-%d %H:%M:%S", localtime(&process_info->start_time));
598 		if (process_info->connection_info->create_time)
599 			strftime(strcreatetime, 128, "%Y-%m-%d %H:%M:%S", localtime(&process_info->connection_info->create_time));
600 
601 		printf(frmt,
602 			   process_info->connection_info->database,
603 			   process_info->connection_info->user,
604 			   strstarttime,
605 			   strcreatetime,
606 			   process_info->connection_info->major,
607 			   process_info->connection_info->minor,
608 			   process_info->connection_info->counter,
609 			   process_info->connection_info->pid,
610 			   process_info->connection_info->connected,
611 			   process_info->pid,
612 			   process_info->connection_info->backend_id);
613 	}
614 	if (printed == false)
615 		printf("No process information available\n\n");
616 }
617 
618 static void
output_watchdog_info_result(PCPResultInfo * pcpResInfo,bool verbose)619 output_watchdog_info_result(PCPResultInfo * pcpResInfo, bool verbose)
620 {
621 	int			i;
622 	PCPWDClusterInfo *cluster = (PCPWDClusterInfo *) pcp_get_binary_data(pcpResInfo, 0);
623 
624 	if (verbose)
625 	{
626 		char	   *quorumStatus;
627 
628 		if (cluster->quorumStatus == 0)
629 			quorumStatus = "QUORUM IS ON THE EDGE";
630 		else if (cluster->quorumStatus == 1)
631 			quorumStatus = "QUORUM EXIST";
632 		else if (cluster->quorumStatus == -1)
633 			quorumStatus = "QUORUM ABSENT";
634 		else if (cluster->quorumStatus == -2)
635 			quorumStatus = "NO MASTER NODE";
636 		else
637 			quorumStatus = "UNKNOWN";
638 
639 		printf("Watchdog Cluster Information \n");
640 		printf("Total Nodes          : %d\n", cluster->remoteNodeCount + 1);
641 		printf("Remote Nodes         : %d\n", cluster->remoteNodeCount);
642 		printf("Quorum state         : %s\n", quorumStatus);
643 		printf("Alive Remote Nodes   : %d\n", cluster->aliveNodeCount);
644 		printf("VIP up on local node : %s\n", cluster->escalated ? "YES" : "NO");
645 		printf("Master Node Name     : %s\n", cluster->masterNodeName);
646 		printf("Master Host Name     : %s\n\n", cluster->masterHostName);
647 
648 		printf("Watchdog Node Information \n");
649 		for (i = 0; i < cluster->nodeCount; i++)
650 		{
651 			PCPWDNodeInfo *watchdog_info = &cluster->nodeList[i];
652 
653 			printf("Node Name      : %s\n", watchdog_info->nodeName);
654 			printf("Host Name      : %s\n", watchdog_info->hostName);
655 			printf("Delegate IP    : %s\n", watchdog_info->delegate_ip);
656 			printf("Pgpool port    : %d\n", watchdog_info->pgpool_port);
657 			printf("Watchdog port  : %d\n", watchdog_info->wd_port);
658 			printf("Node priority  : %d\n", watchdog_info->wd_priority);
659 			printf("Status         : %d\n", watchdog_info->state);
660 			printf("Status Name    : %s\n\n", watchdog_info->stateName);
661 		}
662 	}
663 	else
664 	{
665 		printf("%d %s %s %s\n\n",
666 			   cluster->remoteNodeCount + 1,
667 			   cluster->escalated ? "YES" : "NO",
668 			   cluster->masterNodeName,
669 			   cluster->masterHostName);
670 
671 		for (i = 0; i < cluster->nodeCount; i++)
672 		{
673 			PCPWDNodeInfo *watchdog_info = &cluster->nodeList[i];
674 
675 			printf("%s %s %d %d %d %s\n",
676 				   watchdog_info->nodeName,
677 				   watchdog_info->hostName,
678 				   watchdog_info->pgpool_port,
679 				   watchdog_info->wd_port,
680 				   watchdog_info->state,
681 				   watchdog_info->stateName);
682 		}
683 	}
684 }
685 
686 /* returns true if the current application requires node id argument */
687 static inline bool
app_require_nodeID(void)688 app_require_nodeID(void)
689 {
690 	return (current_app_type->app_type == PCP_ATTACH_NODE ||
691 			current_app_type->app_type == PCP_DETACH_NODE ||
692 			current_app_type->app_type == PCP_NODE_INFO ||
693 			current_app_type->app_type == PCP_PROMOTE_NODE ||
694 			current_app_type->app_type == PCP_RECOVERY_NODE);
695 }
696 
697 
698 static void
usage(void)699 usage(void)
700 {
701 	fprintf(stderr, "%s - %s\n", progname, current_app_type->description);
702 	fprintf(stderr, "Usage:\n");
703 	fprintf(stderr, "%s [OPTION...] %s\n", progname,
704 			app_require_nodeID() ? "[node-id]" :
705 			(current_app_type->app_type == PCP_WATCHDOG_INFO) ? "[watchdog-id]" :
706 			(current_app_type->app_type == PCP_PROC_INFO) ? "[process-id]" : "");
707 
708 	fprintf(stderr, "Options:\n");
709 
710 	/*
711 	 * print the command options
712 	 */
713 	fprintf(stderr, "  -U, --username=NAME    username for PCP authentication\n");
714 	fprintf(stderr, "  -h, --host=HOSTNAME    pgpool-II host\n");
715 	fprintf(stderr, "  -p, --port=PORT        PCP port number\n");
716 	fprintf(stderr, "  -w, --no-password      never prompt for password\n");
717 	fprintf(stderr, "  -W, --password         force password prompt (should happen automatically)\n");
718 
719 	/*
720 	 * Now the options not available for all utilities
721 	 */
722 	if (app_require_nodeID())
723 	{
724 		fprintf(stderr, "  -n, --node-id=NODEID   ID of a backend node\n");
725 	}
726 
727 	if (current_app_type->app_type == PCP_STOP_PGPOOL)
728 	{
729 		fprintf(stderr, "  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n");
730 	}
731 	if (current_app_type->app_type == PCP_PROMOTE_NODE ||
732 		current_app_type->app_type == PCP_DETACH_NODE)
733 	{
734 		fprintf(stderr, "  -g, --gracefully       promote gracefully(optional)\n");
735 	}
736 
737 	if (current_app_type->app_type == PCP_WATCHDOG_INFO)
738 	{
739 		fprintf(stderr, "  -n, --watchdog-id=ID   ID of a other pgpool to get information for\n");
740 		fprintf(stderr, "                         ID 0 for the local watchdog\n");
741 		fprintf(stderr, "                         If omitted then get information of all watchdog nodes\n");
742 	}
743 	if (current_app_type->app_type == PCP_PROC_INFO)
744 	{
745 		fprintf(stderr, "  -P, --process-id=PID   PID of the child process to get information for (optional)\n");
746 		fprintf(stderr, "  -a, --all              display all child processes and their available connection slots\n");
747 	}
748 
749 	fprintf(stderr, "  -d, --debug            enable debug message (optional)\n");
750 	fprintf(stderr, "  -v, --verbose          output verbose messages\n");
751 	fprintf(stderr, "  -?, --help             print this help\n\n");
752 }
753 
754 
755 /*
756  * Extracts the actual name of the program as called -
757  * stripped of .exe suffix if any
758  */
759 const char *
get_progname(const char * argv0)760 get_progname(const char *argv0)
761 {
762 	const char *nodir_name;
763 	char	   *progname;
764 
765 	nodir_name = last_dir_separator(argv0);
766 	if (nodir_name)
767 		nodir_name++;
768 	else
769 		nodir_name = argv0;
770 
771 	/*
772 	 * Make a copy in case argv[0] is modified by ps_status. Leaks memory, but
773 	 * called only once.
774 	 */
775 	progname = strdup(nodir_name);
776 	if (progname == NULL)
777 	{
778 		fprintf(stderr, "%s: out of memory\n", nodir_name);
779 		exit(1);				/* This could exit the postmaster */
780 	}
781 
782 	return progname;
783 }
784 
785 /*
786  * Translate the BACKEND_STATUS enum value to string.
787  * the function returns the constant string so should not be freed
788  */
789 static char *
backend_status_to_string(BackendInfo * bi)790 backend_status_to_string(BackendInfo * bi)
791 {
792 	char	   *statusName;
793 
794 	switch (bi->backend_status)
795 	{
796 
797 		case CON_UNUSED:
798 			statusName = BACKEND_STATUS_CON_UNUSED;
799 			break;
800 
801 		case CON_CONNECT_WAIT:
802 			statusName = BACKEND_STATUS_CON_CONNECT_WAIT;
803 			break;
804 
805 		case CON_UP:
806 			statusName = BACKEND_STATUS_CON_UP;
807 			break;
808 
809 		case CON_DOWN:
810 			{
811 				if (bi->quarantine)
812 					statusName = BACKEND_STATUS_QUARANTINE;
813 				else
814 					statusName = BACKEND_STATUS_CON_DOWN;
815 			}
816 			break;
817 
818 		default:
819 			statusName = "unknown";
820 			break;
821 	}
822 	return statusName;
823 }
824 
825 /* Convert enum role to string */
826 char *
role_to_str(SERVER_ROLE role)827 role_to_str(SERVER_ROLE role)
828 {
829 	static char *role_str[] = {"master", "slave", "primary", "standby"};
830 
831 	if (role < ROLE_MASTER || role > ROLE_STANDBY)
832 		return "unknown";
833 	return role_str[role];
834 }
835