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