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