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