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