This source file includes following definitions.
- all_includes
- default_includes
- find_section_bit
- apply_exclude
- apply_include
- apply_include_exclude
- user_include_exclude_cb
- include_exclude_cb
- as_cgi_cb
- as_html_cb
- as_simple_cb
- as_xml_cb
- fence_history_cb
- group_by_node_cb
- hide_headers_cb
- inactive_resources_cb
- no_curses_cb
- print_brief_cb
- print_detail_cb
- print_timing_cb
- reconnect_cb
- show_attributes_cb
- show_bans_cb
- show_failcounts_cb
- show_operations_cb
- show_tickets_cb
- use_cib_file_cb
- reconnect_after_timeout
- mon_cib_connection_destroy
- mon_shutdown
- mon_winresize
- fencing_connect
- cib_connect
- set_fencing_options
- pacemakerd_event_cb
- pacemakerd_status
- get_option_desc
- detect_user_input
- avoid_zombies
- build_arg_context
- add_output_args
- reconcile_output_format
- clean_up_on_connection_failure
- one_shot
- exit_on_invalid_cib
- main
- send_custom_trap
- handle_rsc_op
- mon_trigger_refresh
- handle_op_for_node
- crm_diff_update_v2
- crm_diff_update_v1
- crm_diff_update
- mon_refresh_display
- mon_st_callback_event
- refresh_after_event
- mon_st_callback_display
- clean_up_fencing_connection
- clean_up
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13
14 #include <crm/crm.h>
15
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <libgen.h>
26 #include <signal.h>
27 #include <sys/utsname.h>
28
29 #include <crm/msg_xml.h>
30 #include <crm/services.h>
31 #include <crm/lrmd.h>
32 #include <crm/common/cmdline_internal.h>
33 #include <crm/common/internal.h>
34 #include <crm/common/ipc.h>
35 #include <crm/common/mainloop.h>
36 #include <crm/common/output.h>
37 #include <crm/common/output_internal.h>
38 #include <crm/common/results.h>
39 #include <crm/common/util.h>
40 #include <crm/common/xml.h>
41 #include <crm/common/xml_internal.h>
42
43 #include <crm/cib/internal.h>
44 #include <crm/pengine/status.h>
45 #include <crm/pengine/internal.h>
46 #include <pacemaker-internal.h>
47 #include <crm/stonith-ng.h>
48 #include <crm/fencing/internal.h>
49
50 #include "crm_mon.h"
51
52 #define SUMMARY "Provides a summary of cluster's current state.\n\n" \
53 "Outputs varying levels of detail in a number of different formats."
54
55
56
57
58
59 static uint32_t show;
60 static uint32_t show_opts = pcmk_show_pending;
61
62
63
64
65
66 static mon_output_format_t output_format = mon_output_unset;
67
68
69 static GIOChannel *io_channel = NULL;
70 static GMainLoop *mainloop = NULL;
71 static guint reconnect_timer = 0;
72 static mainloop_timer_t *refresh_timer = NULL;
73
74 static cib_t *cib = NULL;
75 static stonith_t *st = NULL;
76 static xmlNode *current_cib = NULL;
77
78 static GError *error = NULL;
79 static pcmk__common_args_t *args = NULL;
80 static pcmk__output_t *out = NULL;
81 static GOptionContext *context = NULL;
82 static gchar **processed_args = NULL;
83
84 static time_t last_refresh = 0;
85 volatile crm_trigger_t *refresh_trigger = NULL;
86
87 static enum pcmk__fence_history fence_history = pcmk__fence_history_none;
88 static gboolean on_remote_node = FALSE;
89 static gboolean use_cib_native = FALSE;
90
91 int interactive_fence_level = 0;
92
93 static pcmk__supported_format_t formats[] = {
94 #if CURSES_ENABLED
95 CRM_MON_SUPPORTED_FORMAT_CURSES,
96 #endif
97 PCMK__SUPPORTED_FORMAT_HTML,
98 PCMK__SUPPORTED_FORMAT_NONE,
99 PCMK__SUPPORTED_FORMAT_TEXT,
100 PCMK__SUPPORTED_FORMAT_XML,
101 { NULL, NULL, NULL }
102 };
103
104
105
106
107
108 #define MON_STATUS_WARN CRM_EX_ERROR
109 #define MON_STATUS_CRIT CRM_EX_INVALID_PARAM
110 #define MON_STATUS_UNKNOWN CRM_EX_UNIMPLEMENT_FEATURE
111
112 #define RECONNECT_MSECS 5000
113
114 struct {
115 guint reconnect_ms;
116 gboolean daemonize;
117 gboolean fence_connect;
118 gboolean one_shot;
119 gboolean print_pending;
120 gboolean show_bans;
121 gboolean watch_fencing;
122 char *pid_file;
123 char *external_agent;
124 char *external_recipient;
125 char *neg_location_prefix;
126 char *only_node;
127 char *only_rsc;
128 GSList *user_includes_excludes;
129 GSList *includes_excludes;
130 } options = {
131 .fence_connect = TRUE,
132 .reconnect_ms = RECONNECT_MSECS
133 };
134
135 static void clean_up_fencing_connection(void);
136 static crm_exit_t clean_up(crm_exit_t exit_code);
137 static void crm_diff_update(const char *event, xmlNode * msg);
138 static void clean_up_on_connection_failure(int rc);
139 static int mon_refresh_display(gpointer user_data);
140 static int cib_connect(void);
141 static int fencing_connect(void);
142 static int pacemakerd_status(void);
143 static void mon_st_callback_event(stonith_t * st, stonith_event_t * e);
144 static void mon_st_callback_display(stonith_t * st, stonith_event_t * e);
145 static void refresh_after_event(gboolean data_updated, gboolean enforce);
146
147 static uint32_t
148 all_includes(mon_output_format_t fmt) {
149 if (fmt == mon_output_monitor || fmt == mon_output_plain || fmt == mon_output_console) {
150 return ~pcmk_section_options;
151 } else {
152 return pcmk_section_all;
153 }
154 }
155
156 static uint32_t
157 default_includes(mon_output_format_t fmt) {
158 switch (fmt) {
159 case mon_output_monitor:
160 case mon_output_plain:
161 case mon_output_console:
162 return pcmk_section_summary | pcmk_section_nodes | pcmk_section_resources |
163 pcmk_section_failures;
164
165 case mon_output_xml:
166 case mon_output_legacy_xml:
167 return all_includes(fmt);
168
169 case mon_output_html:
170 case mon_output_cgi:
171 return pcmk_section_summary | pcmk_section_nodes | pcmk_section_resources |
172 pcmk_section_failures;
173
174 default:
175 return 0;
176 }
177 }
178
179 struct {
180 const char *name;
181 uint32_t bit;
182 } sections[] = {
183 { "attributes", pcmk_section_attributes },
184 { "bans", pcmk_section_bans },
185 { "counts", pcmk_section_counts },
186 { "dc", pcmk_section_dc },
187 { "failcounts", pcmk_section_failcounts },
188 { "failures", pcmk_section_failures },
189 { PCMK__VALUE_FENCING, pcmk_section_fencing_all },
190 { "fencing-failed", pcmk_section_fence_failed },
191 { "fencing-pending", pcmk_section_fence_pending },
192 { "fencing-succeeded", pcmk_section_fence_worked },
193 { "maint-mode", pcmk_section_maint_mode },
194 { "nodes", pcmk_section_nodes },
195 { "operations", pcmk_section_operations },
196 { "options", pcmk_section_options },
197 { "resources", pcmk_section_resources },
198 { "stack", pcmk_section_stack },
199 { "summary", pcmk_section_summary },
200 { "tickets", pcmk_section_tickets },
201 { "times", pcmk_section_times },
202 { NULL }
203 };
204
205 static uint32_t
206 find_section_bit(const char *name) {
207 for (int i = 0; sections[i].name != NULL; i++) {
208 if (pcmk__str_eq(sections[i].name, name, pcmk__str_casei)) {
209 return sections[i].bit;
210 }
211 }
212
213 return 0;
214 }
215
216 static gboolean
217 apply_exclude(const gchar *excludes, GError **error) {
218 char **parts = NULL;
219 gboolean result = TRUE;
220
221 parts = g_strsplit(excludes, ",", 0);
222 for (char **s = parts; *s != NULL; s++) {
223 uint32_t bit = find_section_bit(*s);
224
225 if (pcmk__str_eq(*s, "all", pcmk__str_none)) {
226 show = 0;
227 } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) {
228 show = all_includes(output_format);
229 } else if (bit != 0) {
230 show &= ~bit;
231 } else {
232 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
233 "--exclude options: all, attributes, bans, counts, dc, "
234 "failcounts, failures, fencing, fencing-failed, "
235 "fencing-pending, fencing-succeeded, maint-mode, nodes, "
236 PCMK__VALUE_NONE ", operations, options, resources, "
237 "stack, summary, tickets, times");
238 result = FALSE;
239 break;
240 }
241 }
242 g_strfreev(parts);
243 return result;
244 }
245
246 static gboolean
247 apply_include(const gchar *includes, GError **error) {
248 char **parts = NULL;
249 gboolean result = TRUE;
250
251 parts = g_strsplit(includes, ",", 0);
252 for (char **s = parts; *s != NULL; s++) {
253 uint32_t bit = find_section_bit(*s);
254
255 if (pcmk__str_eq(*s, "all", pcmk__str_none)) {
256 show = all_includes(output_format);
257 } else if (pcmk__starts_with(*s, "bans")) {
258 show |= pcmk_section_bans;
259 if (options.neg_location_prefix != NULL) {
260 free(options.neg_location_prefix);
261 options.neg_location_prefix = NULL;
262 }
263
264 if (strlen(*s) > 4 && (*s)[4] == ':') {
265 options.neg_location_prefix = strdup(*s+5);
266 }
267 } else if (pcmk__str_any_of(*s, "default", "defaults", NULL)) {
268 show |= default_includes(output_format);
269 } else if (pcmk__str_eq(*s, PCMK__VALUE_NONE, pcmk__str_none)) {
270 show = 0;
271 } else if (bit != 0) {
272 show |= bit;
273 } else {
274 g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
275 "--include options: all, attributes, bans[:PREFIX], counts, dc, "
276 "default, failcounts, failures, fencing, fencing-failed, "
277 "fencing-pending, fencing-succeeded, maint-mode, nodes, "
278 PCMK__VALUE_NONE ", operations, options, resources, "
279 "stack, summary, tickets, times");
280 result = FALSE;
281 break;
282 }
283 }
284 g_strfreev(parts);
285 return result;
286 }
287
288 static gboolean
289 apply_include_exclude(GSList *lst, GError **error) {
290 gboolean rc = TRUE;
291 GSList *node = lst;
292
293 while (node != NULL) {
294 char *s = node->data;
295
296 if (pcmk__starts_with(s, "--include=")) {
297 rc = apply_include(s+10, error);
298 } else if (pcmk__starts_with(s, "-I=")) {
299 rc = apply_include(s+3, error);
300 } else if (pcmk__starts_with(s, "--exclude=")) {
301 rc = apply_exclude(s+10, error);
302 } else if (pcmk__starts_with(s, "-U=")) {
303 rc = apply_exclude(s+3, error);
304 }
305
306 if (rc != TRUE) {
307 break;
308 }
309
310 node = node->next;
311 }
312
313 return rc;
314 }
315
316 static gboolean
317 user_include_exclude_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
318 char *s = crm_strdup_printf("%s=%s", option_name, optarg);
319
320 options.user_includes_excludes = g_slist_append(options.user_includes_excludes, s);
321 return TRUE;
322 }
323
324 static gboolean
325 include_exclude_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
326 char *s = crm_strdup_printf("%s=%s", option_name, optarg);
327
328 options.includes_excludes = g_slist_append(options.includes_excludes, s);
329 return TRUE;
330 }
331
332 static gboolean
333 as_cgi_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
334 pcmk__str_update(&args->output_ty, "html");
335 output_format = mon_output_cgi;
336 options.one_shot = TRUE;
337 return TRUE;
338 }
339
340 static gboolean
341 as_html_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
342 pcmk__str_update(&args->output_dest, optarg);
343 pcmk__str_update(&args->output_ty, "html");
344 output_format = mon_output_html;
345 umask(S_IWGRP | S_IWOTH);
346 return TRUE;
347 }
348
349 static gboolean
350 as_simple_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
351 pcmk__str_update(&args->output_ty, "text");
352 output_format = mon_output_monitor;
353 options.one_shot = TRUE;
354 return TRUE;
355 }
356
357 static gboolean
358 as_xml_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
359 pcmk__str_update(&args->output_ty, "xml");
360 output_format = mon_output_legacy_xml;
361 return TRUE;
362 }
363
364 static gboolean
365 fence_history_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
366 if (optarg == NULL) {
367 interactive_fence_level = 2;
368 } else {
369 pcmk__scan_min_int(optarg, &interactive_fence_level, 0);
370 }
371
372 switch (interactive_fence_level) {
373 case 3:
374 options.fence_connect = TRUE;
375 fence_history = pcmk__fence_history_full;
376 return include_exclude_cb("--include", PCMK__VALUE_FENCING, data,
377 err);
378
379 case 2:
380 options.fence_connect = TRUE;
381 fence_history = pcmk__fence_history_full;
382 return include_exclude_cb("--include", PCMK__VALUE_FENCING, data,
383 err);
384
385 case 1:
386 options.fence_connect = TRUE;
387 fence_history = pcmk__fence_history_full;
388 return include_exclude_cb("--include", "fencing-failed,fencing-pending", data, err);
389
390 case 0:
391 options.fence_connect = FALSE;
392 fence_history = pcmk__fence_history_none;
393 return include_exclude_cb("--exclude", PCMK__VALUE_FENCING, data,
394 err);
395
396 default:
397 g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Fence history must be 0-3");
398 return FALSE;
399 }
400 }
401
402 static gboolean
403 group_by_node_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
404 show_opts |= pcmk_show_rscs_by_node;
405 return TRUE;
406 }
407
408 static gboolean
409 hide_headers_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
410 return user_include_exclude_cb("--exclude", "summary", data, err);
411 }
412
413 static gboolean
414 inactive_resources_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
415 show_opts |= pcmk_show_inactive_rscs;
416 return TRUE;
417 }
418
419 static gboolean
420 no_curses_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
421 output_format = mon_output_plain;
422 return TRUE;
423 }
424
425 static gboolean
426 print_brief_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
427 show_opts |= pcmk_show_brief;
428 return TRUE;
429 }
430
431 static gboolean
432 print_detail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
433 show_opts |= pcmk_show_details;
434 return TRUE;
435 }
436
437 static gboolean
438 print_timing_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
439 show_opts |= pcmk_show_timing;
440 return user_include_exclude_cb("--include", "operations", data, err);
441 }
442
443 static gboolean
444 reconnect_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
445 int rc = crm_get_msec(optarg);
446
447 if (rc == -1) {
448 g_set_error(err, PCMK__EXITC_ERROR, CRM_EX_INVALID_PARAM, "Invalid value for -i: %s", optarg);
449 return FALSE;
450 } else {
451 options.reconnect_ms = crm_parse_interval_spec(optarg);
452 }
453
454 return TRUE;
455 }
456
457 static gboolean
458 show_attributes_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
459 return user_include_exclude_cb("--include", "attributes", data, err);
460 }
461
462 static gboolean
463 show_bans_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
464 if (optarg != NULL) {
465 char *s = crm_strdup_printf("bans:%s", optarg);
466 gboolean rc = user_include_exclude_cb("--include", s, data, err);
467 free(s);
468 return rc;
469 } else {
470 return user_include_exclude_cb("--include", "bans", data, err);
471 }
472 }
473
474 static gboolean
475 show_failcounts_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
476 return user_include_exclude_cb("--include", "failcounts", data, err);
477 }
478
479 static gboolean
480 show_operations_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
481 return user_include_exclude_cb("--include", "failcounts,operations", data, err);
482 }
483
484 static gboolean
485 show_tickets_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
486 return user_include_exclude_cb("--include", "tickets", data, err);
487 }
488
489 static gboolean
490 use_cib_file_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
491 setenv("CIB_file", optarg, 1);
492 options.one_shot = TRUE;
493 return TRUE;
494 }
495
496 #define INDENT " "
497
498
499 static GOptionEntry addl_entries[] = {
500 { "interval", 'i', 0, G_OPTION_ARG_CALLBACK, reconnect_cb,
501 "Update frequency (default is 5 seconds)",
502 "TIMESPEC" },
503
504 { "one-shot", '1', 0, G_OPTION_ARG_NONE, &options.one_shot,
505 "Display the cluster status once on the console and exit",
506 NULL },
507
508 { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &options.daemonize,
509 "Run in the background as a daemon.\n"
510 INDENT "Requires at least one of --output-to and --external-agent.",
511 NULL },
512
513 { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &options.pid_file,
514 "(Advanced) Daemon pid file location",
515 "FILE" },
516
517 { "external-agent", 'E', 0, G_OPTION_ARG_FILENAME, &options.external_agent,
518 "A program to run when resource operations take place",
519 "FILE" },
520
521 { "external-recipient", 'e', 0, G_OPTION_ARG_STRING, &options.external_recipient,
522 "A recipient for your program (assuming you want the program to send something to someone).",
523 "RCPT" },
524
525 { "watch-fencing", 'W', 0, G_OPTION_ARG_NONE, &options.watch_fencing,
526 "Listen for fencing events. For use with --external-agent.",
527 NULL },
528
529 { "xml-file", 'x', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, use_cib_file_cb,
530 NULL,
531 NULL },
532
533 { NULL }
534 };
535
536 static GOptionEntry display_entries[] = {
537 { "include", 'I', 0, G_OPTION_ARG_CALLBACK, user_include_exclude_cb,
538 "A list of sections to include in the output.\n"
539 INDENT "See `Output Control` help for more information.",
540 "SECTION(s)" },
541
542 { "exclude", 'U', 0, G_OPTION_ARG_CALLBACK, user_include_exclude_cb,
543 "A list of sections to exclude from the output.\n"
544 INDENT "See `Output Control` help for more information.",
545 "SECTION(s)" },
546
547 { "node", 0, 0, G_OPTION_ARG_STRING, &options.only_node,
548 "When displaying information about nodes, show only what's related to the given\n"
549 INDENT "node, or to all nodes tagged with the given tag",
550 "NODE" },
551
552 { "resource", 0, 0, G_OPTION_ARG_STRING, &options.only_rsc,
553 "When displaying information about resources, show only what's related to the given\n"
554 INDENT "resource, or to all resources tagged with the given tag",
555 "RSC" },
556
557 { "group-by-node", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, group_by_node_cb,
558 "Group resources by node",
559 NULL },
560
561 { "inactive", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, inactive_resources_cb,
562 "Display inactive resources",
563 NULL },
564
565 { "failcounts", 'f', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_failcounts_cb,
566 "Display resource fail counts",
567 NULL },
568
569 { "operations", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_operations_cb,
570 "Display resource operation history",
571 NULL },
572
573 { "timing-details", 't', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_timing_cb,
574 "Display resource operation history with timing details",
575 NULL },
576
577 { "tickets", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_tickets_cb,
578 "Display cluster tickets",
579 NULL },
580
581 { "fence-history", 'm', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, fence_history_cb,
582 "Show fence history:\n"
583 INDENT "0=off, 1=failures and pending (default without option),\n"
584 INDENT "2=add successes (default without value for option),\n"
585 INDENT "3=show full history without reduction to most recent of each flavor",
586 "LEVEL" },
587
588 { "neg-locations", 'L', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, show_bans_cb,
589 "Display negative location constraints [optionally filtered by id prefix]",
590 NULL },
591
592 { "show-node-attributes", 'A', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_attributes_cb,
593 "Display node attributes",
594 NULL },
595
596 { "hide-headers", 'D', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, hide_headers_cb,
597 "Hide all headers",
598 NULL },
599
600 { "show-detail", 'R', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_detail_cb,
601 "Show more details (node IDs, individual clone instances)",
602 NULL },
603
604 { "brief", 'b', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_brief_cb,
605 "Brief output",
606 NULL },
607
608 { "pending", 'j', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.print_pending,
609 "Display pending state if 'record-pending' is enabled",
610 NULL },
611
612 { "simple-status", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, as_simple_cb,
613 "Display the cluster status once as a simple one line output (suitable for nagios)",
614 NULL },
615
616 { NULL }
617 };
618
619 static GOptionEntry deprecated_entries[] = {
620 { "as-html", 'h', G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, as_html_cb,
621 "Write cluster status to the named HTML file.\n"
622 INDENT "Use --output-as=html --output-to=FILE instead.",
623 "FILE" },
624
625 { "as-xml", 'X', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, as_xml_cb,
626 "Write cluster status as XML to stdout. This will enable one-shot mode.\n"
627 INDENT "Use --output-as=xml instead.",
628 NULL },
629
630 { "disable-ncurses", 'N', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, no_curses_cb,
631 "Disable the use of ncurses.\n"
632 INDENT "Use --output-as=text instead.",
633 NULL },
634
635 { "web-cgi", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, as_cgi_cb,
636 "Web mode with output suitable for CGI (preselected when run as *.cgi).\n"
637 INDENT "Use --output-as=html --html-cgi instead.",
638 NULL },
639
640 { NULL }
641 };
642
643
644
645
646
647
648 static gboolean
649 reconnect_after_timeout(gpointer data)
650 {
651 #if CURSES_ENABLED
652 if (output_format == mon_output_console) {
653 clear();
654 refresh();
655 }
656 #endif
657
658 out->info(out, "Reconnecting...");
659 if (pacemakerd_status() == pcmk_rc_ok) {
660 fencing_connect();
661 if (cib_connect() == pcmk_rc_ok) {
662
663 reconnect_timer = 0;
664 refresh_after_event(FALSE, TRUE);
665 return G_SOURCE_REMOVE;
666 }
667 }
668
669 reconnect_timer = g_timeout_add(options.reconnect_ms,
670 reconnect_after_timeout, NULL);
671 return G_SOURCE_REMOVE;
672 }
673
674
675
676
677
678 static void
679 mon_cib_connection_destroy(gpointer user_data)
680 {
681 out->info(out, "Connection to the cluster-daemons terminated");
682
683 if (refresh_timer != NULL) {
684
685 mainloop_timer_stop(refresh_timer);
686 }
687 if (reconnect_timer) {
688
689 g_source_remove(reconnect_timer);
690 reconnect_timer = 0;
691 }
692 if (st) {
693
694
695
696 clean_up_fencing_connection();
697 }
698 if (cib) {
699 cib->cmds->signoff(cib);
700 reconnect_timer = g_timeout_add(options.reconnect_ms,
701 reconnect_after_timeout, NULL);
702 }
703 return;
704 }
705
706
707 static void
708 mon_shutdown(int nsig)
709 {
710 clean_up(CRM_EX_OK);
711 }
712
713 #if CURSES_ENABLED
714 static volatile sighandler_t ncurses_winch_handler;
715
716
717
718
719
720 static void
721 mon_winresize(int nsig)
722 {
723 static int not_done;
724 int lines = 0, cols = 0;
725
726 if (!not_done++) {
727 if (ncurses_winch_handler)
728
729
730
731 (*ncurses_winch_handler) (SIGWINCH);
732 getmaxyx(stdscr, lines, cols);
733 resizeterm(lines, cols);
734
735
736
737 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
738 }
739 not_done--;
740 }
741 #endif
742
743 static int
744 fencing_connect(void)
745 {
746 int rc = pcmk_ok;
747
748 if (options.fence_connect && st == NULL) {
749 st = stonith_api_new();
750 }
751
752 if (!options.fence_connect || st == NULL || st->state != stonith_disconnected) {
753 return rc;
754 }
755
756 rc = st->cmds->connect(st, crm_system_name, NULL);
757 if (rc == pcmk_ok) {
758 crm_trace("Setting up stonith callbacks");
759 if (options.watch_fencing) {
760 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
761 mon_st_callback_event);
762 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback_event);
763 } else {
764 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
765 mon_st_callback_display);
766 st->cmds->register_notification(st, T_STONITH_NOTIFY_HISTORY, mon_st_callback_display);
767 }
768 } else {
769 clean_up_fencing_connection();
770 }
771
772 return rc;
773 }
774
775 static int
776 cib_connect(void)
777 {
778 int rc = pcmk_rc_ok;
779
780 CRM_CHECK(cib != NULL, return EINVAL);
781
782 if (cib->state == cib_connected_query ||
783 cib->state == cib_connected_command) {
784 return rc;
785 }
786
787 crm_trace("Connecting to the CIB");
788
789 rc = pcmk_legacy2rc(cib->cmds->signon(cib, crm_system_name, cib_query));
790 if (rc != pcmk_rc_ok) {
791 out->err(out, "Could not connect to the CIB: %s",
792 pcmk_rc_str(rc));
793 return rc;
794 }
795
796
797 if (output_format == mon_output_console) {
798 out->info(out,"Waiting for CIB ...");
799 }
800
801 rc = pcmk_legacy2rc(cib->cmds->query(cib, NULL, ¤t_cib,
802 cib_scope_local | cib_sync_call));
803
804 if (rc == pcmk_rc_ok) {
805 rc = pcmk_legacy2rc(cib->cmds->set_connection_dnotify(cib,
806 mon_cib_connection_destroy));
807 if (rc == EPROTONOSUPPORT) {
808 out->err(out,
809 "Notification setup not supported, won't be "
810 "able to reconnect after failure");
811 if (output_format == mon_output_console) {
812 sleep(2);
813 }
814 rc = pcmk_rc_ok;
815 }
816
817 if (rc == pcmk_rc_ok) {
818 cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY,
819 crm_diff_update);
820 rc = pcmk_legacy2rc(cib->cmds->add_notify_callback(cib,
821 T_CIB_DIFF_NOTIFY, crm_diff_update));
822 }
823
824 if (rc != pcmk_rc_ok) {
825 out->err(out, "Notification setup failed, could not monitor CIB actions");
826 cib__clean_up_connection(&cib);
827 clean_up_fencing_connection();
828 }
829 }
830 return rc;
831 }
832
833
834
835
836
837
838 static void
839 set_fencing_options(int level)
840 {
841 switch (level) {
842 case 3:
843 options.fence_connect = TRUE;
844 fence_history = pcmk__fence_history_full;
845 show |= pcmk_section_fencing_all;
846 break;
847
848 case 2:
849 options.fence_connect = TRUE;
850 fence_history = pcmk__fence_history_full;
851 show |= pcmk_section_fencing_all;
852 break;
853
854 case 1:
855 options.fence_connect = TRUE;
856 fence_history = pcmk__fence_history_full;
857 show |= pcmk_section_fence_failed | pcmk_section_fence_pending;
858 break;
859
860 default:
861 interactive_fence_level = 0;
862 options.fence_connect = FALSE;
863 fence_history = pcmk__fence_history_none;
864 show &= ~pcmk_section_fencing_all;
865 break;
866 }
867 }
868
869
870
871
872
873
874
875
876 static void
877 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
878 enum pcmk_ipc_event event_type, crm_exit_t status,
879 void *event_data, void *user_data)
880 {
881 pcmk_pacemakerd_api_reply_t *reply = event_data;
882 enum pcmk_pacemakerd_state *state =
883 (enum pcmk_pacemakerd_state *) user_data;
884
885
886 *state = pcmk_pacemakerd_state_invalid;
887
888 switch (event_type) {
889 case pcmk_ipc_event_reply:
890 break;
891
892 default:
893 return;
894 }
895
896 if (status != CRM_EX_OK) {
897 out->err(out, "Bad reply from pacemakerd: %s",
898 crm_exit_str(status));
899 return;
900 }
901
902 if (reply->reply_type != pcmk_pacemakerd_reply_ping) {
903 out->err(out, "Unknown reply type %d from pacemakerd",
904 reply->reply_type);
905 } else {
906 if ((reply->data.ping.last_good != (time_t) 0) &&
907 (reply->data.ping.status == pcmk_rc_ok)) {
908 *state = reply->data.ping.state;
909 }
910 }
911 }
912
913 static int
914 pacemakerd_status(void)
915 {
916 int rc = pcmk_rc_ok;
917 pcmk_ipc_api_t *pacemakerd_api = NULL;
918 enum pcmk_pacemakerd_state state = pcmk_pacemakerd_state_invalid;
919
920 if (!use_cib_native) {
921
922 return rc;
923 }
924 if (cib != NULL &&
925 (cib->state == cib_connected_query ||
926 cib->state == cib_connected_command)) {
927
928
929
930
931
932
933
934
935
936 return rc;
937 }
938 rc = pcmk_new_ipc_api(&pacemakerd_api, pcmk_ipc_pacemakerd);
939 if (pacemakerd_api == NULL) {
940 out->err(out, "Could not connect to pacemakerd: %s",
941 pcmk_rc_str(rc));
942
943 return rc;
944 }
945 pcmk_register_ipc_callback(pacemakerd_api, pacemakerd_event_cb, (void *) &state);
946 rc = pcmk_connect_ipc(pacemakerd_api, pcmk_ipc_dispatch_poll);
947 switch (rc) {
948 case pcmk_rc_ok:
949 rc = pcmk_pacemakerd_api_ping(pacemakerd_api, crm_system_name);
950 if (rc == pcmk_rc_ok) {
951 rc = pcmk_poll_ipc(pacemakerd_api, options.reconnect_ms/2);
952 if (rc == pcmk_rc_ok) {
953 pcmk_dispatch_ipc(pacemakerd_api);
954 rc = ENOTCONN;
955 if ((output_format == mon_output_console) ||
956 (output_format == mon_output_plain)) {
957
958 const char *state_str = NULL;
959 state_str = pcmk__pcmkd_state_enum2friendly(state);
960 switch (state) {
961 case pcmk_pacemakerd_state_running:
962 rc = pcmk_rc_ok;
963 break;
964 case pcmk_pacemakerd_state_starting_daemons:
965 out->info(out, "%s", state_str);
966 break;
967 case pcmk_pacemakerd_state_wait_for_ping:
968 out->info(out, "%s", state_str);
969 break;
970 case pcmk_pacemakerd_state_shutting_down:
971 out->info(out, "%s", state_str);
972
973 rc = pcmk_rc_ok;
974 break;
975 case pcmk_pacemakerd_state_shutdown_complete:
976 out->info(out, "%s", state_str);
977 break;
978 default:
979 break;
980 }
981 } else {
982 switch (state) {
983 case pcmk_pacemakerd_state_running:
984 rc = pcmk_rc_ok;
985 break;
986 case pcmk_pacemakerd_state_shutting_down:
987
988 rc = pcmk_rc_ok;
989 break;
990 default:
991 break;
992 }
993 }
994 }
995 }
996 break;
997 case EREMOTEIO:
998 rc = pcmk_rc_ok;
999 on_remote_node = TRUE;
1000
1001 if (output_format == mon_output_console) {
1002 out->info(out,
1003 "Running on remote-node waiting to be connected by cluster ...");
1004 }
1005 break;
1006 default:
1007 break;
1008 }
1009 pcmk_free_ipc_api(pacemakerd_api);
1010
1011 return (rc == pcmk_rc_ok)?rc:ENOTCONN;
1012 }
1013
1014 #if CURSES_ENABLED
1015 static const char *
1016 get_option_desc(char c)
1017 {
1018 const char *desc = "No help available";
1019
1020 for (GOptionEntry *entry = display_entries; entry != NULL; entry++) {
1021 if (entry->short_name == c) {
1022 desc = entry->description;
1023 break;
1024 }
1025 }
1026 return desc;
1027 }
1028
1029 #define print_option_help(out, option, condition) \
1030 curses_formatted_printf(out, "%c %c: \t%s\n", ((condition)? '*': ' '), option, get_option_desc(option));
1031
1032
1033
1034
1035
1036
1037
1038 static gboolean
1039 detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_data)
1040 {
1041 int c;
1042 gboolean config_mode = FALSE;
1043
1044 while (1) {
1045
1046
1047 c = getchar();
1048
1049 switch (c) {
1050 case 'm':
1051 interactive_fence_level++;
1052 if (interactive_fence_level > 3) {
1053 interactive_fence_level = 0;
1054 }
1055
1056 set_fencing_options(interactive_fence_level);
1057 break;
1058 case 'c':
1059 show ^= pcmk_section_tickets;
1060 break;
1061 case 'f':
1062 show ^= pcmk_section_failcounts;
1063 break;
1064 case 'n':
1065 show_opts ^= pcmk_show_rscs_by_node;
1066 break;
1067 case 'o':
1068 show ^= pcmk_section_operations;
1069 if (!pcmk_is_set(show, pcmk_section_operations)) {
1070 show_opts &= ~pcmk_show_timing;
1071 }
1072 break;
1073 case 'r':
1074 show_opts ^= pcmk_show_inactive_rscs;
1075 break;
1076 case 'R':
1077 show_opts ^= pcmk_show_details;
1078 #ifdef PCMK__COMPAT_2_0
1079
1080 show_opts |= pcmk_show_failed_detail;
1081 #endif
1082 break;
1083 case 't':
1084 show_opts ^= pcmk_show_timing;
1085 if (pcmk_is_set(show_opts, pcmk_show_timing)) {
1086 show |= pcmk_section_operations;
1087 }
1088 break;
1089 case 'A':
1090 show ^= pcmk_section_attributes;
1091 break;
1092 case 'L':
1093 show ^= pcmk_section_bans;
1094 break;
1095 case 'D':
1096
1097 if (pcmk_any_flags_set(show, pcmk_section_summary)) {
1098 show &= ~pcmk_section_summary;
1099 } else {
1100 show |= pcmk_section_summary;
1101 }
1102
1103 show &= ~pcmk_section_options;
1104 break;
1105 case 'b':
1106 show_opts ^= pcmk_show_brief;
1107 break;
1108 case 'j':
1109 show_opts ^= pcmk_show_pending;
1110 break;
1111 case '?':
1112 config_mode = TRUE;
1113 break;
1114 default:
1115
1116 goto refresh;
1117 }
1118
1119 if (!config_mode)
1120 goto refresh;
1121
1122 blank_screen();
1123
1124 curses_formatted_printf(out, "%s", "Display option change mode\n");
1125 print_option_help(out, 'c', pcmk_is_set(show, pcmk_section_tickets));
1126 print_option_help(out, 'f', pcmk_is_set(show, pcmk_section_failcounts));
1127 print_option_help(out, 'n', pcmk_is_set(show_opts, pcmk_show_rscs_by_node));
1128 print_option_help(out, 'o', pcmk_is_set(show, pcmk_section_operations));
1129 print_option_help(out, 'r', pcmk_is_set(show_opts, pcmk_show_inactive_rscs));
1130 print_option_help(out, 't', pcmk_is_set(show_opts, pcmk_show_timing));
1131 print_option_help(out, 'A', pcmk_is_set(show, pcmk_section_attributes));
1132 print_option_help(out, 'L', pcmk_is_set(show, pcmk_section_bans));
1133 print_option_help(out, 'D', !pcmk_is_set(show, pcmk_section_summary));
1134 #ifdef PCMK__COMPAT_2_0
1135 print_option_help(out, 'R', pcmk_any_flags_set(show_opts, pcmk_show_details & ~pcmk_show_failed_detail));
1136 #else
1137 print_option_help(out, 'R', pcmk_any_flags_set(show_opts, pcmk_show_details));
1138 #endif
1139 print_option_help(out, 'b', pcmk_is_set(show_opts, pcmk_show_brief));
1140 print_option_help(out, 'j', pcmk_is_set(show_opts, pcmk_show_pending));
1141 curses_formatted_printf(out, "%d m: \t%s\n", interactive_fence_level, get_option_desc('m'));
1142 curses_formatted_printf(out, "%s", "\nToggle fields via field letter, type any other key to return\n");
1143 }
1144
1145 refresh:
1146 refresh_after_event(FALSE, TRUE);
1147
1148 return TRUE;
1149 }
1150 #endif
1151
1152
1153 static void
1154 avoid_zombies(void)
1155 {
1156 struct sigaction sa;
1157
1158 memset(&sa, 0, sizeof(struct sigaction));
1159 if (sigemptyset(&sa.sa_mask) < 0) {
1160 crm_warn("Cannot avoid zombies: %s", pcmk_rc_str(errno));
1161 return;
1162 }
1163 sa.sa_handler = SIG_IGN;
1164 sa.sa_flags = SA_RESTART|SA_NOCLDWAIT;
1165 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1166 crm_warn("Cannot avoid zombies: %s", pcmk_rc_str(errno));
1167 }
1168 }
1169
1170 static GOptionContext *
1171 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
1172 GOptionContext *context = NULL;
1173
1174 GOptionEntry extra_prog_entries[] = {
1175 { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
1176 "Be less descriptive in output.",
1177 NULL },
1178
1179 { NULL }
1180 };
1181
1182 const char *description = "Notes:\n\n"
1183 "If this program is called as crm_mon.cgi, --output-as=html --html-cgi will\n"
1184 "automatically be added to the command line arguments.\n\n"
1185 "Time Specification:\n\n"
1186 "The TIMESPEC in any command line option can be specified in many different\n"
1187 "formats. It can be just an integer number of seconds, a number plus units\n"
1188 "(ms/msec/us/usec/s/sec/m/min/h/hr), or an ISO 8601 period specification.\n\n"
1189 "Output Control:\n\n"
1190 "By default, a certain list of sections are written to the output destination.\n"
1191 "The default varies based on the output format - XML includes everything, while\n"
1192 "other output formats will display less. This list can be modified with the\n"
1193 "--include and --exclude command line options. Each option may be given multiple\n"
1194 "times on the command line, and each can give a comma-separated list of sections.\n"
1195 "The options are applied to the default set, from left to right as seen on the\n"
1196 "command line. For a list of valid sections, pass --include=list or --exclude=list.\n\n"
1197 "Interactive Use:\n\n"
1198 "When run interactively, crm_mon can be told to hide and display various sections\n"
1199 "of output. To see a help screen explaining the options, hit '?'. Any key stroke\n"
1200 "aside from those listed will cause the screen to refresh.\n\n"
1201 "Examples:\n\n"
1202 "Display the cluster status on the console with updates as they occur:\n\n"
1203 "\tcrm_mon\n\n"
1204 "Display the cluster status on the console just once then exit:\n\n"
1205 "\tcrm_mon -1\n\n"
1206 "Display your cluster status, group resources by node, and include inactive resources in the list:\n\n"
1207 "\tcrm_mon --group-by-node --inactive\n\n"
1208 "Start crm_mon as a background daemon and have it write the cluster status to an HTML file:\n\n"
1209 "\tcrm_mon --daemonize --output-as html --output-to /path/to/docroot/filename.html\n\n"
1210 "Start crm_mon and export the current cluster status as XML to stdout, then exit:\n\n"
1211 "\tcrm_mon --output-as xml\n\n";
1212
1213 #if CURSES_ENABLED
1214 context = pcmk__build_arg_context(args, "console (default), html, text, xml", group, NULL);
1215 #else
1216 context = pcmk__build_arg_context(args, "text (default), html, xml", group, NULL);
1217 #endif
1218 pcmk__add_main_args(context, extra_prog_entries);
1219 g_option_context_set_description(context, description);
1220
1221 pcmk__add_arg_group(context, "display", "Display Options:",
1222 "Show display options", display_entries);
1223 pcmk__add_arg_group(context, "additional", "Additional Options:",
1224 "Show additional options", addl_entries);
1225 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
1226 "Show deprecated options", deprecated_entries);
1227
1228 return context;
1229 }
1230
1231
1232
1233
1234
1235 static void
1236 add_output_args(void) {
1237 GError *err = NULL;
1238
1239 if (output_format == mon_output_plain) {
1240 if (!pcmk__force_args(context, &err, "%s --text-fancy", g_get_prgname())) {
1241 g_propagate_error(&error, err);
1242 clean_up(CRM_EX_USAGE);
1243 }
1244 } else if (output_format == mon_output_cgi) {
1245 if (!pcmk__force_args(context, &err, "%s --html-cgi", g_get_prgname())) {
1246 g_propagate_error(&error, err);
1247 clean_up(CRM_EX_USAGE);
1248 }
1249 } else if (output_format == mon_output_xml) {
1250 if (!pcmk__force_args(context, &err, "%s --xml-simple-list --xml-substitute", g_get_prgname())) {
1251 g_propagate_error(&error, err);
1252 clean_up(CRM_EX_USAGE);
1253 }
1254 } else if (output_format == mon_output_legacy_xml) {
1255 output_format = mon_output_xml;
1256 if (!pcmk__force_args(context, &err, "%s --xml-legacy --xml-substitute", g_get_prgname())) {
1257 g_propagate_error(&error, err);
1258 clean_up(CRM_EX_USAGE);
1259 }
1260 }
1261 }
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271 static void
1272 reconcile_output_format(pcmk__common_args_t *args) {
1273 gboolean retval = TRUE;
1274 GError *err = NULL;
1275
1276 if (output_format != mon_output_unset) {
1277 return;
1278 }
1279
1280 if (pcmk__str_eq(args->output_ty, "html", pcmk__str_casei)) {
1281 char *dest = NULL;
1282
1283 pcmk__str_update(&dest, args->output_dest);
1284 retval = as_html_cb("h", dest, NULL, &err);
1285 free(dest);
1286 } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_casei)) {
1287 retval = no_curses_cb("N", NULL, NULL, &err);
1288 } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_casei)) {
1289 pcmk__str_update(&args->output_ty, "xml");
1290 output_format = mon_output_xml;
1291 } else if (options.one_shot) {
1292 pcmk__str_update(&args->output_ty, "text");
1293 output_format = mon_output_plain;
1294 } else if (!options.daemonize && args->output_dest != NULL) {
1295 options.one_shot = TRUE;
1296 pcmk__str_update(&args->output_ty, "text");
1297 output_format = mon_output_plain;
1298 } else {
1299
1300 pcmk__str_update(&args->output_ty, "console");
1301 output_format = mon_output_console;
1302 }
1303
1304 if (!retval) {
1305 g_propagate_error(&error, err);
1306 clean_up(CRM_EX_USAGE);
1307 }
1308 }
1309
1310 static void
1311 clean_up_on_connection_failure(int rc)
1312 {
1313 if (output_format == mon_output_monitor) {
1314 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "CLUSTER CRIT: Connection to cluster failed: %s",
1315 pcmk_rc_str(rc));
1316 clean_up(MON_STATUS_CRIT);
1317 } else if (rc == ENOTCONN) {
1318 if (on_remote_node) {
1319 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error: remote-node not connected to cluster");
1320 } else {
1321 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error: cluster is not available on this node");
1322 }
1323 } else {
1324 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Connection to cluster failed: %s", pcmk_rc_str(rc));
1325 }
1326
1327 clean_up(pcmk_rc2exitc(rc));
1328 }
1329
1330 static void
1331 one_shot(void)
1332 {
1333 int rc = pcmk__status(out, cib, fence_history, show, show_opts,
1334 options.only_node, options.only_rsc,
1335 options.neg_location_prefix,
1336 output_format == mon_output_monitor, 0);
1337
1338 if (rc == pcmk_rc_ok) {
1339 clean_up(pcmk_rc2exitc(rc));
1340 } else {
1341 clean_up_on_connection_failure(rc);
1342 }
1343 }
1344
1345 static void
1346 exit_on_invalid_cib(void)
1347 {
1348 if (cib != NULL) {
1349 return;
1350 }
1351
1352
1353 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Invalid CIB source");
1354 clean_up(CRM_EX_ERROR);
1355 }
1356
1357 int
1358 main(int argc, char **argv)
1359 {
1360 int rc = pcmk_rc_ok;
1361 GOptionGroup *output_group = NULL;
1362
1363 args = pcmk__new_common_args(SUMMARY);
1364 context = build_arg_context(args, &output_group);
1365 pcmk__register_formats(output_group, formats);
1366
1367 options.pid_file = strdup("/tmp/ClusterMon.pid");
1368 pcmk__cli_init_logging("crm_mon", 0);
1369
1370
1371 avoid_zombies();
1372
1373 if (pcmk__ends_with_ext(argv[0], ".cgi")) {
1374 output_format = mon_output_cgi;
1375 options.one_shot = TRUE;
1376 }
1377
1378 processed_args = pcmk__cmdline_preproc(argv, "ehimpxEILU");
1379
1380 fence_history_cb("--fence-history", "1", NULL, NULL);
1381
1382
1383
1384
1385
1386
1387
1388 if (!pcmk__force_args(context, &error, "%s --html-title \"Cluster Status\"",
1389 g_get_prgname())) {
1390 return clean_up(CRM_EX_USAGE);
1391 }
1392
1393 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
1394 return clean_up(CRM_EX_USAGE);
1395 }
1396
1397 for (int i = 0; i < args->verbosity; i++) {
1398 crm_bump_log_level(argc, argv);
1399 }
1400
1401 if (!args->version) {
1402 if (args->quiet) {
1403 include_exclude_cb("--exclude", "times", NULL, NULL);
1404 }
1405
1406 if (options.watch_fencing) {
1407 fence_history_cb("--fence-history", "0", NULL, NULL);
1408 options.fence_connect = TRUE;
1409 }
1410
1411
1412
1413
1414 cib = cib_new();
1415
1416 exit_on_invalid_cib();
1417
1418 switch (cib->variant) {
1419 case cib_native:
1420
1421 use_cib_native = TRUE;
1422 break;
1423
1424 case cib_file:
1425
1426
1427
1428
1429
1430
1431 fence_history_cb("--fence-history", "0", NULL, NULL);
1432 options.one_shot = TRUE;
1433 break;
1434
1435 case cib_remote:
1436
1437 fence_history_cb("--fence-history", "0", NULL, NULL);
1438 break;
1439
1440 case cib_undefined:
1441 case cib_database:
1442 default:
1443
1444 exit_on_invalid_cib();
1445 break;
1446 }
1447
1448 if (options.one_shot) {
1449 if (output_format == mon_output_console) {
1450 output_format = mon_output_plain;
1451 }
1452
1453 } else if (options.daemonize) {
1454 if (pcmk__str_eq(args->output_dest, "-", pcmk__str_null_matches|pcmk__str_casei) &&
1455 !options.external_agent) {
1456 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1457 "--daemonize requires at least one of --output-to and --external-agent");
1458 return clean_up(CRM_EX_USAGE);
1459 }
1460
1461 } else if (output_format == mon_output_console) {
1462 #if CURSES_ENABLED
1463 crm_enable_stderr(FALSE);
1464 #else
1465 options.one_shot = TRUE;
1466 output_format = mon_output_plain;
1467 printf("Defaulting to one-shot mode\n");
1468 printf("You need to have curses available at compile time to enable console mode\n");
1469 #endif
1470 }
1471 }
1472
1473 reconcile_output_format(args);
1474 add_output_args();
1475
1476
1477
1478 if (args->version && output_format == mon_output_console) {
1479
1480
1481
1482
1483 rc = pcmk__output_new(&out, "text", args->output_dest, argv);
1484 } else {
1485 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
1486 }
1487
1488 if (rc != pcmk_rc_ok) {
1489 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error creating output format %s: %s",
1490 args->output_ty, pcmk_rc_str(rc));
1491 return clean_up(CRM_EX_ERROR);
1492 }
1493
1494 if (options.daemonize) {
1495 if (!options.external_agent && (output_format == mon_output_console ||
1496 output_format == mon_output_unset ||
1497 output_format == mon_output_none)) {
1498 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1499 "--daemonize requires --output-as=[html|text|xml]");
1500 return clean_up(CRM_EX_USAGE);
1501 }
1502
1503 crm_enable_stderr(FALSE);
1504
1505 cib_delete(cib);
1506 cib = NULL;
1507 pcmk__daemonize(crm_system_name, options.pid_file);
1508 cib = cib_new();
1509 exit_on_invalid_cib();
1510 }
1511
1512 show = default_includes(output_format);
1513
1514
1515
1516
1517 apply_include_exclude(options.includes_excludes, &error);
1518
1519
1520
1521
1522
1523 if (!apply_include_exclude(options.user_includes_excludes, &error)) {
1524 return clean_up(CRM_EX_USAGE);
1525 }
1526
1527
1528
1529
1530 if (pcmk_all_flags_set(show, pcmk_section_fencing_all)) {
1531 interactive_fence_level = 3;
1532 } else if (pcmk_is_set(show, pcmk_section_fence_worked)) {
1533 interactive_fence_level = 2;
1534 } else if (pcmk_any_flags_set(show, pcmk_section_fence_failed | pcmk_section_fence_pending)) {
1535 interactive_fence_level = 1;
1536 } else {
1537 interactive_fence_level = 0;
1538 }
1539
1540 pcmk__register_lib_messages(out);
1541 crm_mon_register_messages(out);
1542 pe__register_messages(out);
1543 stonith__register_messages(out);
1544
1545 if (args->version) {
1546 out->version(out, false);
1547 return clean_up(CRM_EX_OK);
1548 }
1549
1550
1551 if (output_format == mon_output_cgi) {
1552 if (cib->variant == cib_file) {
1553 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode used with CIB file");
1554 return clean_up(CRM_EX_USAGE);
1555 } else if (options.external_agent != NULL) {
1556 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode cannot be used with --external-agent");
1557 return clean_up(CRM_EX_USAGE);
1558 } else if (options.daemonize == TRUE) {
1559 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode cannot be used with -d");
1560 return clean_up(CRM_EX_USAGE);
1561 }
1562 }
1563
1564 if (output_format == mon_output_xml || output_format == mon_output_legacy_xml) {
1565 show_opts |= pcmk_show_inactive_rscs | pcmk_show_timing;
1566
1567 if (!options.daemonize) {
1568 options.one_shot = TRUE;
1569 }
1570 }
1571
1572 if ((output_format == mon_output_html || output_format == mon_output_cgi) &&
1573 out->dest != stdout) {
1574 pcmk__html_add_header("meta", "http-equiv", "refresh", "content",
1575 pcmk__itoa(options.reconnect_ms / 1000), NULL);
1576 }
1577
1578 #ifdef PCMK__COMPAT_2_0
1579
1580 show_opts |= pcmk_show_failed_detail;
1581 #endif
1582
1583 crm_info("Starting %s", crm_system_name);
1584
1585 cib__set_output(cib, out);
1586
1587 if (options.one_shot) {
1588 one_shot();
1589 }
1590
1591 do {
1592 out->info(out,"Waiting until cluster is available on this node ...");
1593
1594 rc = pacemakerd_status();
1595 if (rc == pcmk_rc_ok) {
1596 fencing_connect();
1597 rc = cib_connect();
1598 }
1599
1600 if (rc != pcmk_rc_ok) {
1601 pcmk__sleep_ms(options.reconnect_ms);
1602 #if CURSES_ENABLED
1603 if (output_format == mon_output_console) {
1604 clear();
1605 refresh();
1606 }
1607 #endif
1608 } else if (output_format == mon_output_html && out->dest != stdout) {
1609 printf("Writing html to %s ...\n", args->output_dest);
1610 }
1611
1612 } while (rc == ENOTCONN);
1613
1614 if (rc != pcmk_rc_ok) {
1615 clean_up_on_connection_failure(rc);
1616 }
1617
1618 set_fencing_options(interactive_fence_level);
1619 mon_refresh_display(NULL);
1620
1621 mainloop = g_main_loop_new(NULL, FALSE);
1622
1623 mainloop_add_signal(SIGTERM, mon_shutdown);
1624 mainloop_add_signal(SIGINT, mon_shutdown);
1625 #if CURSES_ENABLED
1626 if (output_format == mon_output_console) {
1627 ncurses_winch_handler = crm_signal_handler(SIGWINCH, mon_winresize);
1628 if (ncurses_winch_handler == SIG_DFL ||
1629 ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR)
1630 ncurses_winch_handler = NULL;
1631
1632 io_channel = g_io_channel_unix_new(STDIN_FILENO);
1633 g_io_add_watch(io_channel, G_IO_IN, detect_user_input, NULL);
1634 }
1635 #endif
1636
1637
1638
1639
1640 refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL);
1641
1642 g_main_loop_run(mainloop);
1643 g_main_loop_unref(mainloop);
1644
1645 if (io_channel != NULL) {
1646 g_io_channel_shutdown(io_channel, TRUE, NULL);
1647 }
1648
1649 crm_info("Exiting %s", crm_system_name);
1650
1651 return clean_up(CRM_EX_OK);
1652 }
1653
1654 static int
1655 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
1656 int status, const char *desc)
1657 {
1658 pid_t pid;
1659
1660
1661 char *rc_s = pcmk__itoa(rc);
1662 char *status_s = pcmk__itoa(status);
1663 char *target_rc_s = pcmk__itoa(target_rc);
1664
1665 crm_debug("Sending external notification to '%s' via '%s'", options.external_recipient, options.external_agent);
1666
1667 if(rsc) {
1668 setenv("CRM_notify_rsc", rsc, 1);
1669 }
1670 if (options.external_recipient) {
1671 setenv("CRM_notify_recipient", options.external_recipient, 1);
1672 }
1673 setenv("CRM_notify_node", node, 1);
1674 setenv("CRM_notify_task", task, 1);
1675 setenv("CRM_notify_desc", desc, 1);
1676 setenv("CRM_notify_rc", rc_s, 1);
1677 setenv("CRM_notify_target_rc", target_rc_s, 1);
1678 setenv("CRM_notify_status", status_s, 1);
1679
1680 pid = fork();
1681 if (pid == -1) {
1682 crm_perror(LOG_ERR, "notification fork() failed.");
1683 }
1684 if (pid == 0) {
1685
1686 execl(options.external_agent, options.external_agent, NULL);
1687 exit(CRM_EX_ERROR);
1688 }
1689
1690 crm_trace("Finished running custom notification program '%s'.", options.external_agent);
1691 free(target_rc_s);
1692 free(status_s);
1693 free(rc_s);
1694 return 0;
1695 }
1696
1697 static int
1698 handle_rsc_op(xmlNode *xml, void *userdata)
1699 {
1700 const char *node_id = (const char *) userdata;
1701 int rc = -1;
1702 int status = -1;
1703 int target_rc = -1;
1704 gboolean notify = TRUE;
1705
1706 char *rsc = NULL;
1707 char *task = NULL;
1708 const char *desc = NULL;
1709 const char *magic = NULL;
1710 const char *id = NULL;
1711 const char *node = NULL;
1712
1713 xmlNode *n = xml;
1714 xmlNode * rsc_op = xml;
1715
1716 if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
1717 pcmk__xe_foreach_child(xml, NULL, handle_rsc_op, (void *) node_id);
1718 return pcmk_rc_ok;
1719 }
1720
1721 id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
1722 if (id == NULL) {
1723
1724 id = ID(rsc_op);
1725 }
1726
1727 magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
1728 if (magic == NULL) {
1729
1730 return pcmk_rc_ok;
1731 }
1732
1733 if (!decode_transition_magic(magic, NULL, NULL, NULL, &status, &rc,
1734 &target_rc)) {
1735 crm_err("Invalid event %s detected for %s", magic, id);
1736 return pcmk_rc_ok;
1737 }
1738
1739 if (parse_op_key(id, &rsc, &task, NULL) == FALSE) {
1740 crm_err("Invalid event detected for %s", id);
1741 goto bail;
1742 }
1743
1744 node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
1745
1746 while (n != NULL && !pcmk__str_eq(XML_CIB_TAG_STATE, TYPE(n), pcmk__str_casei)) {
1747 n = n->parent;
1748 }
1749
1750 if(node == NULL && n) {
1751 node = crm_element_value(n, XML_ATTR_UNAME);
1752 }
1753
1754 if (node == NULL && n) {
1755 node = ID(n);
1756 }
1757
1758 if (node == NULL) {
1759 node = node_id;
1760 }
1761
1762 if (node == NULL) {
1763 crm_err("No node detected for event %s (%s)", magic, id);
1764 goto bail;
1765 }
1766
1767
1768 desc = pcmk_rc_str(pcmk_rc_ok);
1769 if ((status == PCMK_EXEC_DONE) && (target_rc == rc)) {
1770 crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc);
1771 if (rc == PCMK_OCF_NOT_RUNNING) {
1772 notify = FALSE;
1773 }
1774
1775 } else if (status == PCMK_EXEC_DONE) {
1776 desc = services_ocf_exitcode_str(rc);
1777 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
1778
1779 } else {
1780 desc = pcmk_exec_status_str(status);
1781 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
1782 }
1783
1784 if (notify && options.external_agent) {
1785 send_custom_trap(node, rsc, task, target_rc, rc, status, desc);
1786 }
1787
1788 bail:
1789 free(rsc);
1790 free(task);
1791 return pcmk_rc_ok;
1792 }
1793
1794
1795
1796
1797
1798 static gboolean
1799 mon_trigger_refresh(gpointer user_data)
1800 {
1801 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
1802 return FALSE;
1803 }
1804
1805 static int
1806 handle_op_for_node(xmlNode *xml, void *userdata)
1807 {
1808 const char *node = crm_element_value(xml, XML_ATTR_UNAME);
1809
1810 if (node == NULL) {
1811 node = ID(xml);
1812 }
1813
1814 handle_rsc_op(xml, (void *) node);
1815 return pcmk_rc_ok;
1816 }
1817
1818 static void
1819 crm_diff_update_v2(const char *event, xmlNode * msg)
1820 {
1821 xmlNode *change = NULL;
1822 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1823
1824 for (change = pcmk__xml_first_child(diff); change != NULL;
1825 change = pcmk__xml_next(change)) {
1826 const char *name = NULL;
1827 const char *op = crm_element_value(change, XML_DIFF_OP);
1828 const char *xpath = crm_element_value(change, XML_DIFF_PATH);
1829 xmlNode *match = NULL;
1830 const char *node = NULL;
1831
1832 if(op == NULL) {
1833 continue;
1834
1835 } else if(strcmp(op, "create") == 0) {
1836 match = change->children;
1837
1838 } else if(strcmp(op, "move") == 0) {
1839 continue;
1840
1841 } else if(strcmp(op, "delete") == 0) {
1842 continue;
1843
1844 } else if(strcmp(op, "modify") == 0) {
1845 match = first_named_child(change, XML_DIFF_RESULT);
1846 if(match) {
1847 match = match->children;
1848 }
1849 }
1850
1851 if(match) {
1852 name = (const char *)match->name;
1853 }
1854
1855 crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
1856 if(xpath == NULL) {
1857
1858
1859 } else if(name == NULL) {
1860 crm_debug("No result for %s operation to %s", op, xpath);
1861 CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
1862
1863 } else if(strcmp(name, XML_TAG_CIB) == 0) {
1864 pcmk__xe_foreach_child(first_named_child(match, XML_CIB_TAG_STATUS),
1865 NULL, handle_op_for_node, NULL);
1866
1867 } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
1868 pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL);
1869
1870 } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
1871 node = crm_element_value(match, XML_ATTR_UNAME);
1872 if (node == NULL) {
1873 node = ID(match);
1874 }
1875 handle_rsc_op(match, (void *) node);
1876
1877 } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
1878 node = ID(match);
1879 handle_rsc_op(match, (void *) node);
1880
1881 } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
1882 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1883
1884 handle_rsc_op(match, local_node);
1885 free(local_node);
1886
1887 } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
1888 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1889
1890 handle_rsc_op(match, local_node);
1891 free(local_node);
1892
1893 } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
1894 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1895
1896 handle_rsc_op(match, local_node);
1897 free(local_node);
1898
1899 } else {
1900 crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
1901 }
1902 }
1903 }
1904
1905 static void
1906 crm_diff_update_v1(const char *event, xmlNode * msg)
1907 {
1908
1909 xmlXPathObject *xpathObj = xpath_search(msg,
1910 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
1911 "//" XML_LRM_TAG_RSC_OP);
1912 int lpc = 0, max = numXpathResults(xpathObj);
1913
1914 for (lpc = 0; lpc < max; lpc++) {
1915 xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
1916
1917 handle_rsc_op(rsc_op, NULL);
1918 }
1919 freeXpathObject(xpathObj);
1920 }
1921
1922 static void
1923 crm_diff_update(const char *event, xmlNode * msg)
1924 {
1925 int rc = -1;
1926 static bool stale = FALSE;
1927 gboolean cib_updated = FALSE;
1928 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1929
1930 out->progress(out, false);
1931
1932 if (current_cib != NULL) {
1933 rc = xml_apply_patchset(current_cib, diff, TRUE);
1934
1935 switch (rc) {
1936 case -pcmk_err_diff_resync:
1937 case -pcmk_err_diff_failed:
1938 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
1939 free_xml(current_cib); current_cib = NULL;
1940 break;
1941 case pcmk_ok:
1942 cib_updated = TRUE;
1943 break;
1944 default:
1945 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
1946 free_xml(current_cib); current_cib = NULL;
1947 }
1948 }
1949
1950 if (current_cib == NULL) {
1951 crm_trace("Re-requesting the full cib");
1952 cib->cmds->query(cib, NULL, ¤t_cib, cib_scope_local | cib_sync_call);
1953 }
1954
1955 if (options.external_agent) {
1956 int format = 0;
1957 crm_element_value_int(diff, "format", &format);
1958 switch(format) {
1959 case 1:
1960 crm_diff_update_v1(event, msg);
1961 break;
1962 case 2:
1963 crm_diff_update_v2(event, msg);
1964 break;
1965 default:
1966 crm_err("Unknown patch format: %d", format);
1967 }
1968 }
1969
1970 if (current_cib == NULL) {
1971 if(!stale) {
1972 out->info(out, "--- Stale data ---");
1973 }
1974 stale = TRUE;
1975 return;
1976 }
1977
1978 stale = FALSE;
1979 refresh_after_event(cib_updated, FALSE);
1980 }
1981
1982 static int
1983 mon_refresh_display(gpointer user_data)
1984 {
1985 int rc = pcmk_rc_ok;
1986
1987 last_refresh = time(NULL);
1988
1989 if (output_format == mon_output_none || output_format == mon_output_unset) {
1990 return G_SOURCE_REMOVE;
1991 }
1992
1993 if (fence_history == pcmk__fence_history_full &&
1994 !pcmk_all_flags_set(show, pcmk_section_fencing_all) &&
1995 output_format != mon_output_xml) {
1996 fence_history = pcmk__fence_history_reduced;
1997 }
1998
1999 if (out->dest != stdout) {
2000 out->reset(out);
2001 }
2002
2003 rc = pcmk__output_cluster_status(out, st, cib, current_cib, fence_history,
2004 show, show_opts, options.only_node,
2005 options.only_rsc, options.neg_location_prefix,
2006 output_format == mon_output_monitor);
2007
2008 if (output_format == mon_output_monitor && rc != pcmk_rc_ok) {
2009 clean_up(MON_STATUS_WARN);
2010 return G_SOURCE_REMOVE;
2011 } else if (rc == pcmk_rc_schema_validation) {
2012 clean_up(CRM_EX_CONFIG);
2013 return G_SOURCE_REMOVE;
2014 }
2015
2016 if (out->dest != stdout) {
2017 out->finish(out, CRM_EX_OK, true, NULL);
2018 }
2019
2020 return G_SOURCE_CONTINUE;
2021 }
2022
2023
2024
2025
2026 static void
2027 mon_st_callback_event(stonith_t * st, stonith_event_t * e)
2028 {
2029 if (st->state == stonith_disconnected) {
2030
2031 mon_cib_connection_destroy(NULL);
2032 } else if (options.external_agent) {
2033 char *desc = stonith__event_description(e);
2034
2035 send_custom_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
2036 free(desc);
2037 }
2038 }
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050 static void
2051 refresh_after_event(gboolean data_updated, gboolean enforce)
2052 {
2053 static int updates = 0;
2054 time_t now = time(NULL);
2055
2056 if (data_updated) {
2057 updates++;
2058 }
2059
2060 if(refresh_timer == NULL) {
2061 refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL);
2062 }
2063
2064 if (reconnect_timer > 0) {
2065
2066 mainloop_timer_stop(refresh_timer);
2067 return;
2068 }
2069
2070
2071
2072
2073
2074 fencing_connect();
2075
2076 if (enforce ||
2077 ((now - last_refresh) > (options.reconnect_ms / 1000)) ||
2078 updates >= 10) {
2079 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
2080 mainloop_timer_stop(refresh_timer);
2081 updates = 0;
2082
2083 } else {
2084 mainloop_timer_start(refresh_timer);
2085 }
2086 }
2087
2088
2089
2090
2091 static void
2092 mon_st_callback_display(stonith_t * st, stonith_event_t * e)
2093 {
2094 if (st->state == stonith_disconnected) {
2095
2096 mon_cib_connection_destroy(NULL);
2097 } else {
2098 out->progress(out, false);
2099 refresh_after_event(TRUE, FALSE);
2100 }
2101 }
2102
2103 static void
2104 clean_up_fencing_connection(void)
2105 {
2106 if (st == NULL) {
2107 return;
2108 }
2109
2110 if (st->state != stonith_disconnected) {
2111 st->cmds->remove_notification(st, NULL);
2112 st->cmds->disconnect(st);
2113 }
2114
2115 stonith_api_delete(st);
2116 st = NULL;
2117 }
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127 static crm_exit_t
2128 clean_up(crm_exit_t exit_code)
2129 {
2130
2131
2132
2133 cib__clean_up_connection(&cib);
2134 clean_up_fencing_connection();
2135 free(options.neg_location_prefix);
2136 free(options.only_node);
2137 free(options.only_rsc);
2138 free(options.pid_file);
2139 g_slist_free_full(options.includes_excludes, free);
2140
2141 g_strfreev(processed_args);
2142
2143
2144
2145
2146
2147
2148 if ((error != NULL || exit_code == CRM_EX_USAGE) && output_format == mon_output_console) {
2149 out->finish(out, exit_code, false, NULL);
2150 pcmk__output_free(out);
2151 out = NULL;
2152 }
2153
2154
2155
2156
2157 if (exit_code == CRM_EX_USAGE && (output_format == mon_output_console || output_format == mon_output_plain)) {
2158 char *help = g_option_context_get_help(context, TRUE, NULL);
2159
2160 fprintf(stderr, "%s", help);
2161 g_free(help);
2162 }
2163
2164 pcmk__free_arg_context(context);
2165
2166
2167
2168
2169
2170 if (error != NULL) {
2171 if (out != NULL) {
2172 out->err(out, "%s: %s", g_get_prgname(), error->message);
2173 out->finish(out, exit_code, true, NULL);
2174 pcmk__output_free(out);
2175 } else {
2176 fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
2177 }
2178
2179 g_clear_error(&error);
2180 crm_exit(exit_code);
2181 }
2182
2183
2184
2185
2186 if (out != NULL) {
2187 if (!options.daemonize) {
2188 out->finish(out, exit_code, true, NULL);
2189 }
2190
2191 pcmk__output_free(out);
2192 pcmk__unregister_formats();
2193 }
2194
2195 crm_exit(exit_code);
2196 }