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_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/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 enum pcmk_pacemakerd_state pcmkd_state = pcmk_pacemakerd_state_invalid;
75 static cib_t *cib = NULL;
76 static stonith_t *st = NULL;
77 static xmlNode *current_cib = NULL;
78
79 static GError *error = NULL;
80 static pcmk__common_args_t *args = NULL;
81 static pcmk__output_t *out = NULL;
82 static GOptionContext *context = NULL;
83 static gchar **processed_args = NULL;
84
85 static time_t last_refresh = 0;
86 volatile crm_trigger_t *refresh_trigger = NULL;
87
88 static enum pcmk__fence_history fence_history = pcmk__fence_history_none;
89
90 int interactive_fence_level = 0;
91
92 static pcmk__supported_format_t formats[] = {
93 #if CURSES_ENABLED
94 CRM_MON_SUPPORTED_FORMAT_CURSES,
95 #endif
96 PCMK__SUPPORTED_FORMAT_HTML,
97 PCMK__SUPPORTED_FORMAT_NONE,
98 PCMK__SUPPORTED_FORMAT_TEXT,
99 PCMK__SUPPORTED_FORMAT_XML,
100 { NULL, NULL, NULL }
101 };
102
103 PCMK__OUTPUT_ARGS("crm-mon-disconnected", "const char *",
104 "enum pcmk_pacemakerd_state")
105 static int
106 crm_mon_disconnected_default(pcmk__output_t *out, va_list args)
107 {
108 return pcmk_rc_no_output;
109 }
110
111 PCMK__OUTPUT_ARGS("crm-mon-disconnected", "const char *",
112 "enum pcmk_pacemakerd_state")
113 static int
114 crm_mon_disconnected_html(pcmk__output_t *out, va_list args)
115 {
116 const char *desc = va_arg(args, const char *);
117 enum pcmk_pacemakerd_state state =
118 (enum pcmk_pacemakerd_state) va_arg(args, int);
119
120 if (out->dest != stdout) {
121 out->reset(out);
122 }
123
124 pcmk__output_create_xml_text_node(out, "span", "Not connected to CIB");
125
126 if (desc != NULL) {
127 pcmk__output_create_xml_text_node(out, "span", ": ");
128 pcmk__output_create_xml_text_node(out, "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, "span", " (");
135 pcmk__output_create_xml_text_node(out, "span", state_s);
136 pcmk__output_create_xml_text_node(out, "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, "crm-mon-disconnected",
189 XML_ATTR_DESC, desc,
190 "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, "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 "default, failcounts, failures, fencing, fencing-failed, "
375 "fencing-pending, fencing-succeeded, maint-mode, nodes, "
376 PCMK__VALUE_NONE ", operations, options, resources, "
377 "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 options.reconnect_ms = crm_parse_interval_spec(optarg);
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 '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 stonith_api_delete(st);
858 st = NULL;
859
860 if (cib) {
861 cib->cmds->signoff(cib);
862 reconnect_timer = g_timeout_add(options.reconnect_ms,
863 reconnect_after_timeout, NULL);
864 }
865 }
866
867
868 static void
869 mon_shutdown(int nsig)
870 {
871 clean_up(CRM_EX_OK);
872 }
873
874 #if CURSES_ENABLED
875 static volatile sighandler_t ncurses_winch_handler;
876
877
878
879
880
881 static void
882 mon_winresize(int nsig)
883 {
884 static int not_done;
885 int lines = 0, cols = 0;
886
887 if (!not_done++) {
888 if (ncurses_winch_handler)
889
890
891
892 (*ncurses_winch_handler) (SIGWINCH);
893 getmaxyx(stdscr, lines, cols);
894 resizeterm(lines, cols);
895
896
897
898 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
899 }
900 not_done--;
901 }
902 #endif
903
904 static int
905 setup_fencer_connection(void)
906 {
907 int rc = pcmk_ok;
908
909 if (options.fence_connect && st == NULL) {
910 st = stonith_api_new();
911 }
912
913 if (!options.fence_connect || st == NULL || st->state != stonith_disconnected) {
914 return rc;
915 }
916
917 rc = st->cmds->connect(st, crm_system_name, NULL);
918 if (rc == pcmk_ok) {
919 crm_trace("Setting up stonith callbacks");
920 if (options.watch_fencing) {
921 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
922 mon_st_callback_event);
923 st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback_event);
924 } else {
925 st->cmds->register_notification(st, T_STONITH_NOTIFY_DISCONNECT,
926 mon_st_callback_display);
927 st->cmds->register_notification(st, T_STONITH_NOTIFY_HISTORY, mon_st_callback_display);
928 }
929 } else {
930 stonith_api_delete(st);
931 st = NULL;
932 }
933
934 return rc;
935 }
936
937 static int
938 setup_cib_connection(void)
939 {
940 int rc = pcmk_rc_ok;
941
942 CRM_CHECK(cib != NULL, return EINVAL);
943
944 if (cib->state != cib_disconnected) {
945
946 return rc;
947 }
948
949 rc = cib__signon_query(out, &cib, ¤t_cib);
950
951 if (rc == pcmk_rc_ok) {
952 rc = pcmk_legacy2rc(cib->cmds->set_connection_dnotify(cib,
953 mon_cib_connection_destroy));
954 if (rc == EPROTONOSUPPORT) {
955 out->err(out,
956 "CIB client does not support connection loss "
957 "notifications; crm_mon will be unable to reconnect after "
958 "connection loss");
959 rc = pcmk_rc_ok;
960 }
961
962 if (rc == pcmk_rc_ok) {
963 cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY,
964 crm_diff_update);
965 rc = pcmk_legacy2rc(cib->cmds->add_notify_callback(cib,
966 T_CIB_DIFF_NOTIFY, crm_diff_update));
967 }
968
969 if (rc != pcmk_rc_ok) {
970 if (rc == EPROTONOSUPPORT) {
971 out->err(out,
972 "CIB client does not support CIB diff "
973 "notifications");
974 } else {
975 out->err(out, "CIB diff notification setup failed");
976 }
977
978 out->err(out, "Cannot monitor CIB changes; exiting");
979 cib__clean_up_connection(&cib);
980 stonith_api_delete(st);
981 st = NULL;
982 }
983 }
984 return rc;
985 }
986
987
988
989
990
991
992 static void
993 set_fencing_options(int level)
994 {
995 switch (level) {
996 case 3:
997 options.fence_connect = TRUE;
998 fence_history = pcmk__fence_history_full;
999 show |= pcmk_section_fencing_all;
1000 break;
1001
1002 case 2:
1003 options.fence_connect = TRUE;
1004 fence_history = pcmk__fence_history_full;
1005 show |= pcmk_section_fencing_all;
1006 break;
1007
1008 case 1:
1009 options.fence_connect = TRUE;
1010 fence_history = pcmk__fence_history_full;
1011 show |= pcmk_section_fence_failed | pcmk_section_fence_pending;
1012 break;
1013
1014 default:
1015 interactive_fence_level = 0;
1016 options.fence_connect = FALSE;
1017 fence_history = pcmk__fence_history_none;
1018 show &= ~pcmk_section_fencing_all;
1019 break;
1020 }
1021 }
1022
1023 static int
1024 setup_api_connections(void)
1025 {
1026 int rc = pcmk_rc_ok;
1027
1028 CRM_CHECK(cib != NULL, return EINVAL);
1029
1030 if (cib->state != cib_disconnected) {
1031 return rc;
1032 }
1033
1034 if (cib->variant == cib_native) {
1035 rc = pcmk__pacemakerd_status(out, crm_system_name,
1036 options.reconnect_ms / 2, false,
1037 &pcmkd_state);
1038 if (rc != pcmk_rc_ok) {
1039 return rc;
1040 }
1041
1042 switch (pcmkd_state) {
1043 case pcmk_pacemakerd_state_running:
1044 case pcmk_pacemakerd_state_remote:
1045 case pcmk_pacemakerd_state_shutting_down:
1046
1047
1048
1049 break;
1050 default:
1051
1052 return ENOTCONN;
1053 }
1054
1055 setup_fencer_connection();
1056 }
1057
1058 rc = setup_cib_connection();
1059 return rc;
1060 }
1061
1062 #if CURSES_ENABLED
1063 static const char *
1064 get_option_desc(char c)
1065 {
1066 const char *desc = "No help available";
1067
1068 for (GOptionEntry *entry = display_entries; entry != NULL; entry++) {
1069 if (entry->short_name == c) {
1070 desc = entry->description;
1071 break;
1072 }
1073 }
1074 return desc;
1075 }
1076
1077 #define print_option_help(out, option, condition) \
1078 curses_formatted_printf(out, "%c %c: \t%s\n", ((condition)? '*': ' '), option, get_option_desc(option));
1079
1080
1081
1082
1083
1084
1085
1086 static gboolean
1087 detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer user_data)
1088 {
1089 int c;
1090 gboolean config_mode = FALSE;
1091
1092 while (1) {
1093
1094
1095 c = getchar();
1096
1097 switch (c) {
1098 case 'm':
1099 interactive_fence_level++;
1100 if (interactive_fence_level > 3) {
1101 interactive_fence_level = 0;
1102 }
1103
1104 set_fencing_options(interactive_fence_level);
1105 break;
1106 case 'c':
1107 show ^= pcmk_section_tickets;
1108 break;
1109 case 'f':
1110 show ^= pcmk_section_failcounts;
1111 break;
1112 case 'n':
1113 show_opts ^= pcmk_show_rscs_by_node;
1114 break;
1115 case 'o':
1116 show ^= pcmk_section_operations;
1117 if (!pcmk_is_set(show, pcmk_section_operations)) {
1118 show_opts &= ~pcmk_show_timing;
1119 }
1120 break;
1121 case 'r':
1122 show_opts ^= pcmk_show_inactive_rscs;
1123 break;
1124 case 'R':
1125 show_opts ^= pcmk_show_details;
1126 #ifdef PCMK__COMPAT_2_0
1127
1128 show_opts |= pcmk_show_failed_detail;
1129 #endif
1130 break;
1131 case 't':
1132 show_opts ^= pcmk_show_timing;
1133 if (pcmk_is_set(show_opts, pcmk_show_timing)) {
1134 show |= pcmk_section_operations;
1135 }
1136 break;
1137 case 'A':
1138 show ^= pcmk_section_attributes;
1139 break;
1140 case 'L':
1141 show ^= pcmk_section_bans;
1142 break;
1143 case 'D':
1144
1145 if (pcmk_any_flags_set(show, pcmk_section_summary)) {
1146 show &= ~pcmk_section_summary;
1147 } else {
1148 show |= pcmk_section_summary;
1149 }
1150
1151 show &= ~pcmk_section_options;
1152 break;
1153 case 'b':
1154 show_opts ^= pcmk_show_brief;
1155 break;
1156 case 'j':
1157 show_opts ^= pcmk_show_pending;
1158 break;
1159 case '?':
1160 config_mode = TRUE;
1161 break;
1162 default:
1163
1164 goto refresh;
1165 }
1166
1167 if (!config_mode)
1168 goto refresh;
1169
1170 clear();
1171 refresh();
1172
1173 curses_formatted_printf(out, "%s", "Display option change mode\n");
1174 print_option_help(out, 'c', pcmk_is_set(show, pcmk_section_tickets));
1175 print_option_help(out, 'f', pcmk_is_set(show, pcmk_section_failcounts));
1176 print_option_help(out, 'n', pcmk_is_set(show_opts, pcmk_show_rscs_by_node));
1177 print_option_help(out, 'o', pcmk_is_set(show, pcmk_section_operations));
1178 print_option_help(out, 'r', pcmk_is_set(show_opts, pcmk_show_inactive_rscs));
1179 print_option_help(out, 't', pcmk_is_set(show_opts, pcmk_show_timing));
1180 print_option_help(out, 'A', pcmk_is_set(show, pcmk_section_attributes));
1181 print_option_help(out, 'L', pcmk_is_set(show, pcmk_section_bans));
1182 print_option_help(out, 'D', !pcmk_is_set(show, pcmk_section_summary));
1183 #ifdef PCMK__COMPAT_2_0
1184 print_option_help(out, 'R', pcmk_any_flags_set(show_opts, pcmk_show_details & ~pcmk_show_failed_detail));
1185 #else
1186 print_option_help(out, 'R', pcmk_any_flags_set(show_opts, pcmk_show_details));
1187 #endif
1188 print_option_help(out, 'b', pcmk_is_set(show_opts, pcmk_show_brief));
1189 print_option_help(out, 'j', pcmk_is_set(show_opts, pcmk_show_pending));
1190 curses_formatted_printf(out, "%d m: \t%s\n", interactive_fence_level, get_option_desc('m'));
1191 curses_formatted_printf(out, "%s", "\nToggle fields via field letter, type any other key to return\n");
1192 }
1193
1194 refresh:
1195 refresh_after_event(FALSE, TRUE);
1196
1197 return TRUE;
1198 }
1199 #endif
1200
1201
1202 static void
1203 avoid_zombies(void)
1204 {
1205 struct sigaction sa;
1206
1207 memset(&sa, 0, sizeof(struct sigaction));
1208 if (sigemptyset(&sa.sa_mask) < 0) {
1209 crm_warn("Cannot avoid zombies: %s", pcmk_rc_str(errno));
1210 return;
1211 }
1212 sa.sa_handler = SIG_IGN;
1213 sa.sa_flags = SA_RESTART|SA_NOCLDWAIT;
1214 if (sigaction(SIGCHLD, &sa, NULL) < 0) {
1215 crm_warn("Cannot avoid zombies: %s", pcmk_rc_str(errno));
1216 }
1217 }
1218
1219 static GOptionContext *
1220 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
1221 GOptionContext *context = NULL;
1222
1223 GOptionEntry extra_prog_entries[] = {
1224 { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
1225 "Be less descriptive in output.",
1226 NULL },
1227
1228 { NULL }
1229 };
1230
1231 #if CURSES_ENABLED
1232 const char *fmts = "console (default), html, text, xml, none";
1233 #else
1234 const char *fmts = "text (default), html, xml, none";
1235 #endif
1236 const char *desc = NULL;
1237
1238 desc = "Notes:\n\n"
1239 "If this program is called as crm_mon.cgi, --output-as=html and\n"
1240 "--html-cgi are automatically added to the command line\n"
1241 "arguments.\n\n"
1242
1243 "Time Specification:\n\n"
1244 "The TIMESPEC in any command line option can be specified in many\n"
1245 "different formats. It can be an integer number of seconds, a\n"
1246 "number plus units (us/usec/ms/msec/s/sec/m/min/h/hr), or an ISO\n"
1247 "8601 period specification.\n\n"
1248
1249 "Output Control:\n\n"
1250 "By default, a particular set of sections are written to the\n"
1251 "output destination. The default varies based on the output\n"
1252 "format: XML includes all sections by default, while other output\n"
1253 "formats include less. This set can be modified with the --include\n"
1254 "and --exclude command line options. Each option may be passed\n"
1255 "multiple times, and each can specify a comma-separated list of\n"
1256 "sections. The options are applied to the default set, in order\n"
1257 "from left to right as they are passed on the command line. For a\n"
1258 "list of valid sections, pass --include=list or --exclude=list.\n\n"
1259
1260 "Interactive Use:\n\n"
1261 #if CURSES_ENABLED
1262 "When run interactively, crm_mon can be told to hide and show\n"
1263 "various sections of output. To see a help screen explaining the\n"
1264 "options, press '?'. Any key stroke aside from those listed will\n"
1265 "cause the screen to refresh.\n\n"
1266 #else
1267 "The local installation of Pacemaker was built without support for\n"
1268 "interactive (console) mode. A curses library must be available at\n"
1269 "build time to support interactive mode.\n\n"
1270 #endif
1271
1272 "Examples:\n\n"
1273 #if CURSES_ENABLED
1274 "Display the cluster status on the console with updates as they\n"
1275 "occur:\n\n"
1276 "\tcrm_mon\n\n"
1277 #endif
1278
1279 "Display the cluster status once and exit:\n\n"
1280 "\tcrm_mon -1\n\n"
1281
1282 "Display the cluster status, group resources by node, and include\n"
1283 "inactive resources in the list:\n\n"
1284 "\tcrm_mon --group-by-node --inactive\n\n"
1285
1286 "Start crm_mon as a background daemon and have it write the\n"
1287 "cluster status to an HTML file:\n\n"
1288 "\tcrm_mon --daemonize --output-as html "
1289 "--output-to /path/to/docroot/filename.html\n\n"
1290
1291 "Display the cluster status as XML:\n\n"
1292 "\tcrm_mon --output-as xml\n\n";
1293
1294 context = pcmk__build_arg_context(args, fmts, group, NULL);
1295 pcmk__add_main_args(context, extra_prog_entries);
1296 g_option_context_set_description(context, desc);
1297
1298 pcmk__add_arg_group(context, "display", "Display Options:",
1299 "Show display options", display_entries);
1300 pcmk__add_arg_group(context, "additional", "Additional Options:",
1301 "Show additional options", addl_entries);
1302 pcmk__add_arg_group(context, "deprecated", "Deprecated Options:",
1303 "Show deprecated options", deprecated_entries);
1304
1305 return context;
1306 }
1307
1308
1309
1310
1311
1312 static void
1313 add_output_args(void) {
1314 GError *err = NULL;
1315
1316 if (output_format == mon_output_plain) {
1317 if (!pcmk__force_args(context, &err, "%s --text-fancy", g_get_prgname())) {
1318 g_propagate_error(&error, err);
1319 clean_up(CRM_EX_USAGE);
1320 }
1321 } else if (output_format == mon_output_cgi) {
1322 if (!pcmk__force_args(context, &err, "%s --html-cgi", g_get_prgname())) {
1323 g_propagate_error(&error, err);
1324 clean_up(CRM_EX_USAGE);
1325 }
1326 } else if (output_format == mon_output_xml) {
1327 if (!pcmk__force_args(context, &err, "%s --xml-simple-list --xml-substitute", g_get_prgname())) {
1328 g_propagate_error(&error, err);
1329 clean_up(CRM_EX_USAGE);
1330 }
1331 } else if (output_format == mon_output_legacy_xml) {
1332 output_format = mon_output_xml;
1333 if (!pcmk__force_args(context, &err, "%s --xml-legacy --xml-substitute", g_get_prgname())) {
1334 g_propagate_error(&error, err);
1335 clean_up(CRM_EX_USAGE);
1336 }
1337 }
1338 }
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352 static void
1353 reconcile_output_format(pcmk__common_args_t *args)
1354 {
1355 if (output_format != mon_output_unset) {
1356
1357
1358
1359 return;
1360 }
1361
1362 if (pcmk__str_eq(args->output_ty, "none", pcmk__str_none)) {
1363 output_format = mon_output_none;
1364
1365 } else if (pcmk__str_eq(args->output_ty, "html", pcmk__str_none)) {
1366 output_format = mon_output_html;
1367 umask(S_IWGRP | S_IWOTH);
1368
1369 } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
1370 output_format = mon_output_xml;
1371
1372 #if CURSES_ENABLED
1373 } else if (pcmk__str_eq(args->output_ty, "console",
1374 pcmk__str_null_matches)) {
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384 if ((options.exec_mode == mon_exec_daemonized)
1385 || (options.exec_mode == mon_exec_one_shot)
1386 || args->version
1387 || !pcmk__str_eq(args->output_dest, "-", pcmk__str_null_matches)) {
1388
1389 pcmk__str_update(&args->output_ty, "text");
1390 output_format = mon_output_plain;
1391 } else {
1392 pcmk__str_update(&args->output_ty, "console");
1393 output_format = mon_output_console;
1394 crm_enable_stderr(FALSE);
1395 }
1396 #endif
1397
1398 } else if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches)) {
1399
1400
1401
1402 pcmk__str_update(&args->output_ty, "text");
1403 output_format = mon_output_plain;
1404 }
1405
1406
1407 }
1408
1409
1410
1411
1412
1413
1414
1415 static void
1416 set_default_exec_mode(const pcmk__common_args_t *args)
1417 {
1418 if (output_format == mon_output_console) {
1419
1420
1421
1422 options.exec_mode = mon_exec_update;
1423
1424 } else if (options.exec_mode == mon_exec_unset) {
1425
1426 options.exec_mode = mon_exec_one_shot;
1427
1428 } else if ((options.exec_mode == mon_exec_update)
1429 && pcmk__str_eq(args->output_dest, "-",
1430 pcmk__str_null_matches)) {
1431
1432 options.exec_mode = mon_exec_one_shot;
1433 }
1434 }
1435
1436 static void
1437 clean_up_on_connection_failure(int rc)
1438 {
1439 if (output_format == mon_output_monitor) {
1440 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "CLUSTER CRIT: Connection to cluster failed: %s",
1441 pcmk_rc_str(rc));
1442 clean_up(MON_STATUS_CRIT);
1443 } else if (rc == ENOTCONN) {
1444 if (pcmkd_state == pcmk_pacemakerd_state_remote) {
1445 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error: remote-node not connected to cluster");
1446 } else {
1447 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error: cluster is not available on this node");
1448 }
1449 } else {
1450 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Connection to cluster failed: %s", pcmk_rc_str(rc));
1451 }
1452
1453 clean_up(pcmk_rc2exitc(rc));
1454 }
1455
1456 static void
1457 one_shot(void)
1458 {
1459 int rc = pcmk__status(out, cib, fence_history, show, show_opts,
1460 options.only_node, options.only_rsc,
1461 options.neg_location_prefix,
1462 output_format == mon_output_monitor, 0);
1463
1464 if (rc == pcmk_rc_ok) {
1465 clean_up(pcmk_rc2exitc(rc));
1466 } else {
1467 clean_up_on_connection_failure(rc);
1468 }
1469 }
1470
1471 static void
1472 exit_on_invalid_cib(void)
1473 {
1474 if (cib != NULL) {
1475 return;
1476 }
1477
1478
1479 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Invalid CIB source");
1480 clean_up(CRM_EX_ERROR);
1481 }
1482
1483 int
1484 main(int argc, char **argv)
1485 {
1486 int rc = pcmk_rc_ok;
1487 GOptionGroup *output_group = NULL;
1488
1489 args = pcmk__new_common_args(SUMMARY);
1490 context = build_arg_context(args, &output_group);
1491 pcmk__register_formats(output_group, formats);
1492
1493 options.pid_file = strdup("/tmp/ClusterMon.pid");
1494 pcmk__cli_init_logging("crm_mon", 0);
1495
1496
1497 avoid_zombies();
1498
1499 if (pcmk__ends_with_ext(argv[0], ".cgi")) {
1500 output_format = mon_output_cgi;
1501 options.exec_mode = mon_exec_one_shot;
1502 }
1503
1504 processed_args = pcmk__cmdline_preproc(argv, "ehimpxEILU");
1505
1506 fence_history_cb("--fence-history", "1", NULL, NULL);
1507
1508
1509
1510
1511
1512
1513
1514 if (!pcmk__force_args(context, &error, "%s --html-title \"Cluster Status\"",
1515 g_get_prgname())) {
1516 return clean_up(CRM_EX_USAGE);
1517 }
1518
1519 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
1520 return clean_up(CRM_EX_USAGE);
1521 }
1522
1523 for (int i = 0; i < args->verbosity; i++) {
1524 crm_bump_log_level(argc, argv);
1525 }
1526
1527 if (!args->version) {
1528 if (args->quiet) {
1529 include_exclude_cb("--exclude", "times", NULL, NULL);
1530 }
1531
1532 if (options.watch_fencing) {
1533 fence_history_cb("--fence-history", "0", NULL, NULL);
1534 options.fence_connect = TRUE;
1535 }
1536
1537
1538
1539
1540 cib = cib_new();
1541
1542 exit_on_invalid_cib();
1543
1544 switch (cib->variant) {
1545 case cib_native:
1546
1547 break;
1548
1549 case cib_file:
1550
1551 fence_history_cb("--fence-history", "0", NULL, NULL);
1552
1553
1554
1555
1556 options.exec_mode = mon_exec_one_shot;
1557 break;
1558
1559 case cib_remote:
1560
1561 fence_history_cb("--fence-history", "0", NULL, NULL);
1562 break;
1563
1564 default:
1565
1566 exit_on_invalid_cib();
1567 break;
1568 }
1569
1570 if ((options.exec_mode == mon_exec_daemonized)
1571 && !options.external_agent
1572 && pcmk__str_eq(args->output_dest, "-", pcmk__str_null_matches)) {
1573
1574 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1575 "--daemonize requires at least one of --output-to "
1576 "(with value not set to '-') and --external-agent");
1577 return clean_up(CRM_EX_USAGE);
1578 }
1579 }
1580
1581 reconcile_output_format(args);
1582 set_default_exec_mode(args);
1583 add_output_args();
1584
1585
1586
1587 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
1588 if (rc != pcmk_rc_ok) {
1589 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_ERROR, "Error creating output format %s: %s",
1590 args->output_ty, pcmk_rc_str(rc));
1591 return clean_up(CRM_EX_ERROR);
1592 }
1593
1594
1595
1596
1597 CRM_ASSERT(output_format != mon_output_unset);
1598
1599 if (options.exec_mode == mon_exec_daemonized) {
1600 if (!options.external_agent && (output_format == mon_output_none)) {
1601 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE,
1602 "--daemonize requires --external-agent if used with "
1603 "--output-as=none");
1604 return clean_up(CRM_EX_USAGE);
1605 }
1606 crm_enable_stderr(FALSE);
1607 cib_delete(cib);
1608 cib = NULL;
1609 pcmk__daemonize(crm_system_name, options.pid_file);
1610 cib = cib_new();
1611 exit_on_invalid_cib();
1612 }
1613
1614 show = default_includes(output_format);
1615
1616
1617
1618
1619 apply_include_exclude(options.includes_excludes, &error);
1620
1621
1622
1623
1624
1625 if (!apply_include_exclude(options.user_includes_excludes, &error)) {
1626 return clean_up(CRM_EX_USAGE);
1627 }
1628
1629
1630
1631
1632 if (pcmk_all_flags_set(show, pcmk_section_fencing_all)) {
1633 interactive_fence_level = 3;
1634 } else if (pcmk_is_set(show, pcmk_section_fence_worked)) {
1635 interactive_fence_level = 2;
1636 } else if (pcmk_any_flags_set(show, pcmk_section_fence_failed | pcmk_section_fence_pending)) {
1637 interactive_fence_level = 1;
1638 } else {
1639 interactive_fence_level = 0;
1640 }
1641
1642 pcmk__register_lib_messages(out);
1643 crm_mon_register_messages(out);
1644 pe__register_messages(out);
1645 stonith__register_messages(out);
1646
1647
1648 pcmk__register_messages(out, fmt_functions);
1649
1650 if (args->version) {
1651 out->version(out, false);
1652 return clean_up(CRM_EX_OK);
1653 }
1654
1655
1656 if (output_format == mon_output_cgi) {
1657 if (cib->variant == cib_file) {
1658 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode used with CIB file");
1659 return clean_up(CRM_EX_USAGE);
1660 } else if (options.external_agent != NULL) {
1661 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode cannot be used with --external-agent");
1662 return clean_up(CRM_EX_USAGE);
1663 } else if (options.exec_mode == mon_exec_daemonized) {
1664 g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, "CGI mode cannot be used with -d");
1665 return clean_up(CRM_EX_USAGE);
1666 }
1667 }
1668
1669 if (output_format == mon_output_xml) {
1670 show_opts |= pcmk_show_inactive_rscs | pcmk_show_timing;
1671 }
1672
1673 if ((output_format == mon_output_html || output_format == mon_output_cgi) &&
1674 out->dest != stdout) {
1675 pcmk__html_add_header("meta", "http-equiv", "refresh", "content",
1676 pcmk__itoa(options.reconnect_ms / 1000), NULL);
1677 }
1678
1679 #ifdef PCMK__COMPAT_2_0
1680
1681 show_opts |= pcmk_show_failed_detail;
1682 #endif
1683
1684 crm_info("Starting %s", crm_system_name);
1685
1686 cib__set_output(cib, out);
1687
1688 if (options.exec_mode == mon_exec_one_shot) {
1689 one_shot();
1690 }
1691
1692 out->message(out, "crm-mon-disconnected",
1693 "Waiting for initial connection", pcmkd_state);
1694 do {
1695 out->transient(out, "Connecting to cluster...");
1696 rc = setup_api_connections();
1697
1698 if (rc != pcmk_rc_ok) {
1699 if ((rc == ENOTCONN) || (rc == ECONNREFUSED)) {
1700 out->transient(out, "Connection failed. Retrying in %ums...",
1701 options.reconnect_ms);
1702 }
1703
1704
1705 pcmk__sleep_ms(options.reconnect_ms);
1706 #if CURSES_ENABLED
1707 if (output_format == mon_output_console) {
1708 clear();
1709 refresh();
1710 }
1711 #endif
1712 }
1713 } while ((rc == ENOTCONN) || (rc == ECONNREFUSED));
1714
1715 if (rc != pcmk_rc_ok) {
1716 clean_up_on_connection_failure(rc);
1717 }
1718
1719 set_fencing_options(interactive_fence_level);
1720 mon_refresh_display(NULL);
1721
1722 mainloop = g_main_loop_new(NULL, FALSE);
1723
1724 mainloop_add_signal(SIGTERM, mon_shutdown);
1725 mainloop_add_signal(SIGINT, mon_shutdown);
1726 #if CURSES_ENABLED
1727 if (output_format == mon_output_console) {
1728 ncurses_winch_handler = crm_signal_handler(SIGWINCH, mon_winresize);
1729 if (ncurses_winch_handler == SIG_DFL ||
1730 ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR)
1731 ncurses_winch_handler = NULL;
1732
1733 io_channel = g_io_channel_unix_new(STDIN_FILENO);
1734 g_io_add_watch(io_channel, G_IO_IN, detect_user_input, NULL);
1735 }
1736 #endif
1737
1738
1739
1740
1741 refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL);
1742
1743 g_main_loop_run(mainloop);
1744 g_main_loop_unref(mainloop);
1745
1746 if (io_channel != NULL) {
1747 g_io_channel_shutdown(io_channel, TRUE, NULL);
1748 }
1749
1750 crm_info("Exiting %s", crm_system_name);
1751
1752 return clean_up(CRM_EX_OK);
1753 }
1754
1755 static int
1756 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
1757 int status, const char *desc)
1758 {
1759 pid_t pid;
1760
1761
1762 char *rc_s = pcmk__itoa(rc);
1763 char *status_s = pcmk__itoa(status);
1764 char *target_rc_s = pcmk__itoa(target_rc);
1765
1766 crm_debug("Sending external notification to '%s' via '%s'", options.external_recipient, options.external_agent);
1767
1768 if(rsc) {
1769 setenv("CRM_notify_rsc", rsc, 1);
1770 }
1771 if (options.external_recipient) {
1772 setenv("CRM_notify_recipient", options.external_recipient, 1);
1773 }
1774 setenv("CRM_notify_node", node, 1);
1775 setenv("CRM_notify_task", task, 1);
1776 setenv("CRM_notify_desc", desc, 1);
1777 setenv("CRM_notify_rc", rc_s, 1);
1778 setenv("CRM_notify_target_rc", target_rc_s, 1);
1779 setenv("CRM_notify_status", status_s, 1);
1780
1781 pid = fork();
1782 if (pid == -1) {
1783 out->err(out, "notification fork() failed: %s", strerror(errno));
1784 }
1785 if (pid == 0) {
1786
1787 execl(options.external_agent, options.external_agent, NULL);
1788 exit(CRM_EX_ERROR);
1789 }
1790
1791 crm_trace("Finished running custom notification program '%s'.", options.external_agent);
1792 free(target_rc_s);
1793 free(status_s);
1794 free(rc_s);
1795 return 0;
1796 }
1797
1798 static int
1799 handle_rsc_op(xmlNode *xml, void *userdata)
1800 {
1801 const char *node_id = (const char *) userdata;
1802 int rc = -1;
1803 int status = -1;
1804 int target_rc = -1;
1805 gboolean notify = TRUE;
1806
1807 char *rsc = NULL;
1808 char *task = NULL;
1809 const char *desc = NULL;
1810 const char *magic = NULL;
1811 const char *id = NULL;
1812 const char *node = NULL;
1813
1814 xmlNode *n = xml;
1815 xmlNode * rsc_op = xml;
1816
1817 if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
1818 pcmk__xe_foreach_child(xml, NULL, handle_rsc_op, (void *) node_id);
1819 return pcmk_rc_ok;
1820 }
1821
1822 id = pe__xe_history_key(rsc_op);
1823
1824 magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
1825 if (magic == NULL) {
1826
1827 return pcmk_rc_ok;
1828 }
1829
1830 if (!decode_transition_magic(magic, NULL, NULL, NULL, &status, &rc,
1831 &target_rc)) {
1832 crm_err("Invalid event %s detected for %s", magic, id);
1833 return pcmk_rc_ok;
1834 }
1835
1836 if (parse_op_key(id, &rsc, &task, NULL) == FALSE) {
1837 crm_err("Invalid event detected for %s", id);
1838 goto bail;
1839 }
1840
1841 node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
1842
1843 while ((n != NULL) && !pcmk__xe_is(n, XML_CIB_TAG_STATE)) {
1844 n = n->parent;
1845 }
1846
1847 if(node == NULL && n) {
1848 node = crm_element_value(n, XML_ATTR_UNAME);
1849 }
1850
1851 if (node == NULL && n) {
1852 node = ID(n);
1853 }
1854
1855 if (node == NULL) {
1856 node = node_id;
1857 }
1858
1859 if (node == NULL) {
1860 crm_err("No node detected for event %s (%s)", magic, id);
1861 goto bail;
1862 }
1863
1864
1865 desc = pcmk_rc_str(pcmk_rc_ok);
1866 if ((status == PCMK_EXEC_DONE) && (target_rc == rc)) {
1867 crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc);
1868 if (rc == PCMK_OCF_NOT_RUNNING) {
1869 notify = FALSE;
1870 }
1871
1872 } else if (status == PCMK_EXEC_DONE) {
1873 desc = services_ocf_exitcode_str(rc);
1874 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
1875
1876 } else {
1877 desc = pcmk_exec_status_str(status);
1878 crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
1879 }
1880
1881 if (notify && options.external_agent) {
1882 send_custom_trap(node, rsc, task, target_rc, rc, status, desc);
1883 }
1884
1885 bail:
1886 free(rsc);
1887 free(task);
1888 return pcmk_rc_ok;
1889 }
1890
1891
1892
1893
1894
1895 static gboolean
1896 mon_trigger_refresh(gpointer user_data)
1897 {
1898 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
1899 return FALSE;
1900 }
1901
1902 static int
1903 handle_op_for_node(xmlNode *xml, void *userdata)
1904 {
1905 const char *node = crm_element_value(xml, XML_ATTR_UNAME);
1906
1907 if (node == NULL) {
1908 node = ID(xml);
1909 }
1910
1911 handle_rsc_op(xml, (void *) node);
1912 return pcmk_rc_ok;
1913 }
1914
1915 static void
1916 crm_diff_update_v2(const char *event, xmlNode * msg)
1917 {
1918 xmlNode *change = NULL;
1919 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1920
1921 for (change = pcmk__xml_first_child(diff); change != NULL;
1922 change = pcmk__xml_next(change)) {
1923 const char *name = NULL;
1924 const char *op = crm_element_value(change, XML_DIFF_OP);
1925 const char *xpath = crm_element_value(change, XML_DIFF_PATH);
1926 xmlNode *match = NULL;
1927 const char *node = NULL;
1928
1929 if(op == NULL) {
1930 continue;
1931
1932 } else if(strcmp(op, "create") == 0) {
1933 match = change->children;
1934
1935 } else if(strcmp(op, "move") == 0) {
1936 continue;
1937
1938 } else if(strcmp(op, "delete") == 0) {
1939 continue;
1940
1941 } else if(strcmp(op, "modify") == 0) {
1942 match = first_named_child(change, XML_DIFF_RESULT);
1943 if(match) {
1944 match = match->children;
1945 }
1946 }
1947
1948 if(match) {
1949 name = (const char *)match->name;
1950 }
1951
1952 crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
1953 if(xpath == NULL) {
1954
1955
1956 } else if(name == NULL) {
1957 crm_debug("No result for %s operation to %s", op, xpath);
1958 CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
1959
1960 } else if(strcmp(name, XML_TAG_CIB) == 0) {
1961 pcmk__xe_foreach_child(first_named_child(match, XML_CIB_TAG_STATUS),
1962 NULL, handle_op_for_node, NULL);
1963
1964 } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
1965 pcmk__xe_foreach_child(match, NULL, handle_op_for_node, NULL);
1966
1967 } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
1968 node = crm_element_value(match, XML_ATTR_UNAME);
1969 if (node == NULL) {
1970 node = ID(match);
1971 }
1972 handle_rsc_op(match, (void *) node);
1973
1974 } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
1975 node = ID(match);
1976 handle_rsc_op(match, (void *) node);
1977
1978 } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
1979 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1980
1981 handle_rsc_op(match, local_node);
1982 free(local_node);
1983
1984 } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
1985 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1986
1987 handle_rsc_op(match, local_node);
1988 free(local_node);
1989
1990 } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
1991 char *local_node = pcmk__xpath_node_id(xpath, "lrm");
1992
1993 handle_rsc_op(match, local_node);
1994 free(local_node);
1995
1996 } else {
1997 crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
1998 }
1999 }
2000 }
2001
2002 static void
2003 crm_diff_update_v1(const char *event, xmlNode * msg)
2004 {
2005
2006 xmlXPathObject *xpathObj = xpath_search(msg,
2007 "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
2008 "//" XML_LRM_TAG_RSC_OP);
2009 int lpc = 0, max = numXpathResults(xpathObj);
2010
2011 for (lpc = 0; lpc < max; lpc++) {
2012 xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
2013
2014 handle_rsc_op(rsc_op, NULL);
2015 }
2016 freeXpathObject(xpathObj);
2017 }
2018
2019 static void
2020 crm_diff_update(const char *event, xmlNode * msg)
2021 {
2022 int rc = -1;
2023 static bool stale = FALSE;
2024 gboolean cib_updated = FALSE;
2025 xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
2026
2027 out->progress(out, false);
2028
2029 if (current_cib != NULL) {
2030 rc = xml_apply_patchset(current_cib, diff, TRUE);
2031
2032 switch (rc) {
2033 case -pcmk_err_diff_resync:
2034 case -pcmk_err_diff_failed:
2035 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
2036 free_xml(current_cib); current_cib = NULL;
2037 break;
2038 case pcmk_ok:
2039 cib_updated = TRUE;
2040 break;
2041 default:
2042 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
2043 free_xml(current_cib); current_cib = NULL;
2044 }
2045 }
2046
2047 if (current_cib == NULL) {
2048 crm_trace("Re-requesting the full cib");
2049 cib->cmds->query(cib, NULL, ¤t_cib, cib_scope_local | cib_sync_call);
2050 }
2051
2052 if (options.external_agent) {
2053 int format = 0;
2054 crm_element_value_int(diff, PCMK_XA_FORMAT, &format);
2055 switch(format) {
2056 case 1:
2057 crm_diff_update_v1(event, msg);
2058 break;
2059 case 2:
2060 crm_diff_update_v2(event, msg);
2061 break;
2062 default:
2063 crm_err("Unknown patch format: %d", format);
2064 }
2065 }
2066
2067 if (current_cib == NULL) {
2068 if(!stale) {
2069 out->info(out, "--- Stale data ---");
2070 }
2071 stale = TRUE;
2072 return;
2073 }
2074
2075 stale = FALSE;
2076 refresh_after_event(cib_updated, FALSE);
2077 }
2078
2079 static int
2080 mon_refresh_display(gpointer user_data)
2081 {
2082 int rc = pcmk_rc_ok;
2083
2084 last_refresh = time(NULL);
2085
2086 if (output_format == mon_output_none) {
2087 return G_SOURCE_REMOVE;
2088 }
2089
2090 if (fence_history == pcmk__fence_history_full &&
2091 !pcmk_all_flags_set(show, pcmk_section_fencing_all) &&
2092 output_format != mon_output_xml) {
2093 fence_history = pcmk__fence_history_reduced;
2094 }
2095
2096
2097 if (cib->variant == cib_native) {
2098 pcmk__pacemakerd_status(out, crm_system_name, options.reconnect_ms / 2,
2099 false, &pcmkd_state);
2100 }
2101
2102 if (out->dest != stdout) {
2103 out->reset(out);
2104 }
2105
2106 rc = pcmk__output_cluster_status(out, st, cib, current_cib, pcmkd_state,
2107 fence_history, show, show_opts,
2108 options.only_node,options.only_rsc,
2109 options.neg_location_prefix,
2110 output_format == mon_output_monitor);
2111
2112 if (output_format == mon_output_monitor && rc != pcmk_rc_ok) {
2113 clean_up(MON_STATUS_WARN);
2114 return G_SOURCE_REMOVE;
2115 } else if (rc == pcmk_rc_schema_validation) {
2116 clean_up(CRM_EX_CONFIG);
2117 return G_SOURCE_REMOVE;
2118 }
2119
2120 if (out->dest != stdout) {
2121 out->finish(out, CRM_EX_OK, true, NULL);
2122 }
2123
2124 return G_SOURCE_CONTINUE;
2125 }
2126
2127
2128
2129
2130 static void
2131 mon_st_callback_event(stonith_t * st, stonith_event_t * e)
2132 {
2133 if (st->state == stonith_disconnected) {
2134
2135 mon_cib_connection_destroy(NULL);
2136 } else if (options.external_agent) {
2137 char *desc = stonith__event_description(e);
2138
2139 send_custom_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
2140 free(desc);
2141 }
2142 }
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154 static void
2155 refresh_after_event(gboolean data_updated, gboolean enforce)
2156 {
2157 static int updates = 0;
2158 time_t now = time(NULL);
2159
2160 if (data_updated) {
2161 updates++;
2162 }
2163
2164 if(refresh_timer == NULL) {
2165 refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL);
2166 }
2167
2168 if (reconnect_timer > 0) {
2169
2170 mainloop_timer_stop(refresh_timer);
2171 return;
2172 }
2173
2174
2175
2176
2177
2178 setup_fencer_connection();
2179
2180 if (enforce ||
2181 ((now - last_refresh) > (options.reconnect_ms / 1000)) ||
2182 updates >= 10) {
2183 mainloop_set_trigger((crm_trigger_t *) refresh_trigger);
2184 mainloop_timer_stop(refresh_timer);
2185 updates = 0;
2186
2187 } else {
2188 mainloop_timer_start(refresh_timer);
2189 }
2190 }
2191
2192
2193
2194
2195 static void
2196 mon_st_callback_display(stonith_t * st, stonith_event_t * e)
2197 {
2198 if (st->state == stonith_disconnected) {
2199
2200 mon_cib_connection_destroy(NULL);
2201 } else {
2202 out->progress(out, false);
2203 refresh_after_event(TRUE, FALSE);
2204 }
2205 }
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215 static crm_exit_t
2216 clean_up(crm_exit_t exit_code)
2217 {
2218
2219
2220
2221 cib__clean_up_connection(&cib);
2222 stonith_api_delete(st);
2223 free(options.neg_location_prefix);
2224 free(options.only_node);
2225 free(options.only_rsc);
2226 free(options.pid_file);
2227 g_slist_free_full(options.includes_excludes, free);
2228
2229 g_strfreev(processed_args);
2230
2231
2232
2233
2234
2235
2236 if (((error != NULL) || (exit_code == CRM_EX_USAGE))
2237 && (output_format == mon_output_console)
2238 && (out != NULL)) {
2239
2240 out->finish(out, exit_code, false, NULL);
2241 pcmk__output_free(out);
2242 out = NULL;
2243 }
2244
2245
2246
2247
2248 if (exit_code == CRM_EX_USAGE && (output_format == mon_output_console || output_format == mon_output_plain)) {
2249 char *help = g_option_context_get_help(context, TRUE, NULL);
2250
2251 fprintf(stderr, "%s", help);
2252 g_free(help);
2253 }
2254
2255 pcmk__free_arg_context(context);
2256
2257
2258
2259
2260
2261 if (error != NULL) {
2262 if (out != NULL) {
2263 out->err(out, "%s: %s", g_get_prgname(), error->message);
2264 out->finish(out, exit_code, true, NULL);
2265 pcmk__output_free(out);
2266 } else {
2267 fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
2268 }
2269
2270 g_clear_error(&error);
2271 crm_exit(exit_code);
2272 }
2273
2274
2275
2276
2277 if (out != NULL) {
2278 if (options.exec_mode != mon_exec_daemonized) {
2279 out->finish(out, exit_code, true, NULL);
2280 }
2281
2282 pcmk__output_free(out);
2283 pcmk__unregister_formats();
2284 }
2285
2286 crm_exit(exit_code);
2287 }