root/tools/crm_mon_curses.c

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

DEFINITIONS

This source file includes following definitions.
  1. curses_free_priv
  2. curses_init
  3. curses_finish
  4. curses_reset
  5. curses_subprocess_output
  6. curses_ver
  7. G_GNUC_PRINTF
  8. G_GNUC_PRINTF
  9. curses_output_xml
  10. G_GNUC_PRINTF
  11. G_GNUC_PRINTF
  12. curses_increment_list
  13. curses_end_list
  14. curses_is_quiet
  15. crm_mon_mk_curses_output
  16. G_GNUC_PRINTF
  17. G_GNUC_PRINTF
  18. PCMK__OUTPUT_ARGS
  19. PCMK__OUTPUT_ARGS
  20. crm_mon_register_messages
  21. crm_mon_mk_curses_output
  22. G_GNUC_PRINTF
  23. G_GNUC_PRINTF
  24. crm_mon_register_messages

   1 /*
   2  * Copyright 2019-2020 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <stdarg.h>
  12 #include <stdlib.h>
  13 #include <crm/crm.h>
  14 #include <crm/common/curses_internal.h>
  15 #include <crm/common/output_internal.h>
  16 #include <crm/stonith-ng.h>
  17 #include <crm/fencing/internal.h>
  18 #include <crm/pengine/internal.h>
  19 #include <glib.h>
  20 
  21 #include "crm_mon.h"
  22 
  23 #if CURSES_ENABLED
  24 
  25 GOptionEntry crm_mon_curses_output_entries[] = {
  26     { NULL }
  27 };
  28 
  29 typedef struct curses_list_data_s {
  30     unsigned int len;
  31     char *singular_noun;
  32     char *plural_noun;
  33 } curses_list_data_t;
  34 
  35 typedef struct private_data_s {
  36     GQueue *parent_q;
  37 } private_data_t;
  38 
  39 static void
  40 curses_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  41     private_data_t *priv = out->priv;
  42 
  43     if (priv == NULL) {
  44         return;
  45     }
  46 
  47     g_queue_free(priv->parent_q);
  48     free(priv);
  49     out->priv = NULL;
  50 }
  51 
  52 static bool
  53 curses_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  54     private_data_t *priv = NULL;
  55 
  56     /* If curses_init was previously called on this output struct, just return. */
  57     if (out->priv != NULL) {
  58         return true;
  59     } else {
  60         out->priv = calloc(1, sizeof(private_data_t));
  61         if (out->priv == NULL) {
  62             return false;
  63         }
  64 
  65         priv = out->priv;
  66     }
  67 
  68     priv->parent_q = g_queue_new();
  69 
  70     initscr();
  71     cbreak();
  72     noecho();
  73 
  74     return true;
  75 }
  76 
  77 static void
  78 curses_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
     /* [previous][next][first][last][top][bottom][index][help] */
  79     echo();
  80     nocbreak();
  81     endwin();
  82 }
  83 
  84 static void
  85 curses_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  86     CRM_ASSERT(out != NULL);
  87 
  88     curses_free_priv(out);
  89     curses_init(out);
  90 }
  91 
  92 static void
  93 curses_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
  94                          const char *proc_stdout, const char *proc_stderr) {
  95     if (proc_stdout != NULL) {
  96         printw("%s\n", proc_stdout);
  97     }
  98 
  99     if (proc_stderr != NULL) {
 100         printw("%s\n", proc_stderr);
 101     }
 102 
 103     clrtoeol();
 104     refresh();
 105 }
 106 
 107 /* curses_version is defined in curses.h, so we can't use that name here.
 108  * Note that this function prints out via text, not with curses.
 109  */
 110 static void
 111 curses_ver(pcmk__output_t *out, bool extended) {
     /* [previous][next][first][last][top][bottom][index][help] */
 112     if (extended) {
 113         printf("Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
 114     } else {
 115         printf("Pacemaker %s\n", PACEMAKER_VERSION);
 116         printf("Written by Andrew Beekhof\n");
 117     }
 118 }
 119 
 120 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 static void
 122 curses_error(pcmk__output_t *out, const char *format, ...) {
 123     va_list ap;
 124 
 125     /* Informational output does not get indented, to separate it from other
 126      * potentially indented list output.
 127      */
 128     va_start(ap, format);
 129     vw_printw(stdscr, format, ap);
 130     va_end(ap);
 131 
 132     /* Add a newline. */
 133     addch('\n');
 134 
 135     clrtoeol();
 136     refresh();
 137     sleep(2);
 138 }
 139 
 140 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 141 static void
 142 curses_info(pcmk__output_t *out, const char *format, ...) {
 143     va_list ap;
 144 
 145     /* Informational output does not get indented, to separate it from other
 146      * potentially indented list output.
 147      */
 148     va_start(ap, format);
 149     vw_printw(stdscr, format, ap);
 150     va_end(ap);
 151 
 152     /* Add a newline. */
 153     addch('\n');
 154 
 155     clrtoeol();
 156     refresh();
 157 }
 158 
 159 static void
 160 curses_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 161     private_data_t *priv = out->priv;
 162 
 163     CRM_ASSERT(priv != NULL);
 164     curses_indented_printf(out, "%s", buf);
 165 }
 166 
 167 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 static void
 169 curses_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
 170                   const char *format, ...) {
 171     private_data_t *priv = out->priv;
 172     curses_list_data_t *new_list = NULL;
 173     va_list ap;
 174 
 175     CRM_ASSERT(priv != NULL);
 176 
 177     va_start(ap, format);
 178 
 179     curses_indented_vprintf(out, format, ap);
 180     printw(":\n");
 181 
 182     va_end(ap);
 183 
 184     new_list = calloc(1, sizeof(curses_list_data_t));
 185     new_list->len = 0;
 186     new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun);
 187     new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun);
 188 
 189     g_queue_push_tail(priv->parent_q, new_list);
 190 }
 191 
 192 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 static void
 194 curses_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
 195     private_data_t *priv = out->priv;
 196     va_list ap;
 197 
 198     CRM_ASSERT(priv != NULL);
 199 
 200     va_start(ap, format);
 201 
 202     if (id != NULL) {
 203         curses_indented_printf(out, "%s: ", id);
 204         vw_printw(stdscr, format, ap);
 205     } else {
 206         curses_indented_vprintf(out, format, ap);
 207     }
 208 
 209     addch('\n');
 210     va_end(ap);
 211 
 212     out->increment_list(out);
 213 }
 214 
 215 static void
 216 curses_increment_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 217     private_data_t *priv = out->priv;
 218     gpointer tail;
 219 
 220     CRM_ASSERT(priv != NULL);
 221     tail = g_queue_peek_tail(priv->parent_q);
 222     CRM_ASSERT(tail != NULL);
 223     ((curses_list_data_t *) tail)->len++;
 224 }
 225 
 226 static void
 227 curses_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 228     private_data_t *priv = out->priv;
 229     curses_list_data_t *node = NULL;
 230 
 231     CRM_ASSERT(priv != NULL);
 232     node = g_queue_pop_tail(priv->parent_q);
 233 
 234     if (node->singular_noun != NULL && node->plural_noun != NULL) {
 235         if (node->len == 1) {
 236             curses_indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
 237         } else {
 238             curses_indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
 239         }
 240     }
 241 
 242     free(node);
 243 }
 244 
 245 static bool
 246 curses_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 247     return out->quiet;
 248 }
 249 
 250 pcmk__output_t *
 251 crm_mon_mk_curses_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 252     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
 253 
 254     if (retval == NULL) {
 255         return NULL;
 256     }
 257 
 258     retval->fmt_name = "console";
 259     retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
 260 
 261     retval->init = curses_init;
 262     retval->free_priv = curses_free_priv;
 263     retval->finish = curses_finish;
 264     retval->reset = curses_reset;
 265 
 266     retval->register_message = pcmk__register_message;
 267     retval->message = pcmk__call_message;
 268 
 269     retval->subprocess_output = curses_subprocess_output;
 270     retval->version = curses_ver;
 271     retval->err = curses_error;
 272     retval->info = curses_info;
 273     retval->output_xml = curses_output_xml;
 274 
 275     retval->begin_list = curses_begin_list;
 276     retval->list_item = curses_list_item;
 277     retval->increment_list = curses_increment_list;
 278     retval->end_list = curses_end_list;
 279 
 280     retval->is_quiet = curses_is_quiet;
 281 
 282     return retval;
 283 }
 284 
 285 G_GNUC_PRINTF(2, 0)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 void
 287 curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
 288     int level = 0;
 289     private_data_t *priv = out->priv;
 290 
 291     CRM_ASSERT(priv != NULL);
 292 
 293     level = g_queue_get_length(priv->parent_q);
 294 
 295     for (int i = 0; i < level; i++) {
 296         printw("  ");
 297     }
 298 
 299     if (level > 0) {
 300         printw("* ");
 301     }
 302 
 303     vw_printw(stdscr, format, args);
 304 
 305     clrtoeol();
 306     refresh();
 307 }
 308 
 309 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 void
 311 curses_indented_printf(pcmk__output_t *out, const char *format, ...) {
 312     va_list ap;
 313 
 314     va_start(ap, format);
 315     curses_indented_vprintf(out, format, ap);
 316     va_end(ap);
 317 }
 318 
 319 PCMK__OUTPUT_ARGS("stonith-event", "stonith_history_t *", "gboolean", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
 320 static int
 321 stonith_event_console(pcmk__output_t *out, va_list args) {
 322     stonith_history_t *event = va_arg(args, stonith_history_t *);
 323     gboolean full_history = va_arg(args, gboolean);
 324     gboolean later_succeeded = va_arg(args, gboolean);
 325 
 326     crm_time_t *crm_when = crm_time_new(NULL);
 327     char *buf = NULL;
 328 
 329     crm_time_set_timet(crm_when, &(event->completed));
 330     buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
 331 
 332     switch (event->state) {
 333         case st_failed:
 334             curses_indented_printf(out, "%s of %s failed: delegate=%s, client=%s, origin=%s, %s='%s'%s\n",
 335                                    stonith_action_str(event->action), event->target,
 336                                    event->delegate ? event->delegate : "",
 337                                    event->client, event->origin,
 338                                    full_history ? "completed" : "last-failed", buf,
 339                                    later_succeeded ? " (a later attempt succeeded)" : "");
 340             break;
 341 
 342         case st_done:
 343             curses_indented_printf(out, "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'\n",
 344                                    stonith_action_str(event->action), event->target,
 345                                    event->delegate ? event->delegate : "",
 346                                    event->client, event->origin,
 347                                    full_history ? "completed" : "last-successful", buf);
 348             break;
 349 
 350         default:
 351             curses_indented_printf(out, "%s of %s pending: client=%s, origin=%s\n",
 352                                    stonith_action_str(event->action), event->target,
 353                                    event->client, event->origin);
 354             break;
 355     }
 356 
 357     free(buf);
 358     crm_time_free(crm_when);
 359     return pcmk_rc_ok;
 360 }
 361 
 362 PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long")
     /* [previous][next][first][last][top][bottom][index][help] */
 363 static int
 364 cluster_maint_mode_console(pcmk__output_t *out, va_list args) {
 365     unsigned long long flags = va_arg(args, unsigned long long);
 366     int rc;
 367 
 368     if (pcmk_is_set(flags, pe_flag_maintenance_mode)) {
 369         printw("\n              *** Resource management is DISABLED ***");
 370         printw("\n  The cluster will not attempt to start, stop or recover services");
 371         printw("\n");
 372         rc = pcmk_rc_ok;
 373     } else if (pcmk_is_set(flags, pe_flag_stop_everything)) {
 374         printw("\n    *** Resource management is DISABLED ***");
 375         printw("\n  The cluster will keep all resources stopped");
 376         printw("\n");
 377         rc = pcmk_rc_ok;
 378     } else {
 379         rc = pcmk_rc_no_output;
 380     }
 381 
 382     clrtoeol();
 383     refresh();
 384     return rc;
 385 }
 386 
 387 static pcmk__message_entry_t fmt_functions[] = {
 388     { "ban", "console", pe__ban_text },
 389     { "bundle", "console", pe__bundle_text },
 390     { "clone", "console", pe__clone_text },
 391     { "cluster-counts", "console", pe__cluster_counts_text },
 392     { "cluster-dc", "console", pe__cluster_dc_text },
 393     { "cluster-options", "console", pe__cluster_options_text },
 394     { "cluster-stack", "console", pe__cluster_stack_text },
 395     { "cluster-summary", "console", pe__cluster_summary },
 396     { "cluster-times", "console", pe__cluster_times_text },
 397     { "failed-action", "console", pe__failed_action_text },
 398     { "failed-fencing-history", "console", stonith__failed_history },
 399     { "fencing-history", "console", stonith__history },
 400     { "full-fencing-history", "console", stonith__full_history },
 401     { "group", "console", pe__group_text },
 402     { "maint-mode", "console", cluster_maint_mode_console },
 403     { "node", "console", pe__node_text },
 404     { "node-attribute", "console", pe__node_attribute_text },
 405     { "node-list", "console", pe__node_list_text },
 406     { "op-history", "console", pe__op_history_text },
 407     { "pending-fencing-actions", "console", stonith__pending_actions },
 408     { "primitive", "console", pe__resource_text },
 409     { "resource-history", "console", pe__resource_history_text },
 410     { "stonith-event", "console", stonith_event_console },
 411     { "ticket", "console", pe__ticket_text },
 412 
 413     { NULL, NULL, NULL }
 414 };
 415 
 416 void
 417 crm_mon_register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 418     pcmk__register_messages(out, fmt_functions);
 419 }
 420 
 421 #else
 422 
 423 pcmk__output_t *
 424 crm_mon_mk_curses_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 425     /* curses was disabled in the build, so fall back to text. */
 426     return pcmk__mk_text_output(argv);
 427 }
 428 
 429 G_GNUC_PRINTF(2, 0)
     /* [previous][next][first][last][top][bottom][index][help] */
 430 void
 431 curses_indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
 432     return;
 433 }
 434 
 435 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 void
 437 curses_indented_printf(pcmk__output_t *out, const char *format, ...) {
 438     return;
 439 }
 440 
 441 void
 442 crm_mon_register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 443     return;
 444 }
 445 
 446 #endif

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