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