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