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