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