1 /*
2  * $Header$
3  *
4  * Provides CLI interface to interact with Pgpool-II watchdog
5  *
6  * pgpool: a language independent connection pool server for PostgreSQL
7  * written by Tatsuo Ishii
8  *
9  * Copyright (c) 2003-2021	PgPool Global Development Group
10  *
11  * Permission to use, copy, modify, and distribute this software and
12  * its documentation for any purpose and without fee is hereby
13  * granted, provided that the above copyright notice appear in all
14  * copies and that both that copyright notice and this permission
15  * notice appear in supporting documentation, and that the name of the
16  * author not be used in advertising or publicity pertaining to
17  * distribution of the software without specific, written prior
18  * permission. The author makes no representations about the
19  * suitability of this software for any purpose.  It is provided "as
20  * is" without express or implied warranty.
21  *
22  */
23 #include <stdio.h>
24 #include <errno.h>
25 #include <ctype.h>
26 #include <time.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <netdb.h>
31 #include <sys/wait.h>
32 
33 #include "pool.h"
34 #include "pool_config.h"
35 #include "version.h"
36 #include "parser/stringinfo.h"
37 #include "utils/json.h"
38 #include "utils/json_writer.h"
39 #include "utils/fe_ports.h"
40 
41 #include "watchdog/wd_ipc_conn.h"
42 #include "watchdog/wd_lifecheck.h"
43 #include "watchdog/wd_commands.h"
44 
45 #ifdef HAVE_GETOPT_H
46 #include <getopt.h>
47 #else
48 #include "utils/getopt_long.h"
49 #endif
50 
51 
52 #define LIFECHECK_GETNODE_WAIT_SEC_COUNT 5	/* max number of seconds the
53 											 * lifecheck process should waits
54 											 * before giving up while fetching
55 											 * the configured watchdog node
56 											 * information from watchdog
57 											 * process through IPC channel */
58 
59 const char *progname = NULL;
60 LifeCheckCluster *gslifeCheckCluster = NULL;
61 
62 
63 static void usage(void);
64 static bool validate_number(char* ptr);
65 const char *get_progname(const char *argv0);
66 static void print_lifecheck_cluster(bool include_nodes, bool verbose);
67 static void print_node_info(LifeCheckNode* lifeCheckNode, bool verbose);
68 
69 static bool fetch_watchdog_nodes_data(char *authkey, bool debug);
70 static bool inform_node_is_alive(LifeCheckNode * node, char *message, char* authkey);
71 static bool inform_node_is_dead(LifeCheckNode * node, char *message, char* authkey);
72 
73 static void load_watchdog_nodes_from_json(char *json_data, int len);
74 static char *get_node_status_change_json(int nodeID, int nodeStatus, char *message, char *authKey);
75 
76 static void print_node_info(LifeCheckNode* lifeCheckNode, bool verbose);
77 static LifeCheckNode* get_node_by_options(char *node_name, char* node_host, int node_port, int node_id);
78 
79 int
main(int argc,char ** argv)80 main(int argc, char **argv)
81 {
82 	LifeCheckNode* lifeCheckNode;
83 	char*		conf_file_path = NULL;
84 	char*		node_host = NULL;
85 	char*		node_name = NULL;
86 	char*		wd_authkey = NULL;
87 	char*		socket_dir = NULL;
88 	char*		message = NULL;
89 	int			node_wd_port = -1;
90 	int			node_id = -1;
91 	int			port = -1;
92 	int			ch;
93 	int			optindex;
94 	bool		debug = false;
95 	bool		info_req = false;
96 	bool		inform_status = false;
97 	bool		verbose = false;
98 	bool		all_nodes = false;
99 	bool		status_ALIVE = false;
100 	bool		status_DEAD = false;
101 
102 	/* here we put all the allowed long options for all utilities */
103 	static struct option long_options[] = {
104 		{"help", no_argument, NULL, '?'},
105 		{"all", no_argument, NULL, 'a'},
106 		{"debug", no_argument, NULL, 'd'},
107 		{"config-file", required_argument, NULL, 'f'},
108 		{"node-host", required_argument, NULL, 'H'},
109 		{"info", no_argument, NULL, 'i'},
110 		{"inform", required_argument, NULL, 'I'},
111 		{"auth-key", required_argument, NULL, 'k'},
112 		{"message", required_argument, NULL, 'm'},
113 		{"node-id", required_argument, NULL, 'n'},
114 		{"node-name", required_argument, NULL, 'N'},
115 		{"node-port", required_argument, NULL, 'P'},
116 		{"ipc-port", required_argument, NULL, 'p'},
117 		{"socket-dir", required_argument, NULL, 's'},
118 		{"version", no_argument, NULL, 'V'},
119 		{"verbose", no_argument, NULL, 'v'},
120 		{NULL, 0, NULL, 0}
121 	};
122 
123 	/* Identify the utility app */
124 	progname = get_progname(argv[0]);
125 
126 	if (argc > 1)
127 	{
128 		if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
129 		{
130 			usage();
131 			exit(0);
132 		}
133 		else if (strcmp(argv[1], "-V") == 0
134 				 || strcmp(argv[1], "--version") == 0)
135 		{
136 			fprintf(stderr, "%s (%s) %s\n", progname, PACKAGE, VERSION);
137 			exit(0);
138 		}
139 	}
140 
141 	while ((ch = getopt_long(argc, argv, "?aAdDf:H:iI:k:m:n:N:p:P:s:Vv", long_options, &optindex)) != -1)
142 	{
143 		switch (ch)
144 		{
145 			case 'd':
146 				debug = true;
147 				break;
148 
149 			case 'a':
150 				all_nodes = true;
151 				break;
152 
153 			case 'i': /* Info Request */
154 			{
155 				info_req = true;
156 				if (inform_status)
157 				{
158 					fprintf(stderr, "ERROR: Invalid option, 'info' and 'inform' are mutually exclusive options\n");
159 					exit(EXIT_FAILURE);
160 				}
161 			}
162 				break;
163 
164 			case 'I':
165 				if (!optarg)
166 				{
167 					usage();
168 					exit(EXIT_FAILURE);
169 				}
170 				inform_status = true;
171 				if (info_req)
172 				{
173 					fprintf(stderr, "ERROR: Invalid option, 'info' and 'inform' are mutually exclusive options\n");
174 					exit(EXIT_FAILURE);
175 				}
176 				if (strcasecmp("DEAD",optarg) == 0)
177 					status_DEAD = true;
178 				else if (strcasecmp("ALIVE",optarg) == 0)
179 					status_ALIVE = true;
180 				else
181 				{
182 					fprintf(stderr, "ERROR: Invalid node status \"%s\", Allowed options are DEAD or ALIVE''\n",optarg);
183 					exit(EXIT_FAILURE);
184 				}
185 				break;
186 
187 			case 'n':
188 				if (!optarg)
189 				{
190 					usage();
191 					exit(EXIT_FAILURE);
192 				}
193 				if (validate_number(optarg) == false)
194 				{
195 					fprintf(stderr, "ERROR: Invalid value %s, node-id can only contain numeric values\n",optarg);
196 					exit(EXIT_FAILURE);
197 				}
198 				node_id = atoi(optarg);
199 				break;
200 
201 			case 'H':
202 				if (!optarg)
203 				{
204 					usage();
205 					exit(EXIT_FAILURE);
206 				}
207 				node_host = pstrdup(optarg);
208 				break;
209 
210 			case 'N':
211 				if (!optarg)
212 				{
213 					usage();
214 					exit(EXIT_FAILURE);
215 				}
216 				node_name = pstrdup(optarg);
217 				break;
218 
219 			case 'P':
220 				if (!optarg)
221 				{
222 					usage();
223 					exit(EXIT_FAILURE);
224 				}
225 				if (validate_number(optarg) == false)
226 				{
227 					fprintf(stderr, "ERROR: Invalid value %s, node-port can only contain numeric values\n",optarg);
228 					exit(EXIT_FAILURE);
229 				}
230 				node_wd_port = atoi(optarg);
231 				break;
232 
233 			case 's':			/* socket dir */
234 				if (!optarg)
235 				{
236 					usage();
237 					exit(EXIT_FAILURE);
238 				}
239 				socket_dir = pstrdup(socket_dir);
240 				break;
241 
242 			case 'm':			/* message */
243 				if (!optarg)
244 				{
245 					usage();
246 					exit(EXIT_FAILURE);
247 				}
248 				message = pstrdup(optarg);
249 				break;
250 
251 			case 'f':			/* specify configuration file */
252 				if (!optarg)
253 				{
254 					usage();
255 					exit(EXIT_FAILURE);
256 				}
257 				conf_file_path = pstrdup(optarg);
258 				break;
259 
260 			case 'k':			/* specify authkey */
261 				if (!optarg)
262 				{
263 					usage();
264 					exit(EXIT_FAILURE);
265 				}
266 				wd_authkey = pstrdup(optarg);
267 				break;
268 
269 			case 'v':
270 				verbose = true;
271 				break;
272 
273 			case 'V':
274 				fprintf(stderr, "%s for %s version %s (%s),\n", progname, PACKAGE, VERSION, PGPOOLVERSION);
275 				exit(0);
276 				break;
277 
278 			case 'p':
279 				if (validate_number(optarg) == false)
280 				{
281 					fprintf(stderr, "ERROR: Invalid value %s, port can only contain numeric values\n",optarg);
282 					exit(EXIT_FAILURE);
283 				}
284 				port = atoi(optarg);
285 				if (port <= 1024 || port > 65535)
286 				{
287 					fprintf(stderr, "ERROR: Invalid port number \"%s\", must be between 1024 and 65535\n", optarg);
288 					exit(0);
289 				}
290 				break;
291 
292 			case '?':
293 			default:
294 
295 				/*
296 				 * getopt_long should already have emitted a complaint
297 				 */
298 				fprintf(stderr, "Try \"%s --help\" for more information.\n\n", progname);
299 				exit(EXIT_FAILURE);
300 		}
301 	}
302 
303 	if (!info_req && !inform_status)
304 	{
305 		fprintf(stderr, "ERROR: Missing operation. Try %s --help\" for more information.\n\n", progname);
306 		exit(EXIT_FAILURE);
307 	}
308 
309 	if (inform_status && all_nodes)
310 	{
311 		if (all_nodes)
312 		{
313 			fprintf(stderr, "ERROR: Invalid option \"-a --all\" for inform status operation. Try %s --help\" for more information.\n\n", progname);
314 			exit(EXIT_FAILURE);
315 		}
316 		if (node_name == NULL && node_host == NULL && node_wd_port < 0 && node_id < 0)
317 		{
318 			fprintf(stderr, "ERROR: Missing node search criteria. Try %s --help\" for more information.\n\n", progname);
319 			exit(EXIT_FAILURE);
320 		}
321 	}
322 
323 	if (conf_file_path)
324 	{
325 		if (pool_init_config())
326 		{
327 			fprintf(stderr, "pool_init_config() failed\n\n");
328 			exit(EXIT_FAILURE);
329 		}
330 		if (pool_get_config(conf_file_path, CFGCXT_INIT) == false)
331 		{
332 			fprintf(stderr, "ERROR: Unable to get configuration. Exiting...\n\n");
333 			exit(EXIT_FAILURE);
334 		}
335 
336 		if (debug)
337 			printf("DEBUG: From config %s:%d\n",pool_config->wd_ipc_socket_dir,
338 					pool_config->wd_nodes.wd_node_info[pool_config->pgpool_node_id].wd_port);
339 
340 		pfree(conf_file_path);
341 		/* only use values from pg_config that are not provided explicitly*/
342 		if (wd_authkey == NULL)
343 			wd_authkey = pstrdup(pool_config->wd_authkey);
344 		if (port < 0)
345 			port = pool_config->wd_nodes.wd_node_info[pool_config->pgpool_node_id].wd_port;
346 		if (socket_dir == NULL)
347 			socket_dir = pstrdup(pool_config->wd_ipc_socket_dir);
348 	}
349 
350 	if (port < 0)
351 		port = 9898;
352 
353 	if (socket_dir == NULL)
354 		socket_dir = pstrdup("/tmp");
355 
356 	if(debug)
357 	{
358 		fprintf(stderr, "DEBUG: setting IPC address to %s:%d\n",socket_dir,port);
359 	}
360 
361 	wd_set_ipc_address(socket_dir,port);
362 	wd_ipc_conn_initialize();
363 
364 	if(debug)
365 	{
366 		char c_node_id[10],c_wd_port[10];
367 		snprintf(c_node_id, sizeof(c_node_id), "%d",node_id);
368 		snprintf(c_wd_port, sizeof(c_wd_port), "%d",node_wd_port);
369 
370 		fprintf(stderr, "DEBUG: OPERATION:%s ALL NODE CRITERIA = %s\n",
371 				info_req?"\"INFO REQUEST\"":"\"INFORM NODE STATUS\"",
372 				all_nodes?"TRUE":"FALSE"
373 				);
374 		fprintf(stderr, "DEBUG: Search criteria:[ID=%s AND Name=%s AND Host=%s AND WDPort=%s]\n",
375 				(node_id < 0)?"ANY":c_node_id,
376 				node_name?node_name:"ANY",
377 				node_host?node_host:"ANY",
378 				(node_wd_port < 0)?"ANY":c_wd_port);
379 	}
380 
381 	fetch_watchdog_nodes_data(wd_authkey, debug);
382 
383 	if (info_req)
384 	{
385 		if (all_nodes)
386 		{
387 			print_lifecheck_cluster(true, verbose);
388 			exit(0);
389 		}
390 		if (node_name == NULL && node_host == NULL && node_wd_port < 0 && node_id < 0)
391 		{
392 			fprintf(stderr, "WARNING: Missing node search criteria. applying --all option\n");
393 			print_lifecheck_cluster(true, verbose);
394 			exit(0);
395 		}
396 	}
397 
398 	lifeCheckNode = get_node_by_options(node_name, node_host, node_wd_port, node_id);
399 	if (!lifeCheckNode)
400 	{
401 		char c_node_id[10],c_wd_port[10];
402 		fprintf(stderr, "ERROR: unable to find the node with the requested criteria\n");
403 		snprintf(c_node_id, sizeof(c_node_id), "%d",node_id);
404 		snprintf(c_wd_port, sizeof(c_wd_port), "%d",node_wd_port);
405 		fprintf(stderr, "Criteria:[ID=%s AND Name=%s AND Host=%s AND WDPort=%s]\n",
406 				(node_id < 0)?"ANY":c_node_id,
407 				node_name?node_name:"ANY",
408 				node_host?node_host:"ANY",
409 				(node_wd_port < 0)?"ANY":c_wd_port);
410 		exit(EXIT_FAILURE);
411 
412 	}
413 	if (info_req)
414 	{
415 		print_lifecheck_cluster(false, verbose);
416 		print_node_info(lifeCheckNode, verbose);
417 		exit (0);
418 	}
419 
420 	if (status_DEAD)
421 	{
422 		if (inform_node_is_dead(lifeCheckNode, message, wd_authkey))
423 		{
424 			fprintf(stderr,"INFO: informed watchdog about node id %d is dead\n",node_id);
425 			exit(0);
426 		}
427 		fprintf(stderr,"ERROR: failed to inform watchdog about node id %d is dead\n",node_id);
428 		exit(EXIT_FAILURE);
429 	}
430 	else if (status_ALIVE)
431 	{
432 		if (inform_node_is_alive(lifeCheckNode, message, wd_authkey))
433 		{
434 			fprintf(stderr,"INFO: informed watchdog about node id %d is alive\n",node_id);
435 			exit(0);
436 		}
437 		fprintf(stderr,"ERROR: failed to inform watchdog about node id %d is alive\n",node_id);
438 		exit(EXIT_FAILURE);
439 	}
440 
441 	return 0;
442 }
443 
444 const char *
get_progname(const char * argv0)445 get_progname(const char *argv0)
446 {
447 	return "wd_cli";
448 }
449 
450 
451 static bool
validate_number(char * ptr)452 validate_number(char* ptr)
453 {
454 	while (*ptr)
455 	{
456 		if (isdigit(*ptr) == 0)
457 			return false;
458 		ptr++;
459 	}
460 	return true;
461 }
462 
463 static bool
inform_node_status(LifeCheckNode * node,char * message,char * authkey)464 inform_node_status(LifeCheckNode * node, char *message, char* authkey)
465 {
466 	int			node_status,
467 				x;
468 	char	   *json_data;
469 	WDIPCCmdResult *res = NULL;
470 	char	   *new_status;
471 
472 	if (node->nodeState == NODE_DEAD)
473 	{
474 		new_status = "NODE DEAD";
475 		node_status = WD_LIFECHECK_NODE_STATUS_DEAD;
476 	}
477 	else if (node->nodeState == NODE_ALIVE)
478 	{
479 		new_status = "NODE ALIVE";
480 		node_status = WD_LIFECHECK_NODE_STATUS_ALIVE;
481 	}
482 	else
483 		return false;
484 
485 	fprintf(stderr,"INFO: informing the node status change to watchdog");
486 	fprintf(stderr,"INFO: node id :%d status = \"%s\" message:\"%s\"", node->ID, new_status, message);
487 
488 	json_data = get_node_status_change_json(node->ID, node_status, message, authkey);
489 	if (json_data == NULL)
490 		return false;
491 
492 	for (x = 0; x < MAX_SEC_WAIT_FOR_CLUSTER_TRANSACTION; x++)
493 	{
494 		res = issue_command_to_watchdog(WD_NODE_STATUS_CHANGE_COMMAND, 0, json_data, strlen(json_data), false);
495 		if (res)
496 			break;
497 		sleep(1);
498 	}
499 	pfree(json_data);
500 	if (res)
501 	{
502 		pfree(res);
503 		return true;
504 	}
505 	return false;
506 }
507 
508 static bool
fetch_watchdog_nodes_data(char * authkey,bool debug)509 fetch_watchdog_nodes_data(char *authkey, bool debug)
510 {
511 	char	   *json_data = wd_get_watchdog_nodes_json(authkey, -1);
512 
513 	if (json_data == NULL)
514 	{
515 		ereport(ERROR,
516 				(errmsg("get node list command reply contains no data")));
517 		return false;
518 	}
519 
520 	if(debug)
521 		printf("DEBUG:************\n%s\n************\n",json_data);
522 
523 	load_watchdog_nodes_from_json(json_data, strlen(json_data));
524 	pfree(json_data);
525 	return true;
526 }
527 
528 static void
load_watchdog_nodes_from_json(char * json_data,int len)529 load_watchdog_nodes_from_json(char *json_data, int len)
530 {
531 	json_value *root;
532 	json_value *value;
533 	int			i,
534 				nodeCount;
535 
536 	root = json_parse(json_data, len);
537 
538 	/* The root node must be object */
539 	if (root == NULL || root->type != json_object)
540 	{
541 		json_value_free(root);
542 		ereport(ERROR,
543 				(errmsg("unable to parse json data for node list")));
544 	}
545 
546 	if (json_get_int_value_for_key(root, "NodeCount", &nodeCount))
547 	{
548 		json_value_free(root);
549 		ereport(ERROR,
550 				(errmsg("invalid json data"),
551 				 errdetail("unable to find NodeCount node from data")));
552 	}
553 
554 	/* find the WatchdogNodes array */
555 	value = json_get_value_for_key(root, "WatchdogNodes");
556 	if (value == NULL)
557 	{
558 		json_value_free(root);
559 		ereport(ERROR,
560 				(errmsg("invalid json data"),
561 				 errdetail("unable to find WatchdogNodes node from data")));
562 	}
563 	if (value->type != json_array)
564 	{
565 		json_value_free(root);
566 		ereport(ERROR,
567 				(errmsg("invalid json data"),
568 				 errdetail("WatchdogNodes node does not contains Array")));
569 	}
570 	if (nodeCount != value->u.array.length)
571 	{
572 		json_value_free(root);
573 		ereport(ERROR,
574 				(errmsg("invalid json data"),
575 				 errdetail("WatchdogNodes array contains %d nodes while expecting %d", value->u.array.length, nodeCount)));
576 	}
577 
578 	/* okay we are done, put this in shared memory */
579 	gslifeCheckCluster = malloc(sizeof(LifeCheckCluster));
580 	gslifeCheckCluster->nodeCount = nodeCount;
581 	gslifeCheckCluster->lifeCheckNodes = malloc(sizeof(LifeCheckNode) * gslifeCheckCluster->nodeCount);
582 	for (i = 0; i < nodeCount; i++)
583 	{
584 		WDNodeInfo *nodeInfo = parse_watchdog_node_info_from_wd_node_json(value->u.array.values[i]);
585 
586 		gslifeCheckCluster->lifeCheckNodes[i].nodeState = NODE_EMPTY;
587 		gslifeCheckCluster->lifeCheckNodes[i].wdState = nodeInfo->state;
588 		strcpy(gslifeCheckCluster->lifeCheckNodes[i].stateName, nodeInfo->stateName);
589 		gslifeCheckCluster->lifeCheckNodes[i].ID = nodeInfo->id;
590 		strcpy(gslifeCheckCluster->lifeCheckNodes[i].hostName, nodeInfo->hostName);
591 		strcpy(gslifeCheckCluster->lifeCheckNodes[i].nodeName, nodeInfo->nodeName);
592 		gslifeCheckCluster->lifeCheckNodes[i].wdPort = nodeInfo->wd_port;
593 		gslifeCheckCluster->lifeCheckNodes[i].pgpoolPort = nodeInfo->pgpool_port;
594 		gslifeCheckCluster->lifeCheckNodes[i].retry_lives = pool_config->wd_life_point;
595 		pfree(nodeInfo);
596 	}
597 	json_value_free(root);
598 }
599 
600 
601 static bool
inform_node_is_dead(LifeCheckNode * node,char * message,char * authkey)602 inform_node_is_dead(LifeCheckNode * node, char *message, char* authkey)
603 {
604 	node->nodeState = NODE_DEAD;
605 	return inform_node_status(node, message, authkey);
606 }
607 
608 static bool
inform_node_is_alive(LifeCheckNode * node,char * message,char * authkey)609 inform_node_is_alive(LifeCheckNode * node, char *message, char* authkey)
610 {
611 	node->nodeState = NODE_ALIVE;
612 	return inform_node_status(node, message, authkey);
613 }
614 
615 static LifeCheckNode*
get_node_by_options(char * node_name,char * node_host,int node_port,int node_id)616 get_node_by_options(char *node_name, char* node_host, int node_port, int node_id)
617 {
618 	int i;
619 	if (!gslifeCheckCluster)
620 		return NULL;
621 	for (i = 0; i < gslifeCheckCluster->nodeCount; i++)
622 	{
623 		if (node_id >= 0 && node_id != gslifeCheckCluster->lifeCheckNodes[i].ID)
624 			continue;
625 		if (node_port >= 0 && node_port != gslifeCheckCluster->lifeCheckNodes[i].wdPort)
626 			continue;
627 		if (node_name && strcasecmp(gslifeCheckCluster->lifeCheckNodes[i].nodeName, node_name) != 0 )
628 			continue;
629 		if (node_host && strcasecmp(gslifeCheckCluster->lifeCheckNodes[i].hostName, node_host) != 0 )
630 			continue;
631 
632 		return &gslifeCheckCluster->lifeCheckNodes[i];
633 	}
634 	return NULL;
635 }
636 
637 
638 static void
print_lifecheck_cluster(bool include_nodes,bool verbose)639 print_lifecheck_cluster(bool include_nodes, bool verbose)
640 {
641 	int			i;
642 	if (!gslifeCheckCluster)
643 	{
644 		fprintf(stdout,"ERROR: node information not found\n");
645 		return;
646 	}
647 	fprintf(stdout,"Total Watchdog nodes configured for lifecheck:    %d\n", gslifeCheckCluster->nodeCount);
648 	if (verbose)
649 		fprintf(stdout,"*****************\n");
650 	if(!include_nodes)
651 		return;
652 
653 	for (i = 0; i < gslifeCheckCluster->nodeCount; i++)
654 		print_node_info(&gslifeCheckCluster->lifeCheckNodes[i], verbose);
655 }
656 
657 
658 
659 static void
print_node_info(LifeCheckNode * lifeCheckNode,bool verbose)660 print_node_info(LifeCheckNode* lifeCheckNode, bool verbose)
661 {
662 	if (verbose)
663 	{
664 		fprintf(stdout,"Node ID:           %d\n",lifeCheckNode->ID);
665 		fprintf(stdout,"Node Status code:  %d\n",lifeCheckNode->wdState);
666 		fprintf(stdout,"Node Status:       %s\n",lifeCheckNode->stateName);
667 		fprintf(stdout,"Node Name:         %s\n",lifeCheckNode->nodeName);
668 		fprintf(stdout,"Node Host:         %s\n",lifeCheckNode->hostName);
669 		fprintf(stdout,"Node WD Port:      %d\n",lifeCheckNode->wdPort);
670 		fprintf(stdout,"Node Pgpool Port:  %d\n\n",lifeCheckNode->pgpoolPort);
671 	}
672 	else
673 	{
674 		fprintf(stdout,"%d %d \"%s\"", lifeCheckNode->ID,
675 				lifeCheckNode->nodeState,
676 				lifeCheckNode->stateName),
677 		fprintf(stdout,"\"%s\"",lifeCheckNode->nodeName),
678 		fprintf(stdout,"\"%s\" %d %d\n",
679 				lifeCheckNode->hostName,
680 				lifeCheckNode->wdPort,
681 				lifeCheckNode->pgpoolPort);
682 	}
683 }
684 
685 static char *
get_node_status_change_json(int nodeID,int nodeStatus,char * message,char * authKey)686 get_node_status_change_json(int nodeID, int nodeStatus, char *message, char *authKey)
687 {
688 	char	   *json_str;
689 	JsonNode   *jNode = jw_create_with_object(true);
690 
691 	if (authKey != NULL && strlen(authKey) > 0)
692 		jw_put_string(jNode, WD_IPC_AUTH_KEY, authKey); /* put the auth key */
693 
694 	/* add the node ID */
695 	jw_put_int(jNode, "NodeID", nodeID);
696 	/* add the node status */
697 	jw_put_int(jNode, "NodeStatus", nodeStatus);
698 	/* add the node message if any */
699 	if (message)
700 		jw_put_string(jNode, "Message", message);
701 
702 	jw_finish_document(jNode);
703 	json_str = pstrdup(jw_get_json_string(jNode));
704 	jw_destroy(jNode);
705 	return json_str;
706 }
707 
708 static void
usage(void)709 usage(void)
710 {
711 
712 	fprintf(stderr, "\nWatchdog CLI for ");
713 	fprintf(stderr, "%s version %s (%s)\n", PACKAGE, VERSION, PGPOOLVERSION);
714 
715 	fprintf(stderr, "\nUsage:\n");
716 	fprintf(stderr, "  %s [ operation] [ options] [node search criteria]\n",progname);
717 
718 	fprintf(stderr, "\n Operations:\n");
719 	fprintf(stderr, "  -i, --info                  Get the node status for nodes based on node search criteria\n");
720 	fprintf(stderr, "  -I, --inform=NEW-STATUS     Inform Pgpool-II about new watchdog node status\n");
721 	fprintf(stderr, "                              Allowed values are DEAD and ALIVE\n");
722 
723 	fprintf(stderr, "\n Node search options:\n");
724 
725 	fprintf(stderr, "  -a, --all                Search all nodes (only available with --info option)\n");
726 	fprintf(stderr, "  -n, --node-id=ID         Search watchdog node with node_id\n");
727 	fprintf(stderr, "  -N, --node-name=Name     Search watchdog node with name\n");
728 	fprintf(stderr, "  -H, --node-host=Host     Search watchdog node with Host\n");
729 	fprintf(stderr, "  -P, --node-port=port     Search watchdog node with wd_port\n");
730 
731 	fprintf(stderr, "\n Options:\n");
732 
733 	fprintf(stderr, "  -f, --config-file=CONFIG_FILE\n");
734 	fprintf(stderr, "                      Set the path to the pgpool.conf configuration file\n");
735 	fprintf(stderr, "  -k, --auth-key=KEY\n");
736 	fprintf(stderr, "                      watchdog auth key\n");
737 	fprintf(stderr, "                      This over rides the pgpool.conf->wd_authkey value\n");
738 	fprintf(stderr, "  -s, --socket-dir=WD_IPC_SOCKET_DIRECTORY\n");
739 	fprintf(stderr, "                      Path to the WD IPC socket directory\n");
740 	fprintf(stderr, "                      This over rides the pgpool.conf->wd_ipc_socket_dir value\n");
741 	fprintf(stderr, "  -p, --ipc-port=WD_IPC_PORT\n");
742 	fprintf(stderr, "                      Port number of watchdog IPC socket\n");
743 	fprintf(stderr, "                      This over rides the pgpool.conf->wd_port value\n");
744 	fprintf(stderr, "  -m, --message=message string\n");
745 	fprintf(stderr, "                      Message to be passed to Pgpool-II along with new node status\n");
746 	fprintf(stderr, "  -v, --verbose       Output verbose messages\n");
747 	fprintf(stderr, "  -V, --version       Output Version information\n");
748 	fprintf(stderr, "  -d, --debug         Enable debug output\n");
749 	fprintf(stderr, "  -h, --help          Print this help\n\n");
750 }
751