root/tools/crm_mon.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. blank_screen
  2. mon_timer_popped
  3. mon_cib_connection_destroy
  4. mon_shutdown
  5. mon_winresize
  6. cib_connect
  7. get_option_desc
  8. detect_user_input
  9. main
  10. count_resources
  11. print_simple_status
  12. print_nvpair
  13. print_node_start
  14. print_node_end
  15. print_resources_heading
  16. print_resources_closing
  17. print_resources
  18. print_rsc_history_start
  19. print_rsc_history_end
  20. print_op_history
  21. print_rsc_history
  22. print_node_history
  23. print_attr_msg
  24. compare_attribute
  25. create_attr_list
  26. print_node_attribute
  27. print_node_summary
  28. print_ticket
  29. print_cluster_tickets
  30. get_node_display_name
  31. print_ban
  32. print_neg_locations
  33. crm_mon_get_parameters
  34. print_node_attributes
  35. get_resource_display_options
  36. crm_now_string
  37. print_cluster_summary_header
  38. print_cluster_summary_footer
  39. print_cluster_times
  40. print_cluster_stack
  41. print_cluster_dc
  42. print_cluster_counts
  43. print_cluster_options
  44. get_cluster_stack
  45. print_cluster_summary
  46. print_failed_action
  47. print_failed_actions
  48. print_status
  49. print_xml_status
  50. print_html_status
  51. snmp_input
  52. crm_snmp_init
  53. send_snmp_trap
  54. print_recipient_status
  55. event_cb
  56. crm_smtp_debug
  57. send_custom_trap
  58. send_smtp_trap
  59. handle_rsc_op
  60. mon_trigger_refresh
  61. get_node_from_xpath
  62. crm_diff_update_v2
  63. crm_diff_update_v1
  64. crm_diff_update
  65. mon_refresh_display
  66. mon_st_callback
  67. clean_up

   1 /*
   2  * Copyright (C) 2004-2015 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) any later version.
   8  *
   9  * This software is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 
  19 #include <crm_internal.h>
  20 
  21 #include <sys/param.h>
  22 
  23 #include <crm/crm.h>
  24 
  25 #include <stdio.h>
  26 #include <sys/types.h>
  27 #include <sys/stat.h>
  28 #include <unistd.h>
  29 
  30 #include <stdlib.h>
  31 #include <errno.h>
  32 #include <fcntl.h>
  33 #include <libgen.h>
  34 #include <sys/utsname.h>
  35 
  36 #include <crm/msg_xml.h>
  37 #include <crm/services.h>
  38 #include <crm/lrmd.h>
  39 #include <crm/common/internal.h>  /* crm_ends_with_ext */
  40 #include <crm/common/ipc.h>
  41 #include <crm/common/mainloop.h>
  42 #include <crm/common/util.h>
  43 #include <crm/common/xml.h>
  44 
  45 #include <crm/cib/internal.h>
  46 #include <crm/pengine/status.h>
  47 #include <crm/pengine/internal.h>
  48 #include <../lib/pengine/unpack.h>
  49 #include <../pengine/pengine.h>
  50 #include <crm/stonith-ng.h>
  51 
  52 extern void cleanup_alloc_calculations(pe_working_set_t * data_set);
  53 
  54 void clean_up(int rc);
  55 void crm_diff_update(const char *event, xmlNode * msg);
  56 gboolean mon_refresh_display(gpointer user_data);
  57 int cib_connect(gboolean full);
  58 void mon_st_callback(stonith_t * st, stonith_event_t * e);
  59 static char *get_node_display_name(node_t *node);
  60 
  61 /*
  62  * Definitions indicating which items to print
  63  */
  64 
  65 #define mon_show_times      (0x0001U)
  66 #define mon_show_stack      (0x0002U)
  67 #define mon_show_dc         (0x0004U)
  68 #define mon_show_count      (0x0008U)
  69 #define mon_show_nodes      (0x0010U)
  70 #define mon_show_resources  (0x0020U)
  71 #define mon_show_attributes (0x0040U)
  72 #define mon_show_failcounts (0x0080U)
  73 #define mon_show_operations (0x0100U)
  74 #define mon_show_tickets    (0x0200U)
  75 #define mon_show_bans       (0x0400U)
  76 
  77 #define mon_show_headers    (mon_show_times | mon_show_stack | mon_show_dc | mon_show_count)
  78 #define mon_show_default    (mon_show_headers | mon_show_nodes | mon_show_resources)
  79 #define mon_show_all        (mon_show_default | mon_show_attributes | mon_show_failcounts \
  80                      | mon_show_operations | mon_show_tickets | mon_show_bans)
  81 
  82 unsigned int show = mon_show_default;
  83 
  84 /*
  85  * Definitions indicating how to output
  86  */
  87 
  88 enum mon_output_format_e {
  89     mon_output_none,
  90     mon_output_monitor,
  91     mon_output_plain,
  92     mon_output_console,
  93     mon_output_xml,
  94     mon_output_html,
  95     mon_output_cgi
  96 } output_format = mon_output_console;
  97 
  98 char *output_filename = NULL;   /* if sending output to a file, its name */
  99 
 100 /* other globals */
 101 char *xml_file = NULL;
 102 char *pid_file = NULL;
 103 char *snmp_target = NULL;
 104 char *snmp_community = NULL;
 105 
 106 gboolean group_by_node = FALSE;
 107 gboolean inactive_resources = FALSE;
 108 int reconnect_msec = 5000;
 109 gboolean daemonize = FALSE;
 110 GMainLoop *mainloop = NULL;
 111 guint timer_id = 0;
 112 GList *attr_list = NULL;
 113 
 114 const char *crm_mail_host = NULL;
 115 const char *crm_mail_prefix = NULL;
 116 const char *crm_mail_from = NULL;
 117 const char *crm_mail_to = NULL;
 118 const char *external_agent = NULL;
 119 const char *external_recipient = NULL;
 120 
 121 cib_t *cib = NULL;
 122 stonith_t *st = NULL;
 123 xmlNode *current_cib = NULL;
 124 
 125 gboolean one_shot = FALSE;
 126 gboolean has_warnings = FALSE;
 127 gboolean print_timing = FALSE;
 128 gboolean watch_fencing = FALSE;
 129 gboolean print_brief = FALSE;
 130 gboolean print_pending = TRUE;
 131 gboolean print_clone_detail = FALSE;
 132 
 133 /* FIXME allow, detect, and correctly interpret glob pattern or regex? */
 134 const char *print_neg_location_prefix = "";
 135 
 136 /* Never display node attributes whose name starts with one of these prefixes */
 137 #define FILTER_STR { CRM_FAIL_COUNT_PREFIX, CRM_LAST_FAILURE_PREFIX,       \
 138                      "shutdown", "terminate", "standby", "probe_complete", \
 139                      "#", NULL }
 140 
 141 long last_refresh = 0;
 142 crm_trigger_t *refresh_trigger = NULL;
 143 
 144 /*
 145  * 1.3.6.1.4.1.32723 has been assigned to the project by IANA
 146  * http://www.iana.org/assignments/enterprise-numbers
 147  */
 148 #define PACEMAKER_PREFIX "1.3.6.1.4.1.32723"
 149 #define PACEMAKER_TRAP_PREFIX PACEMAKER_PREFIX ".1"
 150 
 151 #define snmp_crm_trap_oid   PACEMAKER_TRAP_PREFIX
 152 #define snmp_crm_oid_node   PACEMAKER_TRAP_PREFIX ".1"
 153 #define snmp_crm_oid_rsc    PACEMAKER_TRAP_PREFIX ".2"
 154 #define snmp_crm_oid_task   PACEMAKER_TRAP_PREFIX ".3"
 155 #define snmp_crm_oid_desc   PACEMAKER_TRAP_PREFIX ".4"
 156 #define snmp_crm_oid_status PACEMAKER_TRAP_PREFIX ".5"
 157 #define snmp_crm_oid_rc     PACEMAKER_TRAP_PREFIX ".6"
 158 #define snmp_crm_oid_trc    PACEMAKER_TRAP_PREFIX ".7"
 159 
 160 /* Define exit codes for monitoring-compatible output */
 161 #define MON_STATUS_OK   (0)
 162 #define MON_STATUS_WARN (1)
 163 
 164 /* Convenience macro for prettifying output (e.g. "node" vs "nodes") */
 165 #define s_if_plural(i) (((i) == 1)? "" : "s")
 166 
 167 #if CURSES_ENABLED
 168 #  define print_dot() if (output_format == mon_output_console) { \
 169         printw(".");                            \
 170         clrtoeol();                             \
 171         refresh();                              \
 172     } else {                                    \
 173         fprintf(stdout, ".");                   \
 174     }
 175 #else
 176 #  define print_dot() fprintf(stdout, ".");
 177 #endif
 178 
 179 #if CURSES_ENABLED
 180 #  define print_as(fmt, args...) if (output_format == mon_output_console) { \
 181         printw(fmt, ##args);                            \
 182         clrtoeol();                                     \
 183         refresh();                                      \
 184     } else {                                            \
 185         fprintf(stdout, fmt, ##args);                   \
 186     }
 187 #else
 188 #  define print_as(fmt, args...) fprintf(stdout, fmt, ##args);
 189 #endif
 190 
 191 static void
 192 blank_screen(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 {
 194 #if CURSES_ENABLED
 195     int lpc = 0;
 196 
 197     for (lpc = 0; lpc < LINES; lpc++) {
 198         move(lpc, 0);
 199         clrtoeol();
 200     }
 201     move(0, 0);
 202     refresh();
 203 #endif
 204 }
 205 
 206 static gboolean
 207 mon_timer_popped(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 208 {
 209     int rc = pcmk_ok;
 210 
 211 #if CURSES_ENABLED
 212     if (output_format == mon_output_console) {
 213         clear();
 214         refresh();
 215     }
 216 #endif
 217 
 218     if (timer_id > 0) {
 219         g_source_remove(timer_id);
 220     }
 221 
 222     print_as("Reconnecting...\n");
 223     rc = cib_connect(TRUE);
 224 
 225     if (rc != pcmk_ok) {
 226         timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
 227     }
 228     return FALSE;
 229 }
 230 
 231 static void
 232 mon_cib_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 233 {
 234     print_as("Connection to the CIB terminated\n");
 235     if (cib) {
 236         cib->cmds->signoff(cib);
 237         timer_id = g_timeout_add(reconnect_msec, mon_timer_popped, NULL);
 238     }
 239     return;
 240 }
 241 
 242 /*
 243  * Mainloop signal handler.
 244  */
 245 static void
 246 mon_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     clean_up(EX_OK);
 249 }
 250 
 251 #if ON_DARWIN
 252 #  define sighandler_t sig_t
 253 #endif
 254 
 255 #if CURSES_ENABLED
 256 #  ifndef HAVE_SIGHANDLER_T
 257 typedef void (*sighandler_t) (int);
 258 #  endif
 259 static sighandler_t ncurses_winch_handler;
 260 static void
 261 mon_winresize(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 262 {
 263     static int not_done;
 264     int lines = 0, cols = 0;
 265 
 266     if (!not_done++) {
 267         if (ncurses_winch_handler)
 268             /* the original ncurses WINCH signal handler does the
 269              * magic of retrieving the new window size;
 270              * otherwise, we'd have to use ioctl or tgetent */
 271             (*ncurses_winch_handler) (SIGWINCH);
 272         getmaxyx(stdscr, lines, cols);
 273         resizeterm(lines, cols);
 274         mainloop_set_trigger(refresh_trigger);
 275     }
 276     not_done--;
 277 }
 278 #endif
 279 
 280 int
 281 cib_connect(gboolean full)
     /* [previous][next][first][last][top][bottom][index][help] */
 282 {
 283     int rc = pcmk_ok;
 284     static gboolean need_pass = TRUE;
 285 
 286     CRM_CHECK(cib != NULL, return -EINVAL);
 287 
 288     if (getenv("CIB_passwd") != NULL) {
 289         need_pass = FALSE;
 290     }
 291 
 292     if (watch_fencing && st == NULL) {
 293         st = stonith_api_new();
 294     }
 295 
 296     if (watch_fencing && st->state == stonith_disconnected) {
 297         crm_trace("Connecting to stonith");
 298         rc = st->cmds->connect(st, crm_system_name, NULL);
 299         if (rc == pcmk_ok) {
 300             crm_trace("Setting up stonith callbacks");
 301             st->cmds->register_notification(st, T_STONITH_NOTIFY_FENCE, mon_st_callback);
 302         }
 303     }
 304 
 305     if (cib->state != cib_connected_query && cib->state != cib_connected_command) {
 306         crm_trace("Connecting to the CIB");
 307         if ((output_format == mon_output_console) && need_pass && (cib->variant == cib_remote)) {
 308             need_pass = FALSE;
 309             print_as("Password:");
 310         }
 311 
 312         rc = cib->cmds->signon(cib, crm_system_name, cib_query);
 313 
 314         if (rc != pcmk_ok) {
 315             return rc;
 316         }
 317 
 318         rc = cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
 319         if (rc == pcmk_ok) {
 320             mon_refresh_display(NULL);
 321         }
 322 
 323         if (rc == pcmk_ok && full) {
 324             if (rc == pcmk_ok) {
 325                 rc = cib->cmds->set_connection_dnotify(cib, mon_cib_connection_destroy);
 326                 if (rc == -EPROTONOSUPPORT) {
 327                     print_as
 328                         ("Notification setup not supported, won't be able to reconnect after failure");
 329                     if (output_format == mon_output_console) {
 330                         sleep(2);
 331                     }
 332                     rc = pcmk_ok;
 333                 }
 334 
 335             }
 336 
 337             if (rc == pcmk_ok) {
 338                 cib->cmds->del_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
 339                 rc = cib->cmds->add_notify_callback(cib, T_CIB_DIFF_NOTIFY, crm_diff_update);
 340             }
 341 
 342             if (rc != pcmk_ok) {
 343                 print_as("Notification setup failed, could not monitor CIB actions");
 344                 if (output_format == mon_output_console) {
 345                     sleep(2);
 346                 }
 347                 clean_up(-rc);
 348             }
 349         }
 350     }
 351     return rc;
 352 }
 353 
 354 /* *INDENT-OFF* */
 355 static struct crm_option long_options[] = {
 356     /* Top-level Options */
 357     {"help",           0, 0, '?', "\tThis text"},
 358     {"version",        0, 0, '$', "\tVersion information"  },
 359     {"verbose",        0, 0, 'V', "\tIncrease debug output"},
 360     {"quiet",          0, 0, 'Q', "\tDisplay only essential output" },
 361 
 362     {"-spacer-",        1, 0, '-', "\nModes (mutually exclusive):"},
 363     {"as-html",        1, 0, 'h', "\tWrite cluster status to the named html file"},
 364     {"as-xml",         0, 0, 'X', "\t\tWrite cluster status as xml to stdout. This will enable one-shot mode."},
 365     {"web-cgi",        0, 0, 'w', "\t\tWeb mode with output suitable for CGI (preselected when run as *.cgi)"},
 366     {"simple-status",  0, 0, 's', "\tDisplay the cluster status once as a simple one line output (suitable for nagios)"},
 367     {"snmp-traps",     1, 0, 'S', "\tSend SNMP traps to this station", !ENABLE_SNMP},
 368     {"snmp-community", 1, 0, 'C', "Specify community for SNMP traps(default is NULL)", !ENABLE_SNMP},
 369     {"mail-to",        1, 0, 'T', "\tSend Mail alerts to this user.  See also --mail-from, --mail-host, --mail-prefix", !ENABLE_ESMTP},
 370 
 371     {"-spacer-",        1, 0, '-', "\nDisplay Options:"},
 372     {"group-by-node",  0, 0, 'n', "\tGroup resources by node"     },
 373     {"inactive",       0, 0, 'r', "\t\tDisplay inactive resources"  },
 374     {"failcounts",     0, 0, 'f', "\tDisplay resource fail counts"},
 375     {"operations",     0, 0, 'o', "\tDisplay resource operation history" },
 376     {"timing-details", 0, 0, 't', "\tDisplay resource operation history with timing details" },
 377     {"tickets",        0, 0, 'c', "\t\tDisplay cluster tickets"},
 378     {"watch-fencing",  0, 0, 'W', "\tListen for fencing events. For use with --external-agent, --mail-to and/or --snmp-traps where supported"},
 379     {"neg-locations",  2, 0, 'L', "Display negative location constraints [optionally filtered by id prefix]"},
 380     {"show-node-attributes", 0, 0, 'A', "Display node attributes" },
 381     {"hide-headers",   0, 0, 'D', "\tHide all headers" },
 382     {"show-detail",    0, 0, 'R', "\tShow more details (node IDs, individual clone instances)" },
 383     {"brief",          0, 0, 'b', "\t\tBrief output" },
 384     {"pending",        0, 0, 'j', "\t\tDisplay pending state if 'record-pending' is enabled", pcmk_option_hidden},
 385 
 386     {"-spacer-",        1, 0, '-', "\nAdditional Options:"},
 387     {"interval",       1, 0, 'i', "\tUpdate frequency in seconds" },
 388     {"one-shot",       0, 0, '1', "\t\tDisplay the cluster status once on the console and exit"},
 389     {"disable-ncurses",0, 0, 'N', "\tDisable the use of ncurses", !CURSES_ENABLED},
 390     {"daemonize",      0, 0, 'd', "\tRun in the background as a daemon"},
 391     {"pid-file",       1, 0, 'p', "\t(Advanced) Daemon pid file location"},
 392     {"mail-from",      1, 0, 'F', "\tMail alerts should come from the named user", !ENABLE_ESMTP},
 393     {"mail-host",      1, 0, 'H', "\tMail alerts should be sent via the named host", !ENABLE_ESMTP},
 394     {"mail-prefix",    1, 0, 'P', "Subjects for mail alerts should start with this string", !ENABLE_ESMTP},
 395     {"external-agent",    1, 0, 'E', "A program to run when resource operations take place."},
 396     {"external-recipient",1, 0, 'e', "A recipient for your program (assuming you want the program to send something to someone)."},
 397 
 398 
 399     {"xml-file",       1, 0, 'x', NULL, pcmk_option_hidden},
 400 
 401     {"-spacer-",        1, 0, '-', "\nExamples:", pcmk_option_paragraph},
 402     {"-spacer-",        1, 0, '-', "Display the cluster status on the console with updates as they occur:", pcmk_option_paragraph},
 403     {"-spacer-",        1, 0, '-', " crm_mon", pcmk_option_example},
 404     {"-spacer-",        1, 0, '-', "Display the cluster status on the console just once then exit:", pcmk_option_paragraph},
 405     {"-spacer-",        1, 0, '-', " crm_mon -1", pcmk_option_example},
 406     {"-spacer-",        1, 0, '-', "Display your cluster status, group resources by node, and include inactive resources in the list:", pcmk_option_paragraph},
 407     {"-spacer-",        1, 0, '-', " crm_mon --group-by-node --inactive", pcmk_option_example},
 408     {"-spacer-",        1, 0, '-', "Start crm_mon as a background daemon and have it write the cluster status to an HTML file:", pcmk_option_paragraph},
 409     {"-spacer-",        1, 0, '-', " crm_mon --daemonize --as-html /path/to/docroot/filename.html", pcmk_option_example},
 410     {"-spacer-",        1, 0, '-', "Start crm_mon and export the current cluster status as xml to stdout, then exit.:", pcmk_option_paragraph},
 411     {"-spacer-",        1, 0, '-', " crm_mon --as-xml", pcmk_option_example},
 412     {"-spacer-",        1, 0, '-', "Start crm_mon as a background daemon and have it send email alerts:", pcmk_option_paragraph|!ENABLE_ESMTP},
 413     {"-spacer-",        1, 0, '-', " crm_mon --daemonize --mail-to user@example.com --mail-host mail.example.com", pcmk_option_example|!ENABLE_ESMTP},
 414     {"-spacer-",        1, 0, '-', "Start crm_mon as a background daemon and have it send SNMP alerts:", pcmk_option_paragraph|!ENABLE_SNMP},
 415     {"-spacer-",        1, 0, '-', " crm_mon --daemonize --snmp-traps snmptrapd.example.com", pcmk_option_example|!ENABLE_SNMP},
 416 
 417     {NULL, 0, 0, 0}
 418 };
 419 /* *INDENT-ON* */
 420 
 421 #if CURSES_ENABLED
 422 static const char *
 423 get_option_desc(char c)
     /* [previous][next][first][last][top][bottom][index][help] */
 424 {
 425     int lpc;
 426 
 427     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
 428 
 429         if (long_options[lpc].name[0] == '-')
 430             continue;
 431 
 432         if (long_options[lpc].val == c) {
 433             const char * tab = NULL;
 434             tab = strrchr(long_options[lpc].desc, '\t');
 435             return tab ? ++tab : long_options[lpc].desc;
 436         }
 437     }
 438 
 439     return NULL;
 440 }
 441 
 442 #define print_option_help(option, condition) \
 443     print_as("%c %c: \t%s\n", ((condition)? '*': ' '), option, get_option_desc(option));
 444 
 445 static gboolean
 446 detect_user_input(GIOChannel *channel, GIOCondition condition, gpointer unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 {
 448     int c;
 449     gboolean config_mode = FALSE;
 450 
 451     while (1) {
 452 
 453         /* Get user input */
 454         c = getchar();
 455 
 456         switch (c) {
 457             case 'c':
 458                 show ^= mon_show_tickets;
 459                 break;
 460             case 'f':
 461                 show ^= mon_show_failcounts;
 462                 break;
 463             case 'n':
 464                 group_by_node = ! group_by_node;
 465                 break;
 466             case 'o':
 467                 show ^= mon_show_operations;
 468                 if ((show & mon_show_operations) == 0) {
 469                     print_timing = 0;
 470                 }
 471                 break;
 472             case 'r':
 473                 inactive_resources = ! inactive_resources;
 474                 break;
 475             case 'R':
 476                 print_clone_detail = ! print_clone_detail;
 477                 break;
 478             case 't':
 479                 print_timing = ! print_timing;
 480                 if (print_timing) {
 481                     show |= mon_show_operations;
 482                 }
 483                 break;
 484             case 'A':
 485                 show ^= mon_show_attributes;
 486                 break;
 487             case 'L':
 488                 show ^= mon_show_bans;
 489                 break;
 490             case 'D':
 491                 /* If any header is shown, clear them all, otherwise set them all */
 492                 if (show & mon_show_headers) {
 493                     show &= ~mon_show_headers;
 494                 } else {
 495                     show |= mon_show_headers;
 496                 }
 497                 break;
 498             case 'b':
 499                 print_brief = ! print_brief;
 500                 break;
 501             case 'j':
 502                 print_pending = ! print_pending;
 503                 break;
 504             case '?':
 505                 config_mode = TRUE;
 506                 break;
 507             default:
 508                 goto refresh;
 509         }
 510 
 511         if (!config_mode)
 512             goto refresh;
 513 
 514         blank_screen();
 515 
 516         print_as("Display option change mode\n");
 517         print_as("\n");
 518         print_option_help('c', show & mon_show_tickets);
 519         print_option_help('f', show & mon_show_failcounts);
 520         print_option_help('n', group_by_node);
 521         print_option_help('o', show & mon_show_operations);
 522         print_option_help('r', inactive_resources);
 523         print_option_help('t', print_timing);
 524         print_option_help('A', show & mon_show_attributes);
 525         print_option_help('L', show & mon_show_bans);
 526         print_option_help('D', (show & mon_show_headers) == 0);
 527         print_option_help('R', print_clone_detail);
 528         print_option_help('b', print_brief);
 529         print_option_help('j', print_pending);
 530         print_as("\n");
 531         print_as("Toggle fields via field letter, type any other key to return");
 532     }
 533 
 534 refresh:
 535     mon_refresh_display(NULL);
 536     return TRUE;
 537 }
 538 #endif
 539 
 540 int
 541 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 542 {
 543     int flag;
 544     int argerr = 0;
 545     int exit_code = 0;
 546     int option_index = 0;
 547 
 548     pid_file = strdup("/tmp/ClusterMon.pid");
 549     crm_log_cli_init("crm_mon");
 550     crm_set_options(NULL, "mode [options]", long_options,
 551                     "Provides a summary of cluster's current state."
 552                     "\n\nOutputs varying levels of detail in a number of different formats.\n");
 553 
 554 #if !defined (ON_DARWIN) && !defined (ON_BSD)
 555     /* prevent zombies */
 556     signal(SIGCLD, SIG_IGN);
 557 #endif
 558 
 559     if (crm_ends_with_ext(argv[0], ".cgi") == TRUE) {
 560         output_format = mon_output_cgi;
 561         one_shot = TRUE;
 562     }
 563 
 564     while (1) {
 565         flag = crm_get_option(argc, argv, &option_index);
 566         if (flag == -1)
 567             break;
 568 
 569         switch (flag) {
 570             case 'V':
 571                 crm_bump_log_level(argc, argv);
 572                 break;
 573             case 'Q':
 574                 show &= ~mon_show_times;
 575                 break;
 576             case 'i':
 577                 reconnect_msec = crm_get_msec(optarg);
 578                 break;
 579             case 'n':
 580                 group_by_node = TRUE;
 581                 break;
 582             case 'r':
 583                 inactive_resources = TRUE;
 584                 break;
 585             case 'W':
 586                 watch_fencing = TRUE;
 587                 break;
 588             case 'd':
 589                 daemonize = TRUE;
 590                 break;
 591             case 't':
 592                 print_timing = TRUE;
 593                 show |= mon_show_operations;
 594                 break;
 595             case 'o':
 596                 show |= mon_show_operations;
 597                 break;
 598             case 'f':
 599                 show |= mon_show_failcounts;
 600                 break;
 601             case 'A':
 602                 show |= mon_show_attributes;
 603                 break;
 604             case 'L':
 605                 show |= mon_show_bans;
 606                 print_neg_location_prefix = optarg? optarg : "";
 607                 break;
 608             case 'D':
 609                 show &= ~mon_show_headers;
 610                 break;
 611             case 'b':
 612                 print_brief = TRUE;
 613                 break;
 614             case 'j':
 615                 print_pending = TRUE;
 616                 break;
 617             case 'R':
 618                 print_clone_detail = TRUE;
 619                 break;
 620             case 'c':
 621                 show |= mon_show_tickets;
 622                 break;
 623             case 'p':
 624                 free(pid_file);
 625                 if(optarg == NULL) {
 626                     return crm_help(flag, EX_USAGE);
 627                 }
 628                 pid_file = strdup(optarg);
 629                 break;
 630             case 'x':
 631                 if(optarg == NULL) {
 632                     return crm_help(flag, EX_USAGE);
 633                 }
 634                 xml_file = strdup(optarg);
 635                 one_shot = TRUE;
 636                 break;
 637             case 'h':
 638                 if(optarg == NULL) {
 639                     return crm_help(flag, EX_USAGE);
 640                 }
 641                 argerr += (output_format != mon_output_console);
 642                 output_format = mon_output_html;
 643                 output_filename = strdup(optarg);
 644                 umask(S_IWGRP | S_IWOTH);
 645                 break;
 646             case 'X':
 647                 argerr += (output_format != mon_output_console);
 648                 output_format = mon_output_xml;
 649                 one_shot = TRUE;
 650                 break;
 651             case 'w':
 652                 /* do not allow argv[0] and argv[1...] redundancy */
 653                 argerr += (output_format != mon_output_console);
 654                 output_format = mon_output_cgi;
 655                 one_shot = TRUE;
 656                 break;
 657             case 's':
 658                 argerr += (output_format != mon_output_console);
 659                 output_format = mon_output_monitor;
 660                 one_shot = TRUE;
 661                 break;
 662             case 'S':
 663                 snmp_target = optarg;
 664                 break;
 665             case 'T':
 666                 crm_mail_to = optarg;
 667                 break;
 668             case 'F':
 669                 crm_mail_from = optarg;
 670                 break;
 671             case 'H':
 672                 crm_mail_host = optarg;
 673                 break;
 674             case 'P':
 675                 crm_mail_prefix = optarg;
 676                 break;
 677             case 'E':
 678                 external_agent = optarg;
 679                 break;
 680             case 'e':
 681                 external_recipient = optarg;
 682                 break;
 683             case '1':
 684                 one_shot = TRUE;
 685                 break;
 686             case 'N':
 687                 if (output_format == mon_output_console) {
 688                     output_format = mon_output_plain;
 689                 }
 690                 break;
 691             case 'C':
 692                 snmp_community = optarg;
 693                 break;
 694             case '$':
 695             case '?':
 696                 return crm_help(flag, EX_OK);
 697                 break;
 698             default:
 699                 printf("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
 700                 ++argerr;
 701                 break;
 702         }
 703     }
 704 
 705     /* Extra sanity checks when in CGI mode */
 706     if (output_format == mon_output_cgi) {
 707         argerr += (optind < argc);
 708         argerr += (output_filename != NULL);
 709         argerr += (xml_file != NULL);
 710         argerr += (snmp_target != NULL);
 711         argerr += (crm_mail_to != NULL);
 712         argerr += (external_agent != NULL);
 713         argerr += (daemonize == TRUE);  /* paranoia */
 714 
 715     } else if (optind < argc) {
 716         printf("non-option ARGV-elements: ");
 717         while (optind < argc)
 718             printf("%s ", argv[optind++]);
 719         printf("\n");
 720     }
 721 
 722     if (argerr) {
 723         if (output_format == mon_output_cgi) {
 724             fprintf(stdout, "Content-Type: text/plain\n"
 725                             "Status: 500\n\n");
 726             return EX_USAGE;
 727         }
 728         return crm_help('?', EX_USAGE);
 729     }
 730 
 731     /* XML output always prints everything */
 732     if (output_format == mon_output_xml) {
 733         show = mon_show_all;
 734         print_timing = TRUE;
 735     }
 736 
 737     if (one_shot) {
 738         if (output_format == mon_output_console) {
 739             output_format = mon_output_plain;
 740         }
 741 
 742     } else if (daemonize) {
 743         if ((output_format == mon_output_console) || (output_format == mon_output_plain)) {
 744             output_format = mon_output_none;
 745         }
 746         crm_enable_stderr(FALSE);
 747 
 748         if ((output_format != mon_output_html) && (output_format != mon_output_xml)
 749             && !snmp_target && !crm_mail_to && !external_agent) {
 750             printf
 751                 ("Looks like you forgot to specify one or more of: --as-html, --as-xml, --mail-to, --snmp-target, --external-agent\n");
 752             return crm_help('?', EX_USAGE);
 753         }
 754 
 755         crm_make_daemon(crm_system_name, TRUE, pid_file);
 756 
 757     } else if (output_format == mon_output_console) {
 758 #if CURSES_ENABLED
 759         initscr();
 760         cbreak();
 761         noecho();
 762         crm_enable_stderr(FALSE);
 763 #else
 764         one_shot = TRUE;
 765         output_format = mon_output_plain;
 766         printf("Defaulting to one-shot mode\n");
 767         printf("You need to have curses available at compile time to enable console mode\n");
 768 #endif
 769     }
 770 
 771     crm_info("Starting %s", crm_system_name);
 772     if (xml_file != NULL) {
 773         current_cib = filename2xml(xml_file);
 774         mon_refresh_display(NULL);
 775         return exit_code;
 776     }
 777 
 778     if (current_cib == NULL) {
 779         cib = cib_new();
 780 
 781         do {
 782             if (!one_shot) {
 783                 print_as("Attempting connection to the cluster...\n");
 784             }
 785             exit_code = cib_connect(!one_shot);
 786 
 787             if (one_shot) {
 788                 break;
 789 
 790             } else if (exit_code != pcmk_ok) {
 791                 sleep(reconnect_msec / 1000);
 792 #if CURSES_ENABLED
 793                 if (output_format == mon_output_console) {
 794                     clear();
 795                     refresh();
 796                 }
 797 #endif
 798             }
 799 
 800         } while (exit_code == -ENOTCONN);
 801 
 802         if (exit_code != pcmk_ok) {
 803             if (output_format == mon_output_monitor) {
 804                 printf("CLUSTER WARN: Connection to cluster failed: %s\n", pcmk_strerror(exit_code));
 805                 clean_up(MON_STATUS_WARN);
 806             } else {
 807                 print_as("\nConnection to cluster failed: %s\n", pcmk_strerror(exit_code));
 808             }
 809             if (output_format == mon_output_console) {
 810                 sleep(2);
 811             }
 812             clean_up(-exit_code);
 813         }
 814     }
 815 
 816     if (one_shot) {
 817         return exit_code;
 818     }
 819 
 820     mainloop = g_main_new(FALSE);
 821 
 822     mainloop_add_signal(SIGTERM, mon_shutdown);
 823     mainloop_add_signal(SIGINT, mon_shutdown);
 824 #if CURSES_ENABLED
 825     if (output_format == mon_output_console) {
 826         ncurses_winch_handler = signal(SIGWINCH, mon_winresize);
 827         if (ncurses_winch_handler == SIG_DFL ||
 828             ncurses_winch_handler == SIG_IGN || ncurses_winch_handler == SIG_ERR)
 829             ncurses_winch_handler = NULL;
 830         g_io_add_watch(g_io_channel_unix_new(STDIN_FILENO), G_IO_IN, detect_user_input, NULL);
 831     }
 832 #endif
 833     refresh_trigger = mainloop_add_trigger(G_PRIORITY_LOW, mon_refresh_display, NULL);
 834 
 835     g_main_run(mainloop);
 836     g_main_destroy(mainloop);
 837 
 838     crm_info("Exiting %s", crm_system_name);
 839 
 840     clean_up(0);
 841     return 0;                   /* never reached */
 842 }
 843 
 844 #define mon_warn(fmt...) do {                   \
 845         if (!has_warnings) {                    \
 846             print_as("CLUSTER WARN:");          \
 847         } else {                                \
 848             print_as(",");                      \
 849         }                                       \
 850         print_as(fmt);                          \
 851         has_warnings = TRUE;                    \
 852     } while(0)
 853 
 854 static int
 855 count_resources(pe_working_set_t * data_set, resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 856 {
 857     int count = 0;
 858     GListPtr gIter = NULL;
 859 
 860     if (rsc == NULL) {
 861         gIter = data_set->resources;
 862     } else if (rsc->children) {
 863         gIter = rsc->children;
 864     } else {
 865         return is_not_set(rsc->flags, pe_rsc_orphan);
 866     }
 867 
 868     for (; gIter != NULL; gIter = gIter->next) {
 869         count += count_resources(data_set, gIter->data);
 870     }
 871     return count;
 872 }
 873 
 874 /*!
 875  * \internal
 876  * \brief Print one-line status suitable for use with monitoring software
 877  *
 878  * \param[in] data_set  Working set of CIB state
 879  *
 880  * \note This function's output (and the return code when the program exits)
 881  *       should conform to https://www.monitoring-plugins.org/doc/guidelines.html
 882  */
 883 static void
 884 print_simple_status(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 885 {
 886     GListPtr gIter = NULL;
 887     int nodes_online = 0;
 888     int nodes_standby = 0;
 889     int nodes_maintenance = 0;
 890 
 891     if (data_set->dc_node == NULL) {
 892         mon_warn(" No DC");
 893     }
 894 
 895     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
 896         node_t *node = (node_t *) gIter->data;
 897 
 898         if (node->details->standby && node->details->online) {
 899             nodes_standby++;
 900         } else if (node->details->maintenance && node->details->online) {
 901             nodes_maintenance++;
 902         } else if (node->details->online) {
 903             nodes_online++;
 904         } else {
 905             mon_warn(" offline node: %s", node->details->uname);
 906         }
 907     }
 908 
 909     if (!has_warnings) {
 910         int nresources = count_resources(data_set, NULL);
 911 
 912         print_as("CLUSTER OK: %d node%s online", nodes_online, s_if_plural(nodes_online));
 913         if (nodes_standby > 0) {
 914             print_as(", %d standby node%s", nodes_standby, s_if_plural(nodes_standby));
 915         }
 916         if (nodes_maintenance > 0) {
 917             print_as(", %d maintenance node%s", nodes_maintenance, s_if_plural(nodes_maintenance));
 918         }
 919         print_as(", %d resource%s configured", nresources, s_if_plural(nresources));
 920     }
 921 
 922     print_as("\n");
 923 }
 924 
 925 /*!
 926  * \internal
 927  * \brief Print a [name]=[value][units] pair, optionally using time string
 928  *
 929  * \param[in] stream      File stream to display output to
 930  * \param[in] name        Name to display
 931  * \param[in] value       Value to display (or NULL to convert time instead)
 932  * \param[in] units       Units to display (or NULL for no units)
 933  * \param[in] epoch_time  Epoch time to convert if value is NULL
 934  */
 935 static void
 936 print_nvpair(FILE *stream, const char *name, const char *value,
     /* [previous][next][first][last][top][bottom][index][help] */
 937              const char *units, time_t epoch_time)
 938 {
 939     /* print name= */
 940     switch (output_format) {
 941         case mon_output_plain:
 942         case mon_output_console:
 943             print_as(" %s=", name);
 944             break;
 945 
 946         case mon_output_html:
 947         case mon_output_cgi:
 948         case mon_output_xml:
 949             fprintf(stream, " %s=", name);
 950             break;
 951 
 952         default:
 953             break;
 954     }
 955 
 956     /* If we have a value (and optionally units), print it */
 957     if (value) {
 958         switch (output_format) {
 959             case mon_output_plain:
 960             case mon_output_console:
 961                 print_as("%s%s", value, (units? units : ""));
 962                 break;
 963 
 964             case mon_output_html:
 965             case mon_output_cgi:
 966                 fprintf(stream, "%s%s", value, (units? units : ""));
 967                 break;
 968 
 969             case mon_output_xml:
 970                 fprintf(stream, "\"%s%s\"", value, (units? units : ""));
 971                 break;
 972 
 973             default:
 974                 break;
 975         }
 976 
 977     /* Otherwise print user-friendly time string */
 978     } else {
 979         static char empty_str[] = "";
 980         char *c, *date_str = asctime(localtime(&epoch_time));
 981 
 982         for (c = (date_str != NULL) ? date_str : empty_str; *c != '\0'; ++c) {
 983             if (*c == '\n') {
 984                 *c = '\0';
 985                 break;
 986             }
 987         }
 988         switch (output_format) {
 989             case mon_output_plain:
 990             case mon_output_console:
 991                 print_as("'%s'", date_str);
 992                 break;
 993 
 994             case mon_output_html:
 995             case mon_output_cgi:
 996             case mon_output_xml:
 997                 fprintf(stream, "\"%s\"", date_str);
 998                 break;
 999 
1000             default:
1001                 break;
1002         }
1003     }
1004 }
1005 
1006 /*!
1007  * \internal
1008  * \brief Print whatever is needed to start a node section
1009  *
1010  * \param[in] stream     File stream to display output to
1011  * \param[in] node       Node to print
1012  */
1013 static void
1014 print_node_start(FILE *stream, node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1015 {
1016     char *node_name;
1017 
1018     switch (output_format) {
1019         case mon_output_plain:
1020         case mon_output_console:
1021             node_name = get_node_display_name(node);
1022             print_as("* Node %s:\n", node_name);
1023             free(node_name);
1024             break;
1025 
1026         case mon_output_html:
1027         case mon_output_cgi:
1028             node_name = get_node_display_name(node);
1029             fprintf(stream, "  <h3>Node: %s</h3>\n  <ul>\n", node_name);
1030             free(node_name);
1031             break;
1032 
1033         case mon_output_xml:
1034             fprintf(stream, "        <node name=\"%s\">\n", node->details->uname);
1035             break;
1036 
1037         default:
1038             break;
1039     }
1040 }
1041 
1042 /*!
1043  * \internal
1044  * \brief Print whatever is needed to end a node section
1045  *
1046  * \param[in] stream     File stream to display output to
1047  */
1048 static void
1049 print_node_end(FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
1050 {
1051     switch (output_format) {
1052         case mon_output_html:
1053         case mon_output_cgi:
1054             fprintf(stream, "  </ul>\n");
1055             break;
1056 
1057         case mon_output_xml:
1058             fprintf(stream, "        </node>\n");
1059             break;
1060 
1061         default:
1062             break;
1063     }
1064 }
1065 
1066 /*!
1067  * \internal
1068  * \brief Print resources section heading appropriate to options
1069  *
1070  * \param[in] stream      File stream to display output to
1071  */
1072 static void
1073 print_resources_heading(FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
1074 {
1075     const char *heading;
1076 
1077     if (group_by_node) {
1078 
1079         /* Active resources have already been printed by node */
1080         heading = (inactive_resources? "Inactive resources" : NULL);
1081 
1082     } else if (inactive_resources) {
1083         heading = "Full list of resources";
1084 
1085     } else {
1086         heading = "Active resources";
1087     }
1088 
1089     /* Print section heading */
1090     switch (output_format) {
1091         case mon_output_plain:
1092         case mon_output_console:
1093             print_as("\n%s:\n\n", heading);
1094             break;
1095 
1096         case mon_output_html:
1097         case mon_output_cgi:
1098             fprintf(stream, " <hr />\n <h2>%s</h2>\n", heading);
1099             break;
1100 
1101         case mon_output_xml:
1102             fprintf(stream, "    <resources>\n");
1103             break;
1104 
1105         default:
1106             break;
1107     }
1108 
1109 }
1110 
1111 /*!
1112  * \internal
1113  * \brief Print whatever resource section closing is appropriate
1114  *
1115  * \param[in] stream     File stream to display output to
1116  */
1117 static void
1118 print_resources_closing(FILE *stream, gboolean printed_heading)
     /* [previous][next][first][last][top][bottom][index][help] */
1119 {
1120     const char *heading;
1121 
1122     /* What type of resources we did or did not display */
1123     if (group_by_node) {
1124         heading = "inactive ";
1125     } else if (inactive_resources) {
1126         heading = "";
1127     } else {
1128         heading = "active ";
1129     }
1130 
1131     switch (output_format) {
1132         case mon_output_plain:
1133         case mon_output_console:
1134             if (!printed_heading) {
1135                 print_as("\nNo %sresources\n\n", heading);
1136             }
1137             break;
1138 
1139         case mon_output_html:
1140         case mon_output_cgi:
1141             if (!printed_heading) {
1142                 fprintf(stream, " <hr />\n <h2>No %sresources</h2>\n", heading);
1143             }
1144             break;
1145 
1146         case mon_output_xml:
1147             fprintf(stream, "    %s\n",
1148                     (printed_heading? "</resources>" : "<resources/>"));
1149             break;
1150 
1151         default:
1152             break;
1153     }
1154 }
1155 
1156 /*!
1157  * \internal
1158  * \brief Print whatever resource section(s) are appropriate
1159  *
1160  * \param[in] stream     File stream to display output to
1161  * \param[in] data_set   Cluster state to display
1162  * \param[in] print_opts  Bitmask of pe_print_options
1163  */
1164 static void
1165 print_resources(FILE *stream, pe_working_set_t *data_set, int print_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
1166 {
1167     GListPtr rsc_iter;
1168     const char *prefix = NULL;
1169     gboolean printed_heading = FALSE;
1170     gboolean brief_output = print_brief;
1171 
1172     /* If we already showed active resources by node, and
1173      * we're not showing inactive resources, we have nothing to do
1174      */
1175     if (group_by_node && !inactive_resources) {
1176         return;
1177     }
1178 
1179     /* XML uses an indent, and ignores brief option for resources */
1180     if (output_format == mon_output_xml) {
1181         prefix = "        ";
1182         brief_output = FALSE;
1183     }
1184 
1185     /* If we haven't already printed resources grouped by node,
1186      * and brief output was requested, print resource summary */
1187     if (brief_output && !group_by_node) {
1188         print_resources_heading(stream);
1189         printed_heading = TRUE;
1190         print_rscs_brief(data_set->resources, NULL, print_opts, stream,
1191                          inactive_resources);
1192     }
1193 
1194     /* For each resource, display it if appropriate */
1195     for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
1196         resource_t *rsc = (resource_t *) rsc_iter->data;
1197 
1198         /* Complex resources may have some sub-resources active and some inactive */
1199         gboolean is_active = rsc->fns->active(rsc, TRUE);
1200         gboolean partially_active = rsc->fns->active(rsc, FALSE);
1201 
1202         /* Skip inactive orphans (deleted but still in CIB) */
1203         if (is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
1204             continue;
1205 
1206         /* Skip active resources if we already displayed them by node */
1207         } else if (group_by_node) {
1208             if (is_active) {
1209                 continue;
1210             }
1211 
1212         /* Skip primitives already counted in a brief summary */
1213         } else if (brief_output && (rsc->variant == pe_native)) {
1214             continue;
1215 
1216         /* Skip resources that aren't at least partially active,
1217          * unless we're displaying inactive resources
1218          */
1219         } else if (!partially_active && !inactive_resources) {
1220             continue;
1221         }
1222 
1223         /* Print this resource */
1224         if (printed_heading == FALSE) {
1225             print_resources_heading(stream);
1226             printed_heading = TRUE;
1227         }
1228         rsc->fns->print(rsc, prefix, print_opts, stream);
1229     }
1230 
1231     print_resources_closing(stream, printed_heading);
1232 }
1233 
1234 /*!
1235  * \internal
1236  * \brief Print heading for resource history
1237  *
1238  * \param[in] stream      File stream to display output to
1239  * \param[in] data_set    Current state of CIB
1240  * \param[in] node        Node that ran this resource
1241  * \param[in] rsc         Resource to print
1242  * \param[in] rsc_id      ID of resource to print
1243  * \param[in] all         Whether to print every resource or just failed ones
1244  */
1245 static void
1246 print_rsc_history_start(FILE *stream, pe_working_set_t *data_set, node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1247                         resource_t *rsc, const char *rsc_id, gboolean all)
1248 {
1249     time_t last_failure = 0;
1250     int failcount = rsc?
1251                     pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
1252                                      NULL, data_set)
1253                     : 0;
1254 
1255     if (!all && !failcount && (last_failure <= 0)) {
1256         return;
1257     }
1258 
1259     /* Print resource ID */
1260     switch (output_format) {
1261         case mon_output_plain:
1262         case mon_output_console:
1263             print_as("   %s:", rsc_id);
1264             break;
1265 
1266         case mon_output_html:
1267         case mon_output_cgi:
1268             fprintf(stream, "   <li>%s:", rsc_id);
1269             break;
1270 
1271         case mon_output_xml:
1272             fprintf(stream, "            <resource_history id=\"%s\"", rsc_id);
1273             break;
1274 
1275         default:
1276             break;
1277     }
1278 
1279     /* If resource is an orphan, that's all we can say about it */
1280     if (rsc == NULL) {
1281         switch (output_format) {
1282             case mon_output_plain:
1283             case mon_output_console:
1284                 print_as(" orphan");
1285                 break;
1286 
1287             case mon_output_html:
1288             case mon_output_cgi:
1289                 fprintf(stream, " orphan");
1290                 break;
1291 
1292             case mon_output_xml:
1293                 fprintf(stream, " orphan=\"true\"");
1294                 break;
1295 
1296             default:
1297                 break;
1298         }
1299 
1300     /* If resource is not an orphan, print some details */
1301     } else if (all || failcount || (last_failure > 0)) {
1302 
1303         /* Print migration threshold */
1304         switch (output_format) {
1305             case mon_output_plain:
1306             case mon_output_console:
1307                 print_as(" migration-threshold=%d", rsc->migration_threshold);
1308                 break;
1309 
1310             case mon_output_html:
1311             case mon_output_cgi:
1312                 fprintf(stream, " migration-threshold=%d", rsc->migration_threshold);
1313                 break;
1314 
1315             case mon_output_xml:
1316                 fprintf(stream, " orphan=\"false\" migration-threshold=\"%d\"",
1317                         rsc->migration_threshold);
1318                 break;
1319 
1320             default:
1321                 break;
1322         }
1323 
1324         /* Print fail count if any */
1325         if (failcount > 0) {
1326             switch (output_format) {
1327                 case mon_output_plain:
1328                 case mon_output_console:
1329                     print_as(" " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
1330                     break;
1331 
1332                 case mon_output_html:
1333                 case mon_output_cgi:
1334                     fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=%d", failcount);
1335                     break;
1336 
1337                 case mon_output_xml:
1338                     fprintf(stream, " " CRM_FAIL_COUNT_PREFIX "=\"%d\"",
1339                             failcount);
1340                     break;
1341 
1342                 default:
1343                     break;
1344             }
1345         }
1346 
1347         /* Print last failure time if any */
1348         if (last_failure > 0) {
1349             print_nvpair(stream, CRM_LAST_FAILURE_PREFIX, NULL, NULL,
1350                          last_failure);
1351         }
1352     }
1353 
1354     /* End the heading */
1355     switch (output_format) {
1356         case mon_output_plain:
1357         case mon_output_console:
1358             print_as("\n");
1359             break;
1360 
1361         case mon_output_html:
1362         case mon_output_cgi:
1363             fprintf(stream, "\n    <ul>\n");
1364             break;
1365 
1366         case mon_output_xml:
1367             fprintf(stream, ">\n");
1368             break;
1369 
1370         default:
1371             break;
1372     }
1373 }
1374 
1375 /*!
1376  * \internal
1377  * \brief Print closing for resource history
1378  *
1379  * \param[in] stream      File stream to display output to
1380  */
1381 static void
1382 print_rsc_history_end(FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
1383 {
1384     switch (output_format) {
1385         case mon_output_html:
1386         case mon_output_cgi:
1387             fprintf(stream, "    </ul>\n   </li>\n");
1388             break;
1389 
1390         case mon_output_xml:
1391             fprintf(stream, "            </resource_history>\n");
1392             break;
1393 
1394         default:
1395             break;
1396     }
1397 }
1398 
1399 /*!
1400  * \internal
1401  * \brief Print operation history
1402  *
1403  * \param[in] stream      File stream to display output to
1404  * \param[in] data_set    Current state of CIB
1405  * \param[in] node        Node this operation is for
1406  * \param[in] xml_op      Root of XML tree describing this operation
1407  * \param[in] task        Task parsed from this operation's XML
1408  * \param[in] interval    Interval parsed from this operation's XML
1409  * \param[in] rc          Return code parsed from this operation's XML
1410  */
1411 static void
1412 print_op_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1413                  xmlNode *xml_op, const char *task, const char *interval, int rc)
1414 {
1415     const char *value = NULL;
1416     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
1417 
1418     /* Begin the operation description */
1419     switch (output_format) {
1420         case mon_output_plain:
1421         case mon_output_console:
1422             print_as("    + (%s) %s:", call, task);
1423             break;
1424 
1425         case mon_output_html:
1426         case mon_output_cgi:
1427             fprintf(stream, "     <li>(%s) %s:", call, task);
1428             break;
1429 
1430         case mon_output_xml:
1431             fprintf(stream, "                <operation_history call=\"%s\" task=\"%s\"",
1432                     call, task);
1433             break;
1434 
1435         default:
1436             break;
1437     }
1438 
1439     /* Add name=value pairs as appropriate */
1440     if (safe_str_neq(interval, "0")) {
1441         print_nvpair(stream, "interval", interval, "ms", 0);
1442     }
1443     if (print_timing) {
1444         int int_value;
1445         const char *attr;
1446 
1447         attr = XML_RSC_OP_LAST_CHANGE;
1448         value = crm_element_value(xml_op, attr);
1449         if (value) {
1450             int_value = crm_parse_int(value, NULL);
1451             if (int_value > 0) {
1452                 print_nvpair(stream, attr, NULL, NULL, int_value);
1453             }
1454         }
1455 
1456         attr = XML_RSC_OP_LAST_RUN;
1457         value = crm_element_value(xml_op, attr);
1458         if (value) {
1459             int_value = crm_parse_int(value, NULL);
1460             if (int_value > 0) {
1461                 print_nvpair(stream, attr, NULL, NULL, int_value);
1462             }
1463         }
1464 
1465         attr = XML_RSC_OP_T_EXEC;
1466         value = crm_element_value(xml_op, attr);
1467         if (value) {
1468             print_nvpair(stream, attr, value, "ms", 0);
1469         }
1470 
1471         attr = XML_RSC_OP_T_QUEUE;
1472         value = crm_element_value(xml_op, attr);
1473         if (value) {
1474             print_nvpair(stream, attr, value, "ms", 0);
1475         }
1476     }
1477 
1478     /* End the operation description */
1479     switch (output_format) {
1480         case mon_output_plain:
1481         case mon_output_console:
1482             print_as(" rc=%d (%s)\n", rc, services_ocf_exitcode_str(rc));
1483             break;
1484 
1485         case mon_output_html:
1486         case mon_output_cgi:
1487             fprintf(stream, " rc=%d (%s)</li>\n", rc, services_ocf_exitcode_str(rc));
1488             break;
1489 
1490         case mon_output_xml:
1491             fprintf(stream, " rc=\"%d\" rc_text=\"%s\" />\n", rc, services_ocf_exitcode_str(rc));
1492             break;
1493 
1494         default:
1495             break;
1496     }
1497 }
1498 
1499 /*!
1500  * \internal
1501  * \brief Print resource operation/failure history
1502  *
1503  * \param[in] stream      File stream to display output to
1504  * \param[in] data_set    Current state of CIB
1505  * \param[in] node        Node that ran this resource
1506  * \param[in] rsc_entry   Root of XML tree describing resource status
1507  * \param[in] operations  Whether to print operations or just failcounts
1508  */
1509 static void
1510 print_rsc_history(FILE *stream, pe_working_set_t *data_set, node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1511                   xmlNode *rsc_entry, gboolean operations)
1512 {
1513     GListPtr gIter = NULL;
1514     GListPtr op_list = NULL;
1515     gboolean printed = FALSE;
1516     const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
1517     resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
1518     xmlNode *rsc_op = NULL;
1519 
1520     /* If we're not showing operations, just print the resource failure summary */
1521     if (operations == FALSE) {
1522         print_rsc_history_start(stream, data_set, node, rsc, rsc_id, FALSE);
1523         print_rsc_history_end(stream);
1524         return;
1525     }
1526 
1527     /* Create a list of this resource's operations */
1528     for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
1529         if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
1530             op_list = g_list_append(op_list, rsc_op);
1531         }
1532     }
1533     op_list = g_list_sort(op_list, sort_op_by_callid);
1534 
1535     /* Print each operation */
1536     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
1537         xmlNode *xml_op = (xmlNode *) gIter->data;
1538         const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
1539         const char *interval = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
1540         const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
1541         int rc = crm_parse_int(op_rc, "0");
1542 
1543         /* Display 0-interval monitors as "probe" */
1544         if (safe_str_eq(task, CRMD_ACTION_STATUS) && safe_str_eq(interval, "0")) {
1545             task = "probe";
1546         }
1547 
1548         /* Ignore notifies and some probes */
1549         if (safe_str_eq(task, CRMD_ACTION_NOTIFY) || (safe_str_eq(task, "probe") && (rc == 7))) {
1550             continue;
1551         }
1552 
1553         /* If this is the first printed operation, print heading for resource */
1554         if (printed == FALSE) {
1555             printed = TRUE;
1556             print_rsc_history_start(stream, data_set, node, rsc, rsc_id, TRUE);
1557         }
1558 
1559         /* Print the operation */
1560         print_op_history(stream, data_set, node, xml_op, task, interval, rc);
1561     }
1562 
1563     /* Free the list we created (no need to free the individual items) */
1564     g_list_free(op_list);
1565 
1566     /* If we printed anything, close the resource */
1567     if (printed) {
1568         print_rsc_history_end(stream);
1569     }
1570 }
1571 
1572 /*!
1573  * \internal
1574  * \brief Print node operation/failure history
1575  *
1576  * \param[in] stream      File stream to display output to
1577  * \param[in] data_set    Current state of CIB
1578  * \param[in] node_state  Root of XML tree describing node status
1579  * \param[in] operations  Whether to print operations or just failcounts
1580  */
1581 static void
1582 print_node_history(FILE *stream, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
1583                    xmlNode *node_state, gboolean operations)
1584 {
1585     node_t *node = pe_find_node_id(data_set->nodes, ID(node_state));
1586     xmlNode *lrm_rsc = NULL;
1587     xmlNode *rsc_entry = NULL;
1588 
1589     if (node && node->details && node->details->online) {
1590         print_node_start(stream, node);
1591 
1592         lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
1593         lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
1594 
1595         /* Print history of each of the node's resources */
1596         for (rsc_entry = __xml_first_child(lrm_rsc); rsc_entry != NULL;
1597              rsc_entry = __xml_next(rsc_entry)) {
1598 
1599             if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
1600                 print_rsc_history(stream, data_set, node, rsc_entry, operations);
1601             }
1602         }
1603 
1604         print_node_end(stream);
1605     }
1606 }
1607 
1608 /*!
1609  * \internal
1610  * \brief Print extended information about an attribute if appropriate
1611  *
1612  * \param[in] data_set  Working set of CIB state
1613  *
1614  * \return TRUE if extended information was printed, FALSE otherwise
1615  * \note Currently, extended information is only supported for ping/pingd
1616  *       resources, for which a message will be printed if connectivity is lost
1617  *       or degraded.
1618  */
1619 static gboolean
1620 print_attr_msg(FILE *stream, node_t * node, GListPtr rsc_list, const char *attrname, const char *attrvalue)
     /* [previous][next][first][last][top][bottom][index][help] */
1621 {
1622     GListPtr gIter = NULL;
1623 
1624     for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
1625         resource_t *rsc = (resource_t *) gIter->data;
1626         const char *type = g_hash_table_lookup(rsc->meta, "type");
1627 
1628         if (rsc->children != NULL) {
1629             if (print_attr_msg(stream, node, rsc->children, attrname, attrvalue)) {
1630                 return TRUE;
1631             }
1632         }
1633 
1634         if (safe_str_eq(type, "ping") || safe_str_eq(type, "pingd")) {
1635             const char *name = g_hash_table_lookup(rsc->parameters, "name");
1636 
1637             if (name == NULL) {
1638                 name = "pingd";
1639             }
1640 
1641             /* To identify the resource with the attribute name. */
1642             if (safe_str_eq(name, attrname)) {
1643                 int host_list_num = 0;
1644                 int expected_score = 0;
1645                 int value = crm_parse_int(attrvalue, "0");
1646                 const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list");
1647                 const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier");
1648 
1649                 if(hosts) {
1650                     char **host_list = g_strsplit(hosts, " ", 0);
1651                     host_list_num = g_strv_length(host_list);
1652                     g_strfreev(host_list);
1653                 }
1654 
1655                 /* pingd multiplier is the same as the default value. */
1656                 expected_score = host_list_num * crm_parse_int(multiplier, "1");
1657 
1658                 switch (output_format) {
1659                     case mon_output_plain:
1660                     case mon_output_console:
1661                         if (value <= 0) {
1662                             print_as("\t: Connectivity is lost");
1663                         } else if (value < expected_score) {
1664                             print_as("\t: Connectivity is degraded (Expected=%d)", expected_score);
1665                         }
1666                         break;
1667 
1668                     case mon_output_html:
1669                     case mon_output_cgi:
1670                         if (value <= 0) {
1671                             fprintf(stream, " <b>(connectivity is lost)</b>");
1672                         } else if (value < expected_score) {
1673                             fprintf(stream, " <b>(connectivity is degraded -- expected %d)</b>",
1674                                     expected_score);
1675                         }
1676                         break;
1677 
1678                     case mon_output_xml:
1679                         fprintf(stream, " expected=\"%d\"", expected_score);
1680                         break;
1681 
1682                     default:
1683                         break;
1684                 }
1685                 return TRUE;
1686             }
1687         }
1688     }
1689     return FALSE;
1690 }
1691 
1692 static int
1693 compare_attribute(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1694 {
1695     int rc;
1696 
1697     rc = strcmp((const char *)a, (const char *)b);
1698 
1699     return rc;
1700 }
1701 
1702 static void
1703 create_attr_list(gpointer name, gpointer value, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1704 {
1705     int i;
1706     const char *filt_str[] = FILTER_STR;
1707 
1708     CRM_CHECK(name != NULL, return);
1709 
1710     /* filtering automatic attributes */
1711     for (i = 0; filt_str[i] != NULL; i++) {
1712         if (g_str_has_prefix(name, filt_str[i])) {
1713             return;
1714         }
1715     }
1716 
1717     attr_list = g_list_insert_sorted(attr_list, name, compare_attribute);
1718 }
1719 
1720 /* structure for passing multiple user data to g_list_foreach() */
1721 struct mon_attr_data {
1722     FILE *stream;
1723     node_t *node;
1724 };
1725 
1726 static void
1727 print_node_attribute(gpointer name, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1728 {
1729     const char *value = NULL;
1730     struct mon_attr_data *data = (struct mon_attr_data *) user_data;
1731 
1732     value = pe_node_attribute_raw(data->node, name);
1733 
1734     /* Print attribute name and value */
1735     switch (output_format) {
1736         case mon_output_plain:
1737         case mon_output_console:
1738             print_as("    + %-32s\t: %-10s", (char *)name, value);
1739             break;
1740 
1741         case mon_output_html:
1742         case mon_output_cgi:
1743             fprintf(data->stream, "   <li>%s: %s",
1744                     (char *)name, value);
1745             break;
1746 
1747         case mon_output_xml:
1748             fprintf(data->stream,
1749                     "            <attribute name=\"%s\" value=\"%s\"",
1750                     (char *)name, value);
1751             break;
1752 
1753         default:
1754             break;
1755     }
1756 
1757     /* Print extended information if appropriate */
1758     print_attr_msg(data->stream, data->node, data->node->details->running_rsc,
1759                    name, value);
1760 
1761     /* Close out the attribute */
1762     switch (output_format) {
1763         case mon_output_plain:
1764         case mon_output_console:
1765             print_as("\n");
1766             break;
1767 
1768         case mon_output_html:
1769         case mon_output_cgi:
1770             fprintf(data->stream, "</li>\n");
1771             break;
1772 
1773         case mon_output_xml:
1774             fprintf(data->stream, " />\n");
1775             break;
1776 
1777         default:
1778             break;
1779     }
1780 }
1781 
1782 static void
1783 print_node_summary(FILE *stream, pe_working_set_t * data_set, gboolean operations)
     /* [previous][next][first][last][top][bottom][index][help] */
1784 {
1785     xmlNode *node_state = NULL;
1786     xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
1787 
1788     /* Print heading */
1789     switch (output_format) {
1790         case mon_output_plain:
1791         case mon_output_console:
1792             if (operations) {
1793                 print_as("\nOperations:\n");
1794             } else {
1795                 print_as("\nMigration Summary:\n");
1796             }
1797             break;
1798 
1799         case mon_output_html:
1800         case mon_output_cgi:
1801             if (operations) {
1802                 fprintf(stream, " <hr />\n <h2>Operations</h2>\n");
1803             } else {
1804                 fprintf(stream, " <hr />\n <h2>Migration Summary</h2>\n");
1805             }
1806             break;
1807 
1808         case mon_output_xml:
1809             fprintf(stream, "    <node_history>\n");
1810             break;
1811 
1812         default:
1813             break;
1814     }
1815 
1816     /* Print each node in the CIB status */
1817     for (node_state = __xml_first_child(cib_status); node_state != NULL;
1818          node_state = __xml_next(node_state)) {
1819         if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
1820             print_node_history(stream, data_set, node_state, operations);
1821         }
1822     }
1823 
1824     /* Close section */
1825     switch (output_format) {
1826         case mon_output_xml:
1827             fprintf(stream, "    </node_history>\n");
1828             break;
1829 
1830         default:
1831             break;
1832     }
1833 }
1834 
1835 static void
1836 print_ticket(gpointer name, gpointer value, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1837 {
1838     ticket_t *ticket = (ticket_t *) value;
1839     FILE *stream = (FILE *) data;
1840 
1841     switch (output_format) {
1842         case mon_output_plain:
1843         case mon_output_console:
1844             print_as("* %s:\t%s%s", ticket->id,
1845                      (ticket->granted? "granted" : "revoked"),
1846                      (ticket->standby? " [standby]" : ""));
1847             break;
1848 
1849         case mon_output_html:
1850         case mon_output_cgi:
1851             fprintf(stream, "  <li>%s: %s%s", ticket->id,
1852                     (ticket->granted? "granted" : "revoked"),
1853                     (ticket->standby? " [standby]" : ""));
1854             break;
1855 
1856         case mon_output_xml:
1857             fprintf(stream, "        <ticket id=\"%s\" status=\"%s\" standby=\"%s\"",
1858                     ticket->id, (ticket->granted? "granted" : "revoked"),
1859                     (ticket->standby? "true" : "false"));
1860             break;
1861 
1862         default:
1863             break;
1864     }
1865     if (ticket->last_granted > -1) {
1866         print_nvpair(stdout, "last-granted", NULL, NULL, ticket->last_granted);
1867     }
1868     switch (output_format) {
1869         case mon_output_plain:
1870         case mon_output_console:
1871             print_as("\n");
1872             break;
1873 
1874         case mon_output_html:
1875         case mon_output_cgi:
1876             fprintf(stream, "</li>\n");
1877             break;
1878 
1879         case mon_output_xml:
1880             fprintf(stream, " />\n");
1881             break;
1882 
1883         default:
1884             break;
1885     }
1886 }
1887 
1888 static void
1889 print_cluster_tickets(FILE *stream, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1890 {
1891     /* Print section heading */
1892     switch (output_format) {
1893         case mon_output_plain:
1894         case mon_output_console:
1895             print_as("\nTickets:\n");
1896             break;
1897 
1898         case mon_output_html:
1899         case mon_output_cgi:
1900             fprintf(stream, " <hr />\n <h2>Tickets</h2>\n <ul>\n");
1901             break;
1902 
1903         case mon_output_xml:
1904             fprintf(stream, "    <tickets>\n");
1905             break;
1906 
1907         default:
1908             break;
1909     }
1910 
1911     /* Print each ticket */
1912     g_hash_table_foreach(data_set->tickets, print_ticket, stream);
1913 
1914     /* Close section */
1915     switch (output_format) {
1916         case mon_output_html:
1917         case mon_output_cgi:
1918             fprintf(stream, " </ul>\n");
1919             break;
1920 
1921         case mon_output_xml:
1922             fprintf(stream, "    </tickets>\n");
1923             break;
1924 
1925         default:
1926             break;
1927     }
1928 }
1929 
1930 /*!
1931  * \internal
1932  * \brief Return human-friendly string representing node name
1933  *
1934  * The returned string will be in the format
1935  *    uname[@hostUname] [(nodeID)]
1936  * "@hostUname" will be printed if the node is a guest node.
1937  * "(nodeID)" will be printed if the node ID is different from the node uname,
1938  *  and detailed output has been requested.
1939  *
1940  * \param[in] node  Node to represent
1941  * \return Newly allocated string with representation of node name
1942  * \note It is the caller's responsibility to free the result with free().
1943  */
1944 static char *
1945 get_node_display_name(node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1946 {
1947     char *node_name;
1948     const char *node_host = NULL;
1949     const char *node_id = NULL;
1950     int name_len;
1951 
1952     CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
1953 
1954     /* Host is displayed only if this is a guest node */
1955     if (is_container_remote_node(node)) {
1956         if (node->details->remote_rsc->running_on) {
1957             /* running_on is a list, but guest nodes will have exactly one entry
1958              * unless they are in the process of migrating, in which case they
1959              * will have two; either way, we can use the first item in the list
1960              */
1961             node_t *host_node = (node_t *) node->details->remote_rsc->running_on->data;
1962 
1963             if (host_node && host_node->details) {
1964                 node_host = host_node->details->uname;
1965             }
1966         }
1967         if (node_host == NULL) {
1968             node_host = ""; /* so we at least get "uname@" to indicate guest */
1969         }
1970     }
1971 
1972     /* Node ID is displayed if different from uname and detail is requested */
1973     if (print_clone_detail && safe_str_neq(node->details->uname, node->details->id)) {
1974         node_id = node->details->id;
1975     }
1976 
1977     /* Determine name length */
1978     name_len = strlen(node->details->uname) + 1;
1979     if (node_host) {
1980         name_len += strlen(node_host) + 1; /* "@node_host" */
1981     }
1982     if (node_id) {
1983         name_len += strlen(node_id) + 3; /* + " (node_id)" */
1984     }
1985 
1986     /* Allocate and populate display name */
1987     node_name = malloc(name_len);
1988     CRM_ASSERT(node_name != NULL);
1989     strcpy(node_name, node->details->uname);
1990     if (node_host) {
1991         strcat(node_name, "@");
1992         strcat(node_name, node_host);
1993     }
1994     if (node_id) {
1995         strcat(node_name, " (");
1996         strcat(node_name, node_id);
1997         strcat(node_name, ")");
1998     }
1999     return node_name;
2000 }
2001 
2002 /*!
2003  * \internal
2004  * \brief Print a negative location constraint
2005  *
2006  * \param[in] stream     File stream to display output to
2007  * \param[in] node       Node affected by constraint
2008  * \param[in] location   Constraint to print
2009  */
2010 static void print_ban(FILE *stream, node_t *node, rsc_to_node_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
2011 {
2012     char *node_name = NULL;
2013 
2014     switch (output_format) {
2015         case mon_output_plain:
2016         case mon_output_console:
2017             node_name = get_node_display_name(node);
2018             print_as(" %s\tprevents %s from running %son %s\n",
2019                      location->id, location->rsc_lh->id,
2020                      ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
2021                      node_name);
2022             break;
2023 
2024         case mon_output_html:
2025         case mon_output_cgi:
2026             node_name = get_node_display_name(node);
2027             fprintf(stream, "  <li>%s prevents %s from running %son %s</li>\n",
2028                      location->id, location->rsc_lh->id,
2029                      ((location->role_filter == RSC_ROLE_MASTER)? "as Master " : ""),
2030                      node_name);
2031             break;
2032 
2033         case mon_output_xml:
2034             fprintf(stream,
2035                     "        <ban id=\"%s\" resource=\"%s\" node=\"%s\" weight=\"%d\" master_only=\"%s\" />\n",
2036                     location->id, location->rsc_lh->id, node->details->uname, node->weight,
2037                     ((location->role_filter == RSC_ROLE_MASTER)? "true" : "false"));
2038             break;
2039 
2040         default:
2041             break;
2042     }
2043     free(node_name);
2044 }
2045 
2046 /*!
2047  * \internal
2048  * \brief Print section for negative location constraints
2049  *
2050  * \param[in] stream     File stream to display output to
2051  * \param[in] data_set   Working set corresponding to CIB status to display
2052  */
2053 static void print_neg_locations(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2054 {
2055     GListPtr gIter, gIter2;
2056 
2057     /* Print section heading */
2058     switch (output_format) {
2059         case mon_output_plain:
2060         case mon_output_console:
2061             print_as("\nNegative Location Constraints:\n");
2062             break;
2063 
2064         case mon_output_html:
2065         case mon_output_cgi:
2066             fprintf(stream, " <hr />\n <h2>Negative Location Constraints</h2>\n <ul>\n");
2067             break;
2068 
2069         case mon_output_xml:
2070             fprintf(stream, "    <bans>\n");
2071             break;
2072 
2073         default:
2074             break;
2075     }
2076 
2077     /* Print each ban */
2078     for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
2079         rsc_to_node_t *location = (rsc_to_node_t *) gIter->data;
2080         if (!g_str_has_prefix(location->id, print_neg_location_prefix))
2081             continue;
2082         for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
2083             node_t *node = (node_t *) gIter2->data;
2084 
2085             if (node->weight < 0) {
2086                 print_ban(stream, node, location);
2087             }
2088         }
2089     }
2090 
2091     /* Close section */
2092     switch (output_format) {
2093         case mon_output_cgi:
2094         case mon_output_html:
2095             fprintf(stream, " </ul>\n");
2096             break;
2097 
2098         case mon_output_xml:
2099             fprintf(stream, "    </bans>\n");
2100             break;
2101 
2102         default:
2103             break;
2104     }
2105 }
2106 
2107 static void
2108 crm_mon_get_parameters(resource_t *rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2109 {
2110     get_rsc_attributes(rsc->parameters, rsc, NULL, data_set);
2111     crm_trace("Beekhof: unpacked params for %s (%d)", rsc->id, g_hash_table_size(rsc->parameters));
2112     if(rsc->children) {
2113         GListPtr gIter = NULL;
2114 
2115         for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
2116             crm_mon_get_parameters(gIter->data, data_set);
2117         }
2118     }
2119 }
2120 
2121 /*!
2122  * \internal
2123  * \brief Print node attributes section
2124  *
2125  * \param[in] stream     File stream to display output to
2126  * \param[in] data_set   Working set of CIB state
2127  */
2128 static void
2129 print_node_attributes(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2130 {
2131     GListPtr gIter = NULL;
2132 
2133     /* Print section heading */
2134     switch (output_format) {
2135         case mon_output_plain:
2136         case mon_output_console:
2137             print_as("\nNode Attributes:\n");
2138             break;
2139 
2140         case mon_output_html:
2141         case mon_output_cgi:
2142             fprintf(stream, " <hr />\n <h2>Node Attributes</h2>\n");
2143             break;
2144 
2145         case mon_output_xml:
2146             fprintf(stream, "    <node_attributes>\n");
2147             break;
2148 
2149         default:
2150             break;
2151     }
2152 
2153     /* Unpack all resource parameters (it would be more efficient to do this
2154      * only when needed for the first time in print_attr_msg())
2155      */
2156     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
2157         crm_mon_get_parameters(gIter->data, data_set);
2158     }
2159 
2160     /* Display each node's attributes */
2161     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
2162         struct mon_attr_data data;
2163 
2164         data.stream = stream;
2165         data.node = (node_t *) gIter->data;
2166 
2167         if (data.node && data.node->details && data.node->details->online) {
2168             print_node_start(stream, data.node);
2169             g_hash_table_foreach(data.node->details->attrs, create_attr_list, NULL);
2170             g_list_foreach(attr_list, print_node_attribute, &data);
2171             g_list_free(attr_list);
2172             attr_list = NULL;
2173             print_node_end(stream);
2174         }
2175     }
2176 
2177     /* Print section footer */
2178     switch (output_format) {
2179         case mon_output_xml:
2180             fprintf(stream, "    </node_attributes>\n");
2181             break;
2182 
2183         default:
2184             break;
2185     }
2186 }
2187 
2188 /*!
2189  * \internal
2190  * \brief Return resource display options corresponding to command-line choices
2191  *
2192  * \return Bitmask of pe_print_options suitable for resource print functions
2193  */
2194 static int
2195 get_resource_display_options(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2196 {
2197     int print_opts;
2198 
2199     /* Determine basic output format */
2200     switch (output_format) {
2201         case mon_output_console:
2202             print_opts = pe_print_ncurses;
2203             break;
2204         case mon_output_html:
2205         case mon_output_cgi:
2206             print_opts = pe_print_html;
2207             break;
2208         case mon_output_xml:
2209             print_opts = pe_print_xml;
2210             break;
2211         default:
2212             print_opts = pe_print_printf;
2213             break;
2214     }
2215 
2216     /* Add optional display elements */
2217     if (print_pending) {
2218         print_opts |= pe_print_pending;
2219     }
2220     if (print_clone_detail) {
2221         print_opts |= pe_print_clone_details|pe_print_implicit;
2222     }
2223     if (!inactive_resources) {
2224         print_opts |= pe_print_clone_active;
2225     }
2226     if (print_brief) {
2227         print_opts |= pe_print_brief;
2228     }
2229     return print_opts;
2230 }
2231 
2232 /*!
2233  * \internal
2234  * \brief Return human-friendly string representing current time
2235  *
2236  * \return Current time as string (as by ctime() but without newline) on success
2237  *         or "Could not determine current time" on error
2238  * \note The return value points to a statically allocated string which might be
2239  *       overwritten by subsequent calls to any of the C library date and time functions.
2240  */
2241 static const char *
2242 crm_now_string(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2243 {
2244     time_t a_time = time(NULL);
2245     char *since_epoch = ctime(&a_time);
2246 
2247     if ((a_time == (time_t) -1) || (since_epoch == NULL)) {
2248         return "Could not determine current time";
2249     }
2250     since_epoch[strlen(since_epoch) - 1] = EOS; /* trim newline */
2251     return (since_epoch);
2252 }
2253 
2254 /*!
2255  * \internal
2256  * \brief Print header for cluster summary if needed
2257  *
2258  * \param[in] stream     File stream to display output to
2259  */
2260 static void
2261 print_cluster_summary_header(FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
2262 {
2263     switch (output_format) {
2264         case mon_output_html:
2265         case mon_output_cgi:
2266             fprintf(stream, " <h2>Cluster Summary</h2>\n <p>\n");
2267             break;
2268 
2269         case mon_output_xml:
2270             fprintf(stream, "    <summary>\n");
2271             break;
2272 
2273         default:
2274             break;
2275     }
2276 }
2277 
2278 /*!
2279  * \internal
2280  * \brief Print footer for cluster summary if needed
2281  *
2282  * \param[in] stream     File stream to display output to
2283  */
2284 static void
2285 print_cluster_summary_footer(FILE *stream)
     /* [previous][next][first][last][top][bottom][index][help] */
2286 {
2287     switch (output_format) {
2288         case mon_output_cgi:
2289         case mon_output_html:
2290             fprintf(stream, " </p>\n");
2291             break;
2292 
2293         case mon_output_xml:
2294             fprintf(stream, "    </summary>\n");
2295             break;
2296 
2297         default:
2298             break;
2299     }
2300 }
2301 
2302 /*!
2303  * \internal
2304  * \brief Print times the display was last updated and CIB last changed
2305  *
2306  * \param[in] stream     File stream to display output to
2307  * \param[in] data_set   Working set of CIB state
2308  */
2309 static void
2310 print_cluster_times(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2311 {
2312     const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
2313     const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
2314     const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
2315     const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
2316 
2317     switch (output_format) {
2318         case mon_output_plain:
2319         case mon_output_console:
2320             print_as("Last updated: %s", crm_now_string());
2321             print_as((user || client || origin)? "\n" : "\t\t");
2322             print_as("Last change: %s", last_written ? last_written : "");
2323             if (user) {
2324                 print_as(" by %s", user);
2325             }
2326             if (client) {
2327                 print_as(" via %s", client);
2328             }
2329             if (origin) {
2330                 print_as(" on %s", origin);
2331             }
2332             print_as("\n");
2333             break;
2334 
2335         case mon_output_html:
2336         case mon_output_cgi:
2337             fprintf(stream, " <b>Last updated:</b> %s<br/>\n", crm_now_string());
2338             fprintf(stream, " <b>Last change:</b> %s", last_written ? last_written : "");
2339             if (user) {
2340                 fprintf(stream, " by %s", user);
2341             }
2342             if (client) {
2343                 fprintf(stream, " via %s", client);
2344             }
2345             if (origin) {
2346                 fprintf(stream, " on %s", origin);
2347             }
2348             fprintf(stream, "<br/>\n");
2349             break;
2350 
2351         case mon_output_xml:
2352             fprintf(stream, "        <last_update time=\"%s\" />\n", crm_now_string());
2353             fprintf(stream, "        <last_change time=\"%s\" user=\"%s\" client=\"%s\" origin=\"%s\" />\n",
2354                     last_written ? last_written : "", user ? user : "",
2355                     client ? client : "", origin ? origin : "");
2356             break;
2357 
2358         default:
2359             break;
2360     }
2361 }
2362 
2363 /*!
2364  * \internal
2365  * \brief Print cluster stack
2366  *
2367  * \param[in] stream     File stream to display output to
2368  * \param[in] stack_s    Stack name
2369  */
2370 static void
2371 print_cluster_stack(FILE *stream, const char *stack_s)
     /* [previous][next][first][last][top][bottom][index][help] */
2372 {
2373     switch (output_format) {
2374         case mon_output_plain:
2375         case mon_output_console:
2376             print_as("Stack: %s\n", stack_s);
2377             break;
2378 
2379         case mon_output_html:
2380         case mon_output_cgi:
2381             fprintf(stream, " <b>Stack:</b> %s<br/>\n", stack_s);
2382             break;
2383 
2384         case mon_output_xml:
2385             fprintf(stream, "        <stack type=\"%s\" />\n", stack_s);
2386             break;
2387 
2388         default:
2389             break;
2390     }
2391 }
2392 
2393 /*!
2394  * \internal
2395  * \brief Print current DC and its version
2396  *
2397  * \param[in] stream     File stream to display output to
2398  * \param[in] data_set   Working set of CIB state
2399  */
2400 static void
2401 print_cluster_dc(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2402 {
2403     node_t *dc = data_set->dc_node;
2404     xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
2405                                            data_set->input, LOG_DEBUG);
2406     const char *dc_version_s = dc_version?
2407                                crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
2408                                : NULL;
2409     const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
2410     char *dc_name = dc? get_node_display_name(dc) : NULL;
2411 
2412     switch (output_format) {
2413         case mon_output_plain:
2414         case mon_output_console:
2415             print_as("Current DC: ");
2416             if (dc) {
2417                 print_as("%s (version %s) - partition %s quorum\n",
2418                          dc_name, (dc_version_s? dc_version_s : "unknown"),
2419                          (crm_is_true(quorum) ? "with" : "WITHOUT"));
2420             } else {
2421                 print_as("NONE\n");
2422             }
2423             break;
2424 
2425         case mon_output_html:
2426         case mon_output_cgi:
2427             fprintf(stream, " <b>Current DC:</b> ");
2428             if (dc) {
2429                 fprintf(stream, "%s (version %s) - partition %s quorum",
2430                         dc_name, (dc_version_s? dc_version_s : "unknown"),
2431                         (crm_is_true(quorum)? "with" : "<font color=\"red\"><b>WITHOUT</b></font>"));
2432             } else {
2433                 fprintf(stream, "<font color=\"red\"><b>NONE</b></font>");
2434             }
2435             fprintf(stream, "<br/>\n");
2436             break;
2437 
2438         case mon_output_xml:
2439             fprintf(stream,  "        <current_dc ");
2440             if (dc) {
2441                 fprintf(stream,
2442                         "present=\"true\" version=\"%s\" name=\"%s\" id=\"%s\" with_quorum=\"%s\"",
2443                         (dc_version_s? dc_version_s : ""), dc->details->uname, dc->details->id,
2444                         (crm_is_true(quorum) ? "true" : "false"));
2445             } else {
2446                 fprintf(stream, "present=\"false\"");
2447             }
2448             fprintf(stream, " />\n");
2449             break;
2450 
2451         default:
2452             break;
2453     }
2454     free(dc_name);
2455 }
2456 
2457 /*!
2458  * \internal
2459  * \brief Print counts of configured nodes and resources
2460  *
2461  * \param[in] stream     File stream to display output to
2462  * \param[in] data_set   Working set of CIB state
2463  * \param[in] stack_s    Stack name
2464  */
2465 static void
2466 print_cluster_counts(FILE *stream, pe_working_set_t *data_set, const char *stack_s)
     /* [previous][next][first][last][top][bottom][index][help] */
2467 {
2468     int nnodes = g_list_length(data_set->nodes);
2469     int nresources = count_resources(data_set, NULL);
2470     xmlNode *quorum_node = get_xpath_object("//nvpair[@name='" XML_ATTR_EXPECTED_VOTES "']",
2471                                             data_set->input, LOG_DEBUG);
2472     const char *quorum_votes = quorum_node?
2473                                crm_element_value(quorum_node, XML_NVPAIR_ATTR_VALUE)
2474                                : "unknown";
2475 
2476     switch (output_format) {
2477         case mon_output_plain:
2478         case mon_output_console:
2479 
2480             print_as("\n%d node%s configured", nnodes, s_if_plural(nnodes));
2481             if (stack_s && strstr(stack_s, "classic openais") != NULL) {
2482                 print_as(" (%s expected votes)", quorum_votes);
2483             }
2484             print_as("\n");
2485 
2486             print_as("%d resource%s configured",
2487                      nresources, s_if_plural(nresources));
2488             if(data_set->disabled_resources || data_set->blocked_resources) {
2489                 print_as(" (");
2490                 if (data_set->disabled_resources) {
2491                     print_as("%d DISABLED", data_set->disabled_resources);
2492                 }
2493                 if (data_set->disabled_resources && data_set->blocked_resources) {
2494                     print_as(", ");
2495                 }
2496                 if (data_set->blocked_resources) {
2497                     print_as("%d BLOCKED from starting due to failure",
2498                              data_set->blocked_resources);
2499                 }
2500                 print_as(")");
2501             }
2502             print_as("\n");
2503 
2504             break;
2505 
2506         case mon_output_html:
2507         case mon_output_cgi:
2508 
2509             fprintf(stream, " %d node%s configured", nnodes, s_if_plural(nnodes));
2510             if (stack_s && strstr(stack_s, "classic openais") != NULL) {
2511                 fprintf(stream, " (%s expected votes)", quorum_votes);
2512             }
2513             fprintf(stream, "<br/>\n");
2514 
2515             fprintf(stream, " %d resource%s configured",
2516                     nresources, s_if_plural(nresources));
2517             if (data_set->disabled_resources || data_set->blocked_resources) {
2518                 fprintf(stream, " (");
2519                 if (data_set->disabled_resources) {
2520                     fprintf(stream, "%d <strong>DISABLED</strong>",
2521                             data_set->disabled_resources);
2522                 }
2523                 if (data_set->disabled_resources && data_set->blocked_resources) {
2524                     fprintf(stream, ", ");
2525                 }
2526                 if (data_set->blocked_resources) {
2527                     fprintf(stream,
2528                             "%d <strong>BLOCKED</strong> from starting due to failure",
2529                             data_set->blocked_resources);
2530                 }
2531                 fprintf(stream, ")");
2532             }
2533             fprintf(stream, "<br/>\n");
2534             break;
2535 
2536         case mon_output_xml:
2537             fprintf(stream,
2538                     "        <nodes_configured number=\"%d\" expected_votes=\"%s\" />\n",
2539                     g_list_length(data_set->nodes), quorum_votes);
2540             fprintf(stream,
2541                     "        <resources_configured number=\"%d\" disabled=\"%d\" blocked=\"%d\" />\n",
2542                     count_resources(data_set, NULL),
2543                     data_set->disabled_resources, data_set->blocked_resources);
2544             break;
2545 
2546         default:
2547             break;
2548     }
2549 }
2550 
2551 /*!
2552  * \internal
2553  * \brief Print cluster-wide options
2554  *
2555  * \param[in] stream     File stream to display output to
2556  * \param[in] data_set   Working set of CIB state
2557  *
2558  * \note Currently this is only implemented for HTML and XML output, and
2559  *       prints only a few options. If there is demand, more could be added.
2560  */
2561 static void
2562 print_cluster_options(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2563 {
2564     switch (output_format) {
2565         case mon_output_plain:
2566         case mon_output_console:
2567             if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
2568                 print_as("\n              *** Resource management is DISABLED ***");
2569                 print_as("\n  The cluster will not attempt to start, stop or recover services");
2570                 print_as("\n");
2571             }
2572             break;
2573 
2574         case mon_output_html:
2575             fprintf(stream, " </p>\n <h3>Config Options</h3>\n");
2576             fprintf(stream, " <table>\n");
2577             fprintf(stream, "  <tr><th>STONITH of failed nodes</th><td>%s</td></tr>\n",
2578                     is_set(data_set->flags, pe_flag_stonith_enabled)? "enabled" : "disabled");
2579 
2580             fprintf(stream, "  <tr><th>Cluster is</th><td>%ssymmetric</td></tr>\n",
2581                     is_set(data_set->flags, pe_flag_symmetric_cluster)? "" : "a");
2582 
2583             fprintf(stream, "  <tr><th>No Quorum Policy</th><td>");
2584             switch (data_set->no_quorum_policy) {
2585                 case no_quorum_freeze:
2586                     fprintf(stream, "Freeze resources");
2587                     break;
2588                 case no_quorum_stop:
2589                     fprintf(stream, "Stop ALL resources");
2590                     break;
2591                 case no_quorum_ignore:
2592                     fprintf(stream, "Ignore");
2593                     break;
2594                 case no_quorum_suicide:
2595                     fprintf(stream, "Suicide");
2596                     break;
2597             }
2598             fprintf(stream, "</td></tr>\n");
2599 
2600             fprintf(stream, "  <tr><th>Resource management</th><td>");
2601             if (is_set(data_set->flags, pe_flag_maintenance_mode)) {
2602                 fprintf(stream, "<strong>DISABLED</strong> (the cluster will "
2603                                 "not attempt to start, stop or recover services)");
2604             } else {
2605                 fprintf(stream, "enabled");
2606             }
2607             fprintf(stream, "</td></tr>\n");
2608 
2609             fprintf(stream, "</table>\n <p>\n");
2610             break;
2611 
2612         case mon_output_xml:
2613             fprintf(stream, "        <cluster_options");
2614             fprintf(stream, " stonith-enabled=\"%s\"",
2615                     is_set(data_set->flags, pe_flag_stonith_enabled)?
2616                     "true" : "false");
2617             fprintf(stream, " symmetric-cluster=\"%s\"",
2618                     is_set(data_set->flags, pe_flag_symmetric_cluster)?
2619                     "true" : "false");
2620             fprintf(stream, " no-quorum-policy=\"");
2621             switch (data_set->no_quorum_policy) {
2622                 case no_quorum_freeze:
2623                     fprintf(stream, "freeze");
2624                     break;
2625                 case no_quorum_stop:
2626                     fprintf(stream, "stop");
2627                     break;
2628                 case no_quorum_ignore:
2629                     fprintf(stream, "ignore");
2630                     break;
2631                 case no_quorum_suicide:
2632                     fprintf(stream, "suicide");
2633                     break;
2634             }
2635             fprintf(stream, "\"");
2636             fprintf(stream, " maintenance-mode=\"%s\"",
2637                     is_set(data_set->flags, pe_flag_maintenance_mode)?
2638                     "true" : "false");
2639             fprintf(stream, " />\n");
2640             break;
2641 
2642         default:
2643             break;
2644     }
2645 }
2646 
2647 /*!
2648  * \internal
2649  * \brief Get the name of the stack in use (or "unknown" if not available)
2650  *
2651  * \param[in] data_set   Working set of CIB state
2652  *
2653  * \return String representing stack name
2654  */
2655 static const char *
2656 get_cluster_stack(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2657 {
2658     xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
2659                                       data_set->input, LOG_DEBUG);
2660     return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
2661 }
2662 
2663 /*!
2664  * \internal
2665  * \brief Print a summary of cluster-wide information
2666  *
2667  * \param[in] stream     File stream to display output to
2668  * \param[in] data_set   Working set of CIB state
2669  */
2670 static void
2671 print_cluster_summary(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2672 {
2673     const char *stack_s = get_cluster_stack(data_set);
2674     gboolean header_printed = FALSE;
2675 
2676     if (show & mon_show_stack) {
2677         if (header_printed == FALSE) {
2678             print_cluster_summary_header(stream);
2679             header_printed = TRUE;
2680         }
2681         print_cluster_stack(stream, stack_s);
2682     }
2683 
2684     /* Always print DC if none, even if not requested */
2685     if ((data_set->dc_node == NULL) || (show & mon_show_dc)) {
2686         if (header_printed == FALSE) {
2687             print_cluster_summary_header(stream);
2688             header_printed = TRUE;
2689         }
2690         print_cluster_dc(stream, data_set);
2691     }
2692 
2693     if (show & mon_show_times) {
2694         if (header_printed == FALSE) {
2695             print_cluster_summary_header(stream);
2696             header_printed = TRUE;
2697         }
2698         print_cluster_times(stream, data_set);
2699     }
2700 
2701     if (is_set(data_set->flags, pe_flag_maintenance_mode)
2702         || data_set->disabled_resources
2703         || data_set->blocked_resources
2704         || is_set(show, mon_show_count)) {
2705         if (header_printed == FALSE) {
2706             print_cluster_summary_header(stream);
2707             header_printed = TRUE;
2708         }
2709         print_cluster_counts(stream, data_set, stack_s);
2710     }
2711 
2712     /* There is not a separate option for showing cluster options, so show with
2713      * stack for now; a separate option could be added if there is demand
2714      */
2715     if (show & mon_show_stack) {
2716         print_cluster_options(stream, data_set);
2717     }
2718 
2719     if (header_printed) {
2720         print_cluster_summary_footer(stream);
2721     }
2722 }
2723 
2724 /*!
2725  * \internal
2726  * \brief Print a failed action
2727  *
2728  * \param[in] stream     File stream to display output to
2729  * \param[in] xml_op     Root of XML tree describing failed action
2730  */
2731 static void
2732 print_failed_action(FILE *stream, xmlNode *xml_op)
     /* [previous][next][first][last][top][bottom][index][help] */
2733 {
2734     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
2735     const char *op_key_attr = "op_key";
2736     const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE);
2737     const char *node = crm_element_value(xml_op, XML_ATTR_UNAME);
2738     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
2739     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
2740     int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0");
2741     int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0");
2742     char *exit_reason_cleaned;
2743 
2744     /* If no op_key was given, use id instead */
2745     if (op_key == NULL) {
2746         op_key = ID(xml_op);
2747         op_key_attr = "id";
2748     }
2749 
2750     /* If no exit reason was given, use "none" */
2751     if (exit_reason == NULL) {
2752         exit_reason = "none";
2753     }
2754 
2755     /* Print common action information */
2756     switch (output_format) {
2757         case mon_output_plain:
2758         case mon_output_console:
2759             print_as("* %s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
2760                      op_key, node, services_ocf_exitcode_str(rc), rc,
2761                      call, services_lrm_status_str(status), exit_reason);
2762             break;
2763 
2764         case mon_output_html:
2765         case mon_output_cgi:
2766             fprintf(stream, "  <li>%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
2767                      op_key, node, services_ocf_exitcode_str(rc), rc,
2768                      call, services_lrm_status_str(status), exit_reason);
2769             break;
2770 
2771         case mon_output_xml:
2772             exit_reason_cleaned = crm_xml_escape(exit_reason);
2773             fprintf(stream, "        <failure %s=\"%s\" node=\"%s\"",
2774                     op_key_attr, op_key, node);
2775             fprintf(stream, " exitstatus=\"%s\" exitreason=\"%s\" exitcode=\"%d\"",
2776                     services_ocf_exitcode_str(rc), exit_reason_cleaned, rc);
2777             fprintf(stream, " call=\"%s\" status=\"%s\"",
2778                     call, services_lrm_status_str(status));
2779             free(exit_reason_cleaned);
2780             break;
2781 
2782         default:
2783             break;
2784     }
2785 
2786     /* If last change was given, print timing information as well */
2787     if (last) {
2788         time_t run_at = crm_parse_int(last, "0");
2789         char *run_at_s = ctime(&run_at);
2790 
2791         if (run_at_s) {
2792             run_at_s[24] = 0; /* Overwrite the newline */
2793         }
2794 
2795         switch (output_format) {
2796             case mon_output_plain:
2797             case mon_output_console:
2798                 print_as(",\n    last-rc-change='%s', queued=%sms, exec=%sms",
2799                          run_at_s? run_at_s : "",
2800                          crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2801                          crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
2802                 break;
2803 
2804             case mon_output_html:
2805             case mon_output_cgi:
2806                 fprintf(stream, " last-rc-change='%s', queued=%sms, exec=%sms",
2807                         run_at_s? run_at_s : "",
2808                         crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2809                         crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
2810                 break;
2811 
2812             case mon_output_xml:
2813                 fprintf(stream,
2814                         " last-rc-change=\"%s\" queued=\"%s\" exec=\"%s\" interval=\"%d\" task=\"%s\"",
2815                         run_at_s? run_at_s : "",
2816                         crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
2817                         crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
2818                         crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL), "0"),
2819                         crm_element_value(xml_op, XML_LRM_ATTR_TASK));
2820                 break;
2821 
2822             default:
2823                 break;
2824         }
2825     }
2826 
2827     /* End the action listing */
2828     switch (output_format) {
2829         case mon_output_plain:
2830         case mon_output_console:
2831             print_as("\n");
2832             break;
2833 
2834         case mon_output_html:
2835         case mon_output_cgi:
2836             fprintf(stream, "</li>\n");
2837             break;
2838 
2839         case mon_output_xml:
2840             fprintf(stream, " />\n");
2841             break;
2842 
2843         default:
2844             break;
2845     }
2846 }
2847 
2848 /*!
2849  * \internal
2850  * \brief Print a section for failed actions
2851  *
2852  * \param[in] stream     File stream to display output to
2853  * \param[in] data_set   Working set of CIB state
2854  */
2855 static void
2856 print_failed_actions(FILE *stream, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2857 {
2858     xmlNode *xml_op = NULL;
2859 
2860     /* Print section heading */
2861     switch (output_format) {
2862         case mon_output_plain:
2863         case mon_output_console:
2864             print_as("\nFailed Actions:\n");
2865             break;
2866 
2867         case mon_output_html:
2868         case mon_output_cgi:
2869             fprintf(stream, " <hr />\n <h2>Failed Actions</h2>\n <ul>\n");
2870             break;
2871 
2872         case mon_output_xml:
2873             fprintf(stream, "    <failures>\n");
2874             break;
2875 
2876         default:
2877             break;
2878     }
2879 
2880     /* Print each failed action */
2881     for (xml_op = __xml_first_child(data_set->failed); xml_op != NULL;
2882          xml_op = __xml_next(xml_op)) {
2883         print_failed_action(stream, xml_op);
2884     }
2885 
2886     /* End section */
2887     switch (output_format) {
2888         case mon_output_plain:
2889         case mon_output_console:
2890             print_as("\n");
2891             break;
2892 
2893         case mon_output_html:
2894         case mon_output_cgi:
2895             fprintf(stream, " </ul>\n");
2896             break;
2897 
2898         case mon_output_xml:
2899             fprintf(stream, "    </failures>\n");
2900             break;
2901 
2902         default:
2903             break;
2904     }
2905 }
2906 
2907 /*!
2908  * \internal
2909  * \brief Print cluster status to screen
2910  *
2911  * This uses the global display preferences set by command-line options
2912  * to display cluster status in a human-friendly way.
2913  *
2914  * \param[in] data_set   Working set of CIB state
2915  */
2916 static void
2917 print_status(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2918 {
2919     GListPtr gIter = NULL;
2920     int print_opts = get_resource_display_options();
2921 
2922     /* space-separated lists of node names */
2923     char *online_nodes = NULL;
2924     char *online_remote_nodes = NULL;
2925     char *online_guest_nodes = NULL;
2926     char *offline_nodes = NULL;
2927     char *offline_remote_nodes = NULL;
2928 
2929     if (output_format == mon_output_console) {
2930         blank_screen();
2931     }
2932     print_cluster_summary(stdout, data_set);
2933     print_as("\n");
2934 
2935     /* Gather node information (and print if in bad state or grouping by node) */
2936     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
2937         node_t *node = (node_t *) gIter->data;
2938         const char *node_mode = NULL;
2939         char *node_name = get_node_display_name(node);
2940 
2941         /* Get node mode */
2942         if (node->details->unclean) {
2943             if (node->details->online) {
2944                 node_mode = "UNCLEAN (online)";
2945 
2946             } else if (node->details->pending) {
2947                 node_mode = "UNCLEAN (pending)";
2948 
2949             } else {
2950                 node_mode = "UNCLEAN (offline)";
2951             }
2952 
2953         } else if (node->details->pending) {
2954             node_mode = "pending";
2955 
2956         } else if (node->details->standby_onfail && node->details->online) {
2957             node_mode = "standby (on-fail)";
2958 
2959         } else if (node->details->standby) {
2960             if (node->details->online) {
2961                 node_mode = "standby";
2962             } else {
2963                 node_mode = "OFFLINE (standby)";
2964             }
2965 
2966         } else if (node->details->maintenance) {
2967             if (node->details->online) {
2968                 node_mode = "maintenance";
2969             } else {
2970                 node_mode = "OFFLINE (maintenance)";
2971             }
2972 
2973         } else if (node->details->online) {
2974             node_mode = "online";
2975             if (group_by_node == FALSE) {
2976                 if (is_container_remote_node(node)) {
2977                     online_guest_nodes = add_list_element(online_guest_nodes, node_name);
2978                 } else if (is_baremetal_remote_node(node)) {
2979                     online_remote_nodes = add_list_element(online_remote_nodes, node_name);
2980                 } else {
2981                     online_nodes = add_list_element(online_nodes, node_name);
2982                 }
2983                 free(node_name);
2984                 continue;
2985             }
2986         } else {
2987             node_mode = "OFFLINE";
2988             if (group_by_node == FALSE) {
2989                 if (is_baremetal_remote_node(node)) {
2990                     offline_remote_nodes = add_list_element(offline_remote_nodes, node_name);
2991                 } else if (is_container_remote_node(node)) {
2992                     /* ignore offline guest nodes */
2993                 } else {
2994                     offline_nodes = add_list_element(offline_nodes, node_name);
2995                 }
2996                 free(node_name);
2997                 continue;
2998             }
2999         }
3000 
3001         /* If we get here, node is in bad state, or we're grouping by node */
3002 
3003         /* Print the node name and status */
3004         if (is_container_remote_node(node)) {
3005             print_as("Guest");
3006         } else if (is_baremetal_remote_node(node)) {
3007             print_as("Remote");
3008         }
3009         print_as("Node %s: %s\n", node_name, node_mode);
3010 
3011         /* If we're grouping by node, print its resources */
3012         if (group_by_node) {
3013             if (print_brief) {
3014                 print_rscs_brief(node->details->running_rsc, "\t", print_opts | pe_print_rsconly,
3015                                  stdout, FALSE);
3016             } else {
3017                 GListPtr gIter2 = NULL;
3018 
3019                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
3020                     resource_t *rsc = (resource_t *) gIter2->data;
3021 
3022                     rsc->fns->print(rsc, "\t", print_opts | pe_print_rsconly, stdout);
3023                 }
3024             }
3025         }
3026         free(node_name);
3027     }
3028 
3029     /* If we're not grouping by node, summarize nodes by status */
3030     if (online_nodes) {
3031         print_as("Online: [%s ]\n", online_nodes);
3032         free(online_nodes);
3033     }
3034     if (offline_nodes) {
3035         print_as("OFFLINE: [%s ]\n", offline_nodes);
3036         free(offline_nodes);
3037     }
3038     if (online_remote_nodes) {
3039         print_as("RemoteOnline: [%s ]\n", online_remote_nodes);
3040         free(online_remote_nodes);
3041     }
3042     if (offline_remote_nodes) {
3043         print_as("RemoteOFFLINE: [%s ]\n", offline_remote_nodes);
3044         free(offline_remote_nodes);
3045     }
3046     if (online_guest_nodes) {
3047         print_as("GuestOnline: [%s ]\n", online_guest_nodes);
3048         free(online_guest_nodes);
3049     }
3050 
3051     /* Print resources section, if needed */
3052     print_resources(stdout, data_set, print_opts);
3053 
3054     /* print Node Attributes section if requested */
3055     if (show & mon_show_attributes) {
3056         print_node_attributes(stdout, data_set);
3057     }
3058 
3059     /* If requested, print resource operations (which includes failcounts)
3060      * or just failcounts
3061      */
3062     if (show & (mon_show_operations | mon_show_failcounts)) {
3063         print_node_summary(stdout, data_set,
3064                            ((show & mon_show_operations)? TRUE : FALSE));
3065     }
3066 
3067     /* If there were any failed actions, print them */
3068     if (xml_has_children(data_set->failed)) {
3069         print_failed_actions(stdout, data_set);
3070     }
3071 
3072     /* Print tickets if requested */
3073     if (show & mon_show_tickets) {
3074         print_cluster_tickets(stdout, data_set);
3075     }
3076 
3077     /* Print negative location constraints if requested */
3078     if (show & mon_show_bans) {
3079         print_neg_locations(stdout, data_set);
3080     }
3081 
3082 #if CURSES_ENABLED
3083     if (output_format == mon_output_console) {
3084         refresh();
3085     }
3086 #endif
3087 }
3088 
3089 /*!
3090  * \internal
3091  * \brief Print cluster status in XML format
3092  *
3093  * \param[in] data_set   Working set of CIB state
3094  */
3095 static void
3096 print_xml_status(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
3097 {
3098     FILE *stream = stdout;
3099     GListPtr gIter = NULL;
3100     int print_opts = get_resource_display_options();
3101 
3102     fprintf(stream, "<?xml version=\"1.0\"?>\n");
3103     fprintf(stream, "<crm_mon version=\"%s\">\n", VERSION);
3104 
3105     print_cluster_summary(stream, data_set);
3106 
3107     /*** NODES ***/
3108     fprintf(stream, "    <nodes>\n");
3109     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
3110         node_t *node = (node_t *) gIter->data;
3111         const char *node_type = "unknown";
3112 
3113         switch (node->details->type) {
3114             case node_member:
3115                 node_type = "member";
3116                 break;
3117             case node_remote:
3118                 node_type = "remote";
3119                 break;
3120             case node_ping:
3121                 node_type = "ping";
3122                 break;
3123         }
3124 
3125         fprintf(stream, "        <node name=\"%s\" ", node->details->uname);
3126         fprintf(stream, "id=\"%s\" ", node->details->id);
3127         fprintf(stream, "online=\"%s\" ", node->details->online ? "true" : "false");
3128         fprintf(stream, "standby=\"%s\" ", node->details->standby ? "true" : "false");
3129         fprintf(stream, "standby_onfail=\"%s\" ", node->details->standby_onfail ? "true" : "false");
3130         fprintf(stream, "maintenance=\"%s\" ", node->details->maintenance ? "true" : "false");
3131         fprintf(stream, "pending=\"%s\" ", node->details->pending ? "true" : "false");
3132         fprintf(stream, "unclean=\"%s\" ", node->details->unclean ? "true" : "false");
3133         fprintf(stream, "shutdown=\"%s\" ", node->details->shutdown ? "true" : "false");
3134         fprintf(stream, "expected_up=\"%s\" ", node->details->expected_up ? "true" : "false");
3135         fprintf(stream, "is_dc=\"%s\" ", node->details->is_dc ? "true" : "false");
3136         fprintf(stream, "resources_running=\"%d\" ", g_list_length(node->details->running_rsc));
3137         fprintf(stream, "type=\"%s\" ", node_type);
3138         if (is_container_remote_node(node)) {
3139             fprintf(stream, "id_as_resource=\"%s\" ", node->details->remote_rsc->container->id);
3140         }
3141 
3142         if (group_by_node) {
3143             GListPtr lpc2 = NULL;
3144 
3145             fprintf(stream, ">\n");
3146             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
3147                 resource_t *rsc = (resource_t *) lpc2->data;
3148 
3149                 rsc->fns->print(rsc, "            ", print_opts | pe_print_rsconly, stream);
3150             }
3151             fprintf(stream, "        </node>\n");
3152         } else {
3153             fprintf(stream, "/>\n");
3154         }
3155     }
3156     fprintf(stream, "    </nodes>\n");
3157 
3158     /* Print resources section, if needed */
3159     print_resources(stream, data_set, print_opts);
3160 
3161     /* print Node Attributes section if requested */
3162     if (show & mon_show_attributes) {
3163         print_node_attributes(stream, data_set);
3164     }
3165 
3166     /* If requested, print resource operations (which includes failcounts)
3167      * or just failcounts
3168      */
3169     if (show & (mon_show_operations | mon_show_failcounts)) {
3170         print_node_summary(stream, data_set,
3171                            ((show & mon_show_operations)? TRUE : FALSE));
3172     }
3173 
3174     /* If there were any failed actions, print them */
3175     if (xml_has_children(data_set->failed)) {
3176         print_failed_actions(stream, data_set);
3177     }
3178 
3179     /* Print tickets if requested */
3180     if (show & mon_show_tickets) {
3181         print_cluster_tickets(stream, data_set);
3182     }
3183 
3184     /* Print negative location constraints if requested */
3185     if (show & mon_show_bans) {
3186         print_neg_locations(stream, data_set);
3187     }
3188 
3189     fprintf(stream, "</crm_mon>\n");
3190     fflush(stream);
3191     fclose(stream);
3192 }
3193 
3194 /*!
3195  * \internal
3196  * \brief Print cluster status in HTML format (with HTTP headers if CGI)
3197  *
3198  * \param[in] data_set   Working set of CIB state
3199  * \param[in] filename   Name of file to write HTML to (ignored if CGI)
3200  *
3201  * \return 0 on success, -1 on error
3202  */
3203 static int
3204 print_html_status(pe_working_set_t * data_set, const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
3205 {
3206     FILE *stream;
3207     GListPtr gIter = NULL;
3208     char *filename_tmp = NULL;
3209     int print_opts = get_resource_display_options();
3210 
3211     if (output_format == mon_output_cgi) {
3212         stream = stdout;
3213         fprintf(stream, "Content-Type: text/html\n\n");
3214 
3215     } else {
3216         filename_tmp = crm_concat(filename, "tmp", '.');
3217         stream = fopen(filename_tmp, "w");
3218         if (stream == NULL) {
3219             crm_perror(LOG_ERR, "Cannot open %s for writing", filename_tmp);
3220             free(filename_tmp);
3221             return -1;
3222         }
3223     }
3224 
3225     fprintf(stream, "<html>\n");
3226     fprintf(stream, " <head>\n");
3227     fprintf(stream, "  <title>Cluster status</title>\n");
3228     fprintf(stream, "  <meta http-equiv=\"refresh\" content=\"%d\">\n", reconnect_msec / 1000);
3229     fprintf(stream, " </head>\n");
3230     fprintf(stream, "<body>\n");
3231 
3232     print_cluster_summary(stream, data_set);
3233 
3234     /*** NODE LIST ***/
3235 
3236     fprintf(stream, " <hr />\n <h2>Node List</h2>\n");
3237     fprintf(stream, "<ul>\n");
3238     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
3239         node_t *node = (node_t *) gIter->data;
3240         char *node_name = get_node_display_name(node);
3241 
3242         fprintf(stream, "<li>Node: %s: ", node_name);
3243         if (node->details->standby_onfail && node->details->online) {
3244             fprintf(stream, "<font color=\"orange\">standby (on-fail)</font>\n");
3245         } else if (node->details->standby && node->details->online) {
3246             fprintf(stream, "<font color=\"orange\">standby</font>\n");
3247         } else if (node->details->standby) {
3248             fprintf(stream, "<font color=\"red\">OFFLINE (standby)</font>\n");
3249         } else if (node->details->maintenance && node->details->online) {
3250             fprintf(stream, "<font color=\"blue\">maintenance</font>\n");
3251         } else if (node->details->maintenance) {
3252             fprintf(stream, "<font color=\"red\">OFFLINE (maintenance)</font>\n");
3253         } else if (node->details->online) {
3254             fprintf(stream, "<font color=\"green\">online</font>\n");
3255         } else {
3256             fprintf(stream, "<font color=\"red\">OFFLINE</font>\n");
3257         }
3258         if (print_brief && group_by_node) {
3259             fprintf(stream, "<ul>\n");
3260             print_rscs_brief(node->details->running_rsc, NULL, print_opts | pe_print_rsconly,
3261                              stream, FALSE);
3262             fprintf(stream, "</ul>\n");
3263 
3264         } else if (group_by_node) {
3265             GListPtr lpc2 = NULL;
3266 
3267             fprintf(stream, "<ul>\n");
3268             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
3269                 resource_t *rsc = (resource_t *) lpc2->data;
3270 
3271                 fprintf(stream, "<li>");
3272                 rsc->fns->print(rsc, NULL, print_opts | pe_print_rsconly, stream);
3273                 fprintf(stream, "</li>\n");
3274             }
3275             fprintf(stream, "</ul>\n");
3276         }
3277         fprintf(stream, "</li>\n");
3278         free(node_name);
3279     }
3280     fprintf(stream, "</ul>\n");
3281 
3282     /* Print resources section, if needed */
3283     print_resources(stream, data_set, print_opts);
3284 
3285     /* print Node Attributes section if requested */
3286     if (show & mon_show_attributes) {
3287         print_node_attributes(stream, data_set);
3288     }
3289 
3290     /* If requested, print resource operations (which includes failcounts)
3291      * or just failcounts
3292      */
3293     if (show & (mon_show_operations | mon_show_failcounts)) {
3294         print_node_summary(stream, data_set,
3295                            ((show & mon_show_operations)? TRUE : FALSE));
3296     }
3297 
3298     /* If there were any failed actions, print them */
3299     if (xml_has_children(data_set->failed)) {
3300         print_failed_actions(stream, data_set);
3301     }
3302 
3303     /* Print tickets if requested */
3304     if (show & mon_show_tickets) {
3305         print_cluster_tickets(stream, data_set);
3306     }
3307 
3308     /* Print negative location constraints if requested */
3309     if (show & mon_show_bans) {
3310         print_neg_locations(stream, data_set);
3311     }
3312 
3313     fprintf(stream, "</body>\n");
3314     fprintf(stream, "</html>\n");
3315     fflush(stream);
3316     fclose(stream);
3317 
3318     if (output_format != mon_output_cgi) {
3319         if (rename(filename_tmp, filename) != 0) {
3320             crm_perror(LOG_ERR, "Unable to rename %s->%s", filename_tmp, filename);
3321         }
3322         free(filename_tmp);
3323     }
3324     return 0;
3325 }
3326 
3327 #if ENABLE_SNMP
3328 #  include <net-snmp/net-snmp-config.h>
3329 #  include <net-snmp/snmpv3_api.h>
3330 #  include <net-snmp/agent/agent_trap.h>
3331 #  include <net-snmp/library/snmp_client.h>
3332 #  include <net-snmp/library/mib.h>
3333 #  include <net-snmp/library/snmp_debug.h>
3334 
3335 #  define add_snmp_field(list, oid_string, value) do {                  \
3336         oid name[MAX_OID_LEN];                                          \
3337         size_t name_length = MAX_OID_LEN;                               \
3338         if (snmp_parse_oid(oid_string, name, &name_length)) {           \
3339             int s_rc = snmp_add_var(list, name, name_length, 's', (value)); \
3340             if(s_rc != 0) {                                             \
3341                 crm_err("Could not add %s=%s rc=%d", oid_string, value, s_rc); \
3342             } else {                                                    \
3343                 crm_trace("Added %s=%s", oid_string, value);            \
3344             }                                                           \
3345         } else {                                                        \
3346             crm_err("Could not parse OID: %s", oid_string);             \
3347         }                                                               \
3348     } while(0)                                                          \
3349 
3350 #  define add_snmp_field_int(list, oid_string, value) do {              \
3351         oid name[MAX_OID_LEN];                                          \
3352         size_t name_length = MAX_OID_LEN;                               \
3353         if (snmp_parse_oid(oid_string, name, &name_length)) {           \
3354             if(NULL == snmp_pdu_add_variable(                           \
3355                    list, name, name_length, ASN_INTEGER,                \
3356                    (u_char *) & value, sizeof(value))) {                \
3357                 crm_err("Could not add %s=%d", oid_string, value);      \
3358             } else {                                                    \
3359                 crm_trace("Added %s=%d", oid_string, value);            \
3360             }                                                           \
3361         } else {                                                        \
3362             crm_err("Could not parse OID: %s", oid_string);             \
3363         }                                                               \
3364     } while(0)                                                          \
3365 
3366 static int
3367 snmp_input(int operation, netsnmp_session * session, int reqid, netsnmp_pdu * pdu, void *magic)
     /* [previous][next][first][last][top][bottom][index][help] */
3368 {
3369     return 1;
3370 }
3371 
3372 static netsnmp_session *
3373 crm_snmp_init(const char *target, char *community)
     /* [previous][next][first][last][top][bottom][index][help] */
3374 {
3375     static netsnmp_session *session = NULL;
3376 
3377 #  ifdef NETSNMPV53
3378     char target53[128];
3379 
3380     snprintf(target53, sizeof(target53), "%s:162", target);
3381 #  endif
3382 
3383     if (session) {
3384         return session;
3385     }
3386 
3387     if (target == NULL) {
3388         return NULL;
3389     }
3390 
3391     if (get_crm_log_level() > LOG_INFO) {
3392         char *debug_tokens = strdup("run:shell,snmptrap,tdomain");
3393 
3394         debug_register_tokens(debug_tokens);
3395         snmp_set_do_debugging(1);
3396     }
3397 
3398     session = calloc(1, sizeof(netsnmp_session));
3399     snmp_sess_init(session);
3400     session->version = SNMP_VERSION_2c;
3401     session->callback = snmp_input;
3402     session->callback_magic = NULL;
3403 
3404     if (community) {
3405         session->community_len = strlen(community);
3406         session->community = (unsigned char *)community;
3407     }
3408 
3409     session = snmp_add(session,
3410 #  ifdef NETSNMPV53
3411                        netsnmp_tdomain_transport(target53, 0, "udp"),
3412 #  else
3413                        netsnmp_transport_open_client("snmptrap", target),
3414 #  endif
3415                        NULL, NULL);
3416 
3417     if (session == NULL) {
3418         snmp_sess_perror("Could not create snmp transport", session);
3419     }
3420     return session;
3421 }
3422 
3423 #endif
3424 
3425 static int
3426 send_snmp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
     /* [previous][next][first][last][top][bottom][index][help] */
3427                int status, const char *desc)
3428 {
3429     int ret = 1;
3430 
3431 #if ENABLE_SNMP
3432     static oid snmptrap_oid[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
3433     static oid sysuptime_oid[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
3434 
3435     netsnmp_pdu *trap_pdu;
3436     netsnmp_session *session = crm_snmp_init(snmp_target, snmp_community);
3437 
3438     trap_pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
3439     if (!trap_pdu) {
3440         crm_err("Failed to create SNMP notification");
3441         return SNMPERR_GENERR;
3442     }
3443 
3444     if (1) {
3445         /* send uptime */
3446         char csysuptime[20];
3447         time_t now = time(NULL);
3448 
3449         sprintf(csysuptime, "%lld", (long long) now);
3450         snmp_add_var(trap_pdu, sysuptime_oid, sizeof(sysuptime_oid) / sizeof(oid), 't', csysuptime);
3451     }
3452 
3453     /* Indicate what the trap is by setting snmpTrapOid.0 */
3454     ret =
3455         snmp_add_var(trap_pdu, snmptrap_oid, sizeof(snmptrap_oid) / sizeof(oid), 'o',
3456                      snmp_crm_trap_oid);
3457     if (ret != 0) {
3458         crm_err("Failed set snmpTrapOid.0=%s", snmp_crm_trap_oid);
3459         return ret;
3460     }
3461 
3462     /* Add extries to the trap */
3463     if (rsc) {
3464         add_snmp_field(trap_pdu, snmp_crm_oid_rsc, rsc);
3465     }
3466     add_snmp_field(trap_pdu, snmp_crm_oid_node, node);
3467     add_snmp_field(trap_pdu, snmp_crm_oid_task, task);
3468     add_snmp_field(trap_pdu, snmp_crm_oid_desc, desc);
3469 
3470     add_snmp_field_int(trap_pdu, snmp_crm_oid_rc, rc);
3471     add_snmp_field_int(trap_pdu, snmp_crm_oid_trc, target_rc);
3472     add_snmp_field_int(trap_pdu, snmp_crm_oid_status, status);
3473 
3474     /* Send and cleanup */
3475     ret = snmp_send(session, trap_pdu);
3476     if (ret == 0) {
3477         /* error */
3478         snmp_sess_perror("Could not send SNMP trap", session);
3479         snmp_free_pdu(trap_pdu);
3480         ret = SNMPERR_GENERR;
3481     } else {
3482         ret = SNMPERR_SUCCESS;
3483     }
3484 #else
3485     crm_err("Sending SNMP traps is not supported by this installation");
3486 #endif
3487     return ret;
3488 }
3489 
3490 #if ENABLE_ESMTP
3491 #  include <auth-client.h>
3492 #  include <libesmtp.h>
3493 
3494 static void
3495 print_recipient_status(smtp_recipient_t recipient, const char *mailbox, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3496 {
3497     const smtp_status_t *status;
3498 
3499     status = smtp_recipient_status(recipient);
3500     printf("%s: %d %s", mailbox, status->code, status->text);
3501 }
3502 
3503 static void
3504 event_cb(smtp_session_t session, int event_no, void *arg, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
3505 {
3506     int *ok;
3507     va_list alist;
3508 
3509     va_start(alist, arg);
3510     switch (event_no) {
3511         case SMTP_EV_CONNECT:
3512         case SMTP_EV_MAILSTATUS:
3513         case SMTP_EV_RCPTSTATUS:
3514         case SMTP_EV_MESSAGEDATA:
3515         case SMTP_EV_MESSAGESENT:
3516         case SMTP_EV_DISCONNECT:
3517             break;
3518 
3519         case SMTP_EV_WEAK_CIPHER:{
3520                 int bits = va_arg(alist, long);
3521                 ok = va_arg(alist, int *);
3522 
3523                 crm_debug("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.", bits);
3524                 *ok = 1;
3525                 break;
3526             }
3527         case SMTP_EV_STARTTLS_OK:
3528             crm_debug("SMTP_EV_STARTTLS_OK - TLS started here.");
3529             break;
3530 
3531         case SMTP_EV_INVALID_PEER_CERTIFICATE:{
3532                 long vfy_result = va_arg(alist, long);
3533                 ok = va_arg(alist, int *);
3534 
3535                 /* There is a table in handle_invalid_peer_certificate() of mail-file.c */
3536                 crm_err("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld", vfy_result);
3537                 *ok = 1;
3538                 break;
3539             }
3540         case SMTP_EV_NO_PEER_CERTIFICATE:
3541             ok = va_arg(alist, int *);
3542 
3543             crm_debug("SMTP_EV_NO_PEER_CERTIFICATE - accepted.");
3544             *ok = 1;
3545             break;
3546         case SMTP_EV_WRONG_PEER_CERTIFICATE:
3547             ok = va_arg(alist, int *);
3548 
3549             crm_debug("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted.");
3550             *ok = 1;
3551             break;
3552         case SMTP_EV_NO_CLIENT_CERTIFICATE:
3553             ok = va_arg(alist, int *);
3554 
3555             crm_debug("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted.");
3556             *ok = 1;
3557             break;
3558         default:
3559             crm_debug("Got event: %d - ignored.", event_no);
3560     }
3561     va_end(alist);
3562 }
3563 #endif
3564 
3565 #define BODY_MAX 2048
3566 
3567 #if ENABLE_ESMTP
3568 static void
3569 crm_smtp_debug(const char *buf, int buflen, int writing, void *arg)
     /* [previous][next][first][last][top][bottom][index][help] */
3570 {
3571     char type = 0;
3572     int lpc = 0, last = 0, level = *(int *)arg;
3573 
3574     if (writing == SMTP_CB_HEADERS) {
3575         type = 'H';
3576     } else if (writing) {
3577         type = 'C';
3578     } else {
3579         type = 'S';
3580     }
3581 
3582     for (; lpc < buflen; lpc++) {
3583         switch (buf[lpc]) {
3584             case 0:
3585             case '\n':
3586                 if (last > 0) {
3587                     do_crm_log(level, "   %.*s", lpc - last, buf + last);
3588                 } else {
3589                     do_crm_log(level, "%c: %.*s", type, lpc - last, buf + last);
3590                 }
3591                 last = lpc + 1;
3592                 break;
3593         }
3594     }
3595 }
3596 #endif
3597 
3598 static int
3599 send_custom_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
     /* [previous][next][first][last][top][bottom][index][help] */
3600                  int status, const char *desc)
3601 {
3602     pid_t pid;
3603 
3604     /*setenv needs chars, these are ints */
3605     char *rc_s = crm_itoa(rc);
3606     char *status_s = crm_itoa(status);
3607     char *target_rc_s = crm_itoa(target_rc);
3608 
3609     crm_debug("Sending external notification to '%s' via '%s'", external_recipient, external_agent);
3610 
3611     if(rsc) {
3612         setenv("CRM_notify_rsc", rsc, 1);
3613     }
3614     if (external_recipient) {
3615         setenv("CRM_notify_recipient", external_recipient, 1);
3616     }
3617     setenv("CRM_notify_node", node, 1);
3618     setenv("CRM_notify_task", task, 1);
3619     setenv("CRM_notify_desc", desc, 1);
3620     setenv("CRM_notify_rc", rc_s, 1);
3621     setenv("CRM_notify_target_rc", target_rc_s, 1);
3622     setenv("CRM_notify_status", status_s, 1);
3623 
3624     pid = fork();
3625     if (pid == -1) {
3626         crm_perror(LOG_ERR, "notification fork() failed.");
3627     }
3628     if (pid == 0) {
3629         /* crm_debug("notification: I am the child. Executing the nofitication program."); */
3630         execl(external_agent, external_agent, NULL);
3631         exit(EXIT_FAILURE);
3632     }
3633 
3634     crm_trace("Finished running custom notification program '%s'.", external_agent);
3635     free(target_rc_s);
3636     free(status_s);
3637     free(rc_s);
3638     return 0;
3639 }
3640 
3641 static int
3642 send_smtp_trap(const char *node, const char *rsc, const char *task, int target_rc, int rc,
     /* [previous][next][first][last][top][bottom][index][help] */
3643                int status, const char *desc)
3644 {
3645 #if ENABLE_ESMTP
3646     smtp_session_t session;
3647     smtp_message_t message;
3648     auth_context_t authctx;
3649     struct sigaction sa;
3650 
3651     int len = 25; /* Note: Check extra padding on the Subject line below */
3652     int noauth = 1;
3653     int smtp_debug = LOG_DEBUG;
3654     char crm_mail_body[BODY_MAX];
3655     char *crm_mail_subject = NULL;
3656 
3657     memset(&sa, 0, sizeof(struct sigaction));
3658 
3659     if (node == NULL) {
3660         node = "-";
3661     }
3662     if (rsc == NULL) {
3663         rsc = "-";
3664     }
3665     if (desc == NULL) {
3666         desc = "-";
3667     }
3668 
3669     if (crm_mail_to == NULL) {
3670         return 1;
3671     }
3672 
3673     if (crm_mail_host == NULL) {
3674         crm_mail_host = "localhost:25";
3675     }
3676 
3677     if (crm_mail_prefix == NULL) {
3678         crm_mail_prefix = "Cluster notification";
3679     }
3680 
3681     crm_debug("Sending '%s' mail to %s via %s", crm_mail_prefix, crm_mail_to, crm_mail_host);
3682 
3683     len += strlen(crm_mail_prefix);
3684     len += strlen(task);
3685     len += strlen(rsc);
3686     len += strlen(node);
3687     len += strlen(desc);
3688     len++;
3689 
3690     crm_mail_subject = calloc(1, len);
3691     /* If you edit this line, ensure you allocate enough memory for it by altering 'len' above */
3692     snprintf(crm_mail_subject, len, "%s - %s event for %s on %s: %s\r\n", crm_mail_prefix, task,
3693              rsc, node, desc);
3694 
3695     len = 0;
3696     len += snprintf(crm_mail_body + len, BODY_MAX - len, "\r\n%s\r\n", crm_mail_prefix);
3697     len += snprintf(crm_mail_body + len, BODY_MAX - len, "====\r\n\r\n");
3698     if (rc == target_rc) {
3699         len += snprintf(crm_mail_body + len, BODY_MAX - len,
3700                         "Completed operation %s for resource %s on %s\r\n", task, rsc, node);
3701     } else {
3702         len += snprintf(crm_mail_body + len, BODY_MAX - len,
3703                         "Operation %s for resource %s on %s failed: %s\r\n", task, rsc, node, desc);
3704     }
3705 
3706     len += snprintf(crm_mail_body + len, BODY_MAX - len, "\r\nDetails:\r\n");
3707     len += snprintf(crm_mail_body + len, BODY_MAX - len,
3708                     "\toperation status: (%d) %s\r\n", status, services_lrm_status_str(status));
3709     if (status == PCMK_LRM_OP_DONE) {
3710         len += snprintf(crm_mail_body + len, BODY_MAX - len,
3711                         "\tscript returned: (%d) %s\r\n", rc, services_ocf_exitcode_str(rc));
3712         len += snprintf(crm_mail_body + len, BODY_MAX - len,
3713                         "\texpected return value: (%d) %s\r\n", target_rc,
3714                         services_ocf_exitcode_str(target_rc));
3715     }
3716 
3717     auth_client_init();
3718     session = smtp_create_session();
3719     message = smtp_add_message(session);
3720 
3721     smtp_starttls_enable(session, Starttls_ENABLED);
3722 
3723     sa.sa_handler = SIG_IGN;
3724     sigemptyset(&sa.sa_mask);
3725     sa.sa_flags = 0;
3726     sigaction(SIGPIPE, &sa, NULL);
3727 
3728     smtp_set_server(session, crm_mail_host);
3729 
3730     authctx = auth_create_context();
3731     auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
3732 
3733     smtp_set_eventcb(session, event_cb, NULL);
3734 
3735     /* Now tell libESMTP it can use the SMTP AUTH extension.
3736      */
3737     if (!noauth) {
3738         crm_debug("Adding authentication context");
3739         smtp_auth_set_context(session, authctx);
3740     }
3741 
3742     if (crm_mail_from == NULL) {
3743         struct utsname us;
3744         char auto_from[BODY_MAX];
3745 
3746         CRM_ASSERT(uname(&us) == 0);
3747         snprintf(auto_from, BODY_MAX, "crm_mon@%s", us.nodename);
3748         smtp_set_reverse_path(message, auto_from);
3749 
3750     } else {
3751         /* NULL is ok */
3752         smtp_set_reverse_path(message, crm_mail_from);
3753     }
3754 
3755     smtp_set_header(message, "To", NULL /*phrase */ , NULL /*addr */ ); /* "Phrase" <addr> */
3756     smtp_add_recipient(message, crm_mail_to);
3757 
3758     /* Set the Subject: header and override any subject line in the message headers. */
3759     smtp_set_header(message, "Subject", crm_mail_subject);
3760     smtp_set_header_option(message, "Subject", Hdr_OVERRIDE, 1);
3761 
3762     smtp_set_message_str(message, crm_mail_body);
3763     smtp_set_monitorcb(session, crm_smtp_debug, &smtp_debug, 1);
3764 
3765     if (smtp_start_session(session)) {
3766         char buf[128];
3767         int rc = smtp_errno();
3768 
3769         crm_err("SMTP server problem: %s (%d)", smtp_strerror(rc, buf, sizeof buf), rc);
3770 
3771     } else {
3772         char buf[128];
3773         int rc = smtp_errno();
3774         const smtp_status_t *smtp_status = smtp_message_transfer_status(message);
3775 
3776         if (rc != 0) {
3777             crm_err("SMTP server problem: %s (%d)", smtp_strerror(rc, buf, sizeof buf), rc);
3778         }
3779         crm_info("Send status: %d %s", smtp_status->code, crm_str(smtp_status->text));
3780         smtp_enumerate_recipients(message, print_recipient_status, NULL);
3781     }
3782 
3783     smtp_destroy_session(session);
3784     auth_destroy_context(authctx);
3785     auth_client_exit();
3786 #endif
3787     return 0;
3788 }
3789 
3790 static void
3791 handle_rsc_op(xmlNode * xml, const char *node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
3792 {
3793     int rc = -1;
3794     int status = -1;
3795     int action = -1;
3796     int interval = 0;
3797     int target_rc = -1;
3798     int transition_num = -1;
3799     gboolean notify = TRUE;
3800 
3801     char *rsc = NULL;
3802     char *task = NULL;
3803     const char *desc = NULL;
3804     const char *magic = NULL;
3805     const char *id = NULL;
3806     char *update_te_uuid = NULL;
3807     const char *node = NULL;
3808 
3809     xmlNode *n = xml;
3810     xmlNode * rsc_op = xml;
3811 
3812     if(strcmp((const char*)xml->name, XML_LRM_TAG_RSC_OP) != 0) {
3813         xmlNode *cIter;
3814 
3815         for(cIter = xml->children; cIter; cIter = cIter->next) {
3816             handle_rsc_op(cIter, node_id);
3817         }
3818 
3819         return;
3820     }
3821 
3822     id = crm_element_value(rsc_op, XML_LRM_ATTR_TASK_KEY);
3823     if (id == NULL) {
3824         /* Compatibility with <= 1.1.5 */
3825         id = ID(rsc_op);
3826     }
3827 
3828     magic = crm_element_value(rsc_op, XML_ATTR_TRANSITION_MAGIC);
3829     if (magic == NULL) {
3830         /* non-change */
3831         return;
3832     }
3833 
3834     if (FALSE == decode_transition_magic(magic, &update_te_uuid, &transition_num, &action,
3835                                          &status, &rc, &target_rc)) {
3836         crm_err("Invalid event %s detected for %s", magic, id);
3837         return;
3838     }
3839 
3840     if (parse_op_key(id, &rsc, &task, &interval) == FALSE) {
3841         crm_err("Invalid event detected for %s", id);
3842         goto bail;
3843     }
3844 
3845     node = crm_element_value(rsc_op, XML_LRM_ATTR_TARGET);
3846 
3847     while (n != NULL && safe_str_neq(XML_CIB_TAG_STATE, TYPE(n))) {
3848         n = n->parent;
3849     }
3850 
3851     if(node == NULL && n) {
3852         node = crm_element_value(n, XML_ATTR_UNAME);
3853     }
3854 
3855     if (node == NULL && n) {
3856         node = ID(n);
3857     }
3858 
3859     if (node == NULL) {
3860         node = node_id;
3861     }
3862 
3863     if (node == NULL) {
3864         crm_err("No node detected for event %s (%s)", magic, id);
3865         goto bail;
3866     }
3867 
3868     /* look up where we expected it to be? */
3869     desc = pcmk_strerror(pcmk_ok);
3870     if (status == PCMK_LRM_OP_DONE && target_rc == rc) {
3871         crm_notice("%s of %s on %s completed: %s", task, rsc, node, desc);
3872         if (rc == PCMK_OCF_NOT_RUNNING) {
3873             notify = FALSE;
3874         }
3875 
3876     } else if (status == PCMK_LRM_OP_DONE) {
3877         desc = services_ocf_exitcode_str(rc);
3878         crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
3879 
3880     } else {
3881         desc = services_lrm_status_str(status);
3882         crm_warn("%s of %s on %s failed: %s", task, rsc, node, desc);
3883     }
3884 
3885     if (notify && snmp_target) {
3886         send_snmp_trap(node, rsc, task, target_rc, rc, status, desc);
3887     }
3888     if (notify && crm_mail_to) {
3889         send_smtp_trap(node, rsc, task, target_rc, rc, status, desc);
3890     }
3891     if (notify && external_agent) {
3892         send_custom_trap(node, rsc, task, target_rc, rc, status, desc);
3893     }
3894   bail:
3895     free(update_te_uuid);
3896     free(rsc);
3897     free(task);
3898 }
3899 
3900 static gboolean
3901 mon_trigger_refresh(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
3902 {
3903     mainloop_set_trigger(refresh_trigger);
3904     return FALSE;
3905 }
3906 
3907 #define NODE_PATT "/lrm[@id="
3908 static char *get_node_from_xpath(const char *xpath) 
     /* [previous][next][first][last][top][bottom][index][help] */
3909 {
3910     char *nodeid = NULL;
3911     char *tmp = strstr(xpath, NODE_PATT);
3912 
3913     if(tmp) {
3914         tmp += strlen(NODE_PATT);
3915         tmp += 1;
3916 
3917         nodeid = strdup(tmp);
3918         tmp = strstr(nodeid, "\'");
3919         CRM_ASSERT(tmp);
3920         tmp[0] = 0;
3921     }
3922     return nodeid;
3923 }
3924 
3925 static void crm_diff_update_v2(const char *event, xmlNode * msg) 
     /* [previous][next][first][last][top][bottom][index][help] */
3926 {
3927     xmlNode *change = NULL;
3928     xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
3929 
3930     for (change = __xml_first_child(diff); change != NULL; change = __xml_next(change)) {
3931         const char *name = NULL;
3932         const char *op = crm_element_value(change, XML_DIFF_OP);
3933         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
3934         xmlNode *match = NULL;
3935         const char *node = NULL;
3936 
3937         if(op == NULL) {
3938             continue;
3939 
3940         } else if(strcmp(op, "create") == 0) {
3941             match = change->children;
3942 
3943         } else if(strcmp(op, "move") == 0) {
3944             continue;
3945 
3946         } else if(strcmp(op, "delete") == 0) {
3947             continue;
3948 
3949         } else if(strcmp(op, "modify") == 0) {
3950             match = first_named_child(change, XML_DIFF_RESULT);
3951             if(match) {
3952                 match = match->children;
3953             }
3954         }
3955 
3956         if(match) {
3957             name = (const char *)match->name;
3958         }
3959 
3960         crm_trace("Handling %s operation for %s %p, %s", op, xpath, match, name);
3961         if(xpath == NULL) {
3962             /* Version field, ignore */
3963 
3964         } else if(name == NULL) {
3965             crm_debug("No result for %s operation to %s", op, xpath);
3966             CRM_ASSERT(strcmp(op, "delete") == 0 || strcmp(op, "move") == 0);
3967 
3968         } else if(strcmp(name, XML_TAG_CIB) == 0) {
3969             xmlNode *state = NULL;
3970             xmlNode *status = first_named_child(match, XML_CIB_TAG_STATUS);
3971 
3972             for (state = __xml_first_child(status); state != NULL; state = __xml_next(state)) {
3973                 node = crm_element_value(state, XML_ATTR_UNAME);
3974                 if (node == NULL) {
3975                     node = ID(state);
3976                 }
3977                 handle_rsc_op(state, node);
3978             }
3979 
3980         } else if(strcmp(name, XML_CIB_TAG_STATUS) == 0) {
3981             xmlNode *state = NULL;
3982 
3983             for (state = __xml_first_child(match); state != NULL; state = __xml_next(state)) {
3984                 node = crm_element_value(state, XML_ATTR_UNAME);
3985                 if (node == NULL) {
3986                     node = ID(state);
3987                 }
3988                 handle_rsc_op(state, node);
3989             }
3990 
3991         } else if(strcmp(name, XML_CIB_TAG_STATE) == 0) {
3992             node = crm_element_value(match, XML_ATTR_UNAME);
3993             if (node == NULL) {
3994                 node = ID(match);
3995             }
3996             handle_rsc_op(match, node);
3997 
3998         } else if(strcmp(name, XML_CIB_TAG_LRM) == 0) {
3999             node = ID(match);
4000             handle_rsc_op(match, node);
4001 
4002         } else if(strcmp(name, XML_LRM_TAG_RESOURCES) == 0) {
4003             char *local_node = get_node_from_xpath(xpath);
4004 
4005             handle_rsc_op(match, local_node);
4006             free(local_node);
4007 
4008         } else if(strcmp(name, XML_LRM_TAG_RESOURCE) == 0) {
4009             char *local_node = get_node_from_xpath(xpath);
4010 
4011             handle_rsc_op(match, local_node);
4012             free(local_node);
4013 
4014         } else if(strcmp(name, XML_LRM_TAG_RSC_OP) == 0) {
4015             char *local_node = get_node_from_xpath(xpath);
4016 
4017             handle_rsc_op(match, local_node);
4018             free(local_node);
4019 
4020         } else {
4021             crm_trace("Ignoring %s operation for %s %p, %s", op, xpath, match, name);
4022         }
4023     }
4024 }
4025 
4026 static void crm_diff_update_v1(const char *event, xmlNode * msg) 
     /* [previous][next][first][last][top][bottom][index][help] */
4027 {
4028     /* Process operation updates */
4029     xmlXPathObject *xpathObj = xpath_search(msg,
4030                                             "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
4031                                             "//" XML_LRM_TAG_RSC_OP);
4032     int lpc = 0, max = numXpathResults(xpathObj);
4033 
4034     for (lpc = 0; lpc < max; lpc++) {
4035         xmlNode *rsc_op = getXpathResult(xpathObj, lpc);
4036 
4037         handle_rsc_op(rsc_op, NULL);
4038     }
4039     freeXpathObject(xpathObj);
4040 }
4041 
4042 void
4043 crm_diff_update(const char *event, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
4044 {
4045     int rc = -1;
4046     long now = time(NULL);
4047     static bool stale = FALSE;
4048     static int updates = 0;
4049     static mainloop_timer_t *refresh_timer = NULL;
4050     xmlNode *diff = get_message_xml(msg, F_CIB_UPDATE_RESULT);
4051 
4052     print_dot();
4053 
4054     if(refresh_timer == NULL) {
4055         refresh_timer = mainloop_timer_add("refresh", 2000, FALSE, mon_trigger_refresh, NULL);
4056     }
4057 
4058     if (current_cib != NULL) {
4059         rc = xml_apply_patchset(current_cib, diff, TRUE);
4060 
4061         switch (rc) {
4062             case -pcmk_err_diff_resync:
4063             case -pcmk_err_diff_failed:
4064                 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
4065                 free_xml(current_cib); current_cib = NULL;
4066                 break;
4067             case pcmk_ok:
4068                 updates++;
4069                 break;
4070             default:
4071                 crm_notice("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
4072                 free_xml(current_cib); current_cib = NULL;
4073         }
4074     }
4075 
4076     if (current_cib == NULL) {
4077         crm_trace("Re-requesting the full cib");
4078         cib->cmds->query(cib, NULL, &current_cib, cib_scope_local | cib_sync_call);
4079     }
4080 
4081     if (crm_mail_to || snmp_target || external_agent) {
4082         int format = 0;
4083         crm_element_value_int(diff, "format", &format);
4084         switch(format) {
4085             case 1:
4086                 crm_diff_update_v1(event, msg);
4087                 break;
4088             case 2:
4089                 crm_diff_update_v2(event, msg);
4090                 break;
4091             default:
4092                 crm_err("Unknown patch format: %d", format);
4093         }
4094     }
4095 
4096     if (current_cib == NULL) {
4097         if(!stale) {
4098             print_as("--- Stale data ---");
4099         }
4100         stale = TRUE;
4101         return;
4102     }
4103 
4104     stale = FALSE;
4105     /* Refresh
4106      * - immediately if the last update was more than 5s ago
4107      * - every 10 updates
4108      * - at most 2s after the last update
4109      */
4110     if ((now - last_refresh) > (reconnect_msec / 1000)) {
4111         mainloop_set_trigger(refresh_trigger);
4112         mainloop_timer_stop(refresh_timer);
4113         updates = 0;
4114 
4115     } else if(updates > 10) {
4116         mainloop_set_trigger(refresh_trigger);
4117         mainloop_timer_stop(refresh_timer);
4118         updates = 0;
4119 
4120     } else {
4121         mainloop_timer_start(refresh_timer);
4122     }
4123 }
4124 
4125 gboolean
4126 mon_refresh_display(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
4127 {
4128     xmlNode *cib_copy = copy_xml(current_cib);
4129     pe_working_set_t data_set;
4130 
4131     last_refresh = time(NULL);
4132 
4133     if (cli_config_update(&cib_copy, NULL, FALSE) == FALSE) {
4134         if (cib) {
4135             cib->cmds->signoff(cib);
4136         }
4137         print_as("Upgrade failed: %s", pcmk_strerror(-pcmk_err_schema_validation));
4138         if (output_format == mon_output_console) {
4139             sleep(2);
4140         }
4141         clean_up(EX_USAGE);
4142         return FALSE;
4143     }
4144 
4145     set_working_set_defaults(&data_set);
4146     data_set.input = cib_copy;
4147     cluster_status(&data_set);
4148 
4149     /* Unpack constraints if any section will need them
4150      * (tickets may be referenced in constraints but not granted yet,
4151      * and bans need negative location constraints) */
4152     if (show & (mon_show_bans | mon_show_tickets)) {
4153         xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input);
4154         unpack_constraints(cib_constraints, &data_set);
4155     }
4156 
4157     switch (output_format) {
4158         case mon_output_html:
4159         case mon_output_cgi:
4160             if (print_html_status(&data_set, output_filename) != 0) {
4161                 fprintf(stderr, "Critical: Unable to output html file\n");
4162                 clean_up(EX_USAGE);
4163             }
4164             break;
4165 
4166         case mon_output_xml:
4167             print_xml_status(&data_set);
4168             break;
4169 
4170         case mon_output_monitor:
4171             print_simple_status(&data_set);
4172             if (has_warnings) {
4173                 clean_up(MON_STATUS_WARN);
4174             }
4175             break;
4176 
4177         case mon_output_plain:
4178         case mon_output_console:
4179             print_status(&data_set);
4180             break;
4181 
4182         case mon_output_none:
4183             break;
4184     }
4185 
4186     cleanup_alloc_calculations(&data_set);
4187     return TRUE;
4188 }
4189 
4190 void
4191 mon_st_callback(stonith_t * st, stonith_event_t * e)
     /* [previous][next][first][last][top][bottom][index][help] */
4192 {
4193     char *desc = crm_strdup_printf("Operation %s requested by %s for peer %s: %s (ref=%s)",
4194                                  e->operation, e->origin, e->target, pcmk_strerror(e->result),
4195                                  e->id);
4196 
4197     if (snmp_target) {
4198         send_snmp_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4199     }
4200     if (crm_mail_to) {
4201         send_smtp_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4202     }
4203     if (external_agent) {
4204         send_custom_trap(e->target, NULL, e->operation, pcmk_ok, e->result, 0, desc);
4205     }
4206     free(desc);
4207 }
4208 
4209 /*
4210  * De-init ncurses, signoff from the CIB and deallocate memory.
4211  */
4212 void
4213 clean_up(int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
4214 {
4215 #if ENABLE_SNMP
4216     netsnmp_session *session = crm_snmp_init(NULL, NULL);
4217 
4218     if (session) {
4219         snmp_close(session);
4220         snmp_shutdown("snmpapp");
4221     }
4222 #endif
4223 
4224 #if CURSES_ENABLED
4225     if (output_format == mon_output_console) {
4226         output_format = mon_output_plain;
4227         echo();
4228         nocbreak();
4229         endwin();
4230     }
4231 #endif
4232 
4233     if (cib != NULL) {
4234         cib->cmds->signoff(cib);
4235         cib_delete(cib);
4236         cib = NULL;
4237     }
4238 
4239     free(output_filename);
4240     free(xml_file);
4241     free(pid_file);
4242 
4243     if (rc >= 0) {
4244         crm_exit(rc);
4245     }
4246     return;
4247 }

/* [previous][next][first][last][top][bottom][index][help] */