root/lib/common/output_text.c

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

DEFINITIONS

This source file includes following definitions.
  1. free_list_data
  2. text_free_priv
  3. text_init
  4. text_finish
  5. text_reset
  6. text_subprocess_output
  7. text_version
  8. G_GNUC_PRINTF
  9. G_GNUC_PRINTF
  10. G_GNUC_PRINTF
  11. text_output_xml
  12. G_GNUC_PRINTF
  13. G_GNUC_PRINTF
  14. text_increment_list
  15. text_end_list
  16. text_is_quiet
  17. text_spacer
  18. text_progress
  19. pcmk__mk_text_output
  20. pcmk__output_text_get_fancy
  21. pcmk__output_text_set_fancy
  22. G_GNUC_PRINTF
  23. G_GNUC_PRINTF
  24. G_GNUC_PRINTF
  25. G_GNUC_PRINTF
  26. pcmk__text_prompt

   1 /*
   2  * Copyright 2019-2025 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 <crm/common/cmdline_internal.h>
  12 
  13 #include <stdarg.h>
  14 #include <stdlib.h>
  15 #include <glib.h>
  16 #include <termios.h>
  17 
  18 #include "crmcommon_private.h"
  19 
  20 typedef struct text_list_data_s {
  21     unsigned int len;
  22     char *singular_noun;
  23     char *plural_noun;
  24 } text_list_data_t;
  25 
  26 typedef struct private_data_s {
  27     GQueue *parent_q;
  28     bool fancy;
  29 } private_data_t;
  30 
  31 static void
  32 free_list_data(gpointer data) {
     /* [previous][next][first][last][top][bottom][index][help] */
  33     text_list_data_t *list_data = data;
  34 
  35     free(list_data->singular_noun);
  36     free(list_data->plural_noun);
  37     free(list_data);
  38 }
  39 
  40 static void
  41 text_free_priv(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  42     private_data_t *priv = NULL;
  43 
  44     if (out == NULL || out->priv == NULL) {
  45         return;
  46     }
  47 
  48     priv = out->priv;
  49 
  50     g_queue_free_full(priv->parent_q, free_list_data);
  51     free(priv);
  52     out->priv = NULL;
  53 }
  54 
  55 static bool
  56 text_init(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  57     private_data_t *priv = NULL;
  58 
  59     pcmk__assert(out != NULL);
  60 
  61     /* If text_init was previously called on this output struct, just return. */
  62     if (out->priv != NULL) {
  63         return true;
  64     }
  65 
  66     out->priv = calloc(1, sizeof(private_data_t));
  67     if (out->priv == NULL) {
  68         return false;
  69     }
  70 
  71     priv = out->priv;
  72     priv->parent_q = g_queue_new();
  73     return true;
  74 }
  75 
  76 static void
  77 text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
     /* [previous][next][first][last][top][bottom][index][help] */
  78 {
  79     pcmk__assert((out != NULL) && (out->dest != NULL));
  80     fflush(out->dest);
  81 }
  82 
  83 static void
  84 text_reset(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
  85     private_data_t *priv = NULL;
  86     bool old_fancy = false;
  87 
  88     pcmk__assert(out != NULL);
  89 
  90     if (out->dest != stdout) {
  91         out->dest = freopen(NULL, "w", out->dest);
  92     }
  93 
  94     pcmk__assert(out->dest != NULL);
  95 
  96     // Save priv->fancy before free/init sequence overwrites it
  97     priv = out->priv;
  98     old_fancy = priv->fancy;
  99 
 100     text_free_priv(out);
 101     text_init(out);
 102 
 103     priv = out->priv;
 104     priv->fancy = old_fancy;
 105 }
 106 
 107 static void
 108 text_subprocess_output(pcmk__output_t *out, int exit_status,
     /* [previous][next][first][last][top][bottom][index][help] */
 109                        const char *proc_stdout, const char *proc_stderr) {
 110     pcmk__assert(out != NULL);
 111 
 112     if (proc_stdout != NULL) {
 113         fprintf(out->dest, "%s\n", proc_stdout);
 114     }
 115 
 116     if (proc_stderr != NULL) {
 117         fprintf(out->dest, "%s\n", proc_stderr);
 118     }
 119 }
 120 
 121 static void
 122 text_version(pcmk__output_t *out, bool extended)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     pcmk__assert((out != NULL) && (out->dest != NULL));
 125 
 126     if (extended) {
 127         fprintf(out->dest, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
 128     } else {
 129         fprintf(out->dest, "Pacemaker %s\n", PACEMAKER_VERSION);
 130         fprintf(out->dest, "Written by Andrew Beekhof and "
 131                            "the Pacemaker project contributors\n");
 132     }
 133 }
 134 
 135 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 static void
 137 text_err(pcmk__output_t *out, const char *format, ...) {
 138     va_list ap;
 139 
 140     pcmk__assert(out != NULL);
 141 
 142     va_start(ap, format);
 143 
 144     /* Informational output does not get indented, to separate it from other
 145      * potentially indented list output.
 146      */
 147     vfprintf(stderr, format, ap);
 148     va_end(ap);
 149 
 150     /* Add a newline. */
 151     fprintf(stderr, "\n");
 152 }
 153 
 154 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 static int
 156 text_info(pcmk__output_t *out, const char *format, ...) {
 157     va_list ap;
 158 
 159     pcmk__assert(out != NULL);
 160 
 161     if (out->is_quiet(out)) {
 162         return pcmk_rc_no_output;
 163     }
 164 
 165     va_start(ap, format);
 166 
 167     /* Informational output does not get indented, to separate it from other
 168      * potentially indented list output.
 169      */
 170     vfprintf(out->dest, format, ap);
 171     va_end(ap);
 172 
 173     /* Add a newline. */
 174     fprintf(out->dest, "\n");
 175     return pcmk_rc_ok;
 176 }
 177 
 178 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 static int
 180 text_transient(pcmk__output_t *out, const char *format, ...)
 181 {
 182     return pcmk_rc_no_output;
 183 }
 184 
 185 static void
 186 text_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 187     pcmk__assert(out != NULL);
 188     pcmk__indented_printf(out, "%s", buf);
 189 }
 190 
 191 G_GNUC_PRINTF(4, 5)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 static void
 193 text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
 194                 const char *format, ...) {
 195     private_data_t *priv = NULL;
 196     text_list_data_t *new_list = NULL;
 197     va_list ap;
 198 
 199     pcmk__assert((out != NULL) && (out->priv != NULL));
 200     priv = out->priv;
 201 
 202     va_start(ap, format);
 203 
 204     if (priv->fancy && (format != NULL)) {
 205         pcmk__indented_vprintf(out, format, ap);
 206         fprintf(out->dest, ":\n");
 207     }
 208 
 209     va_end(ap);
 210 
 211     new_list = pcmk__assert_alloc(1, sizeof(text_list_data_t));
 212     new_list->len = 0;
 213     new_list->singular_noun = pcmk__str_copy(singular_noun);
 214     new_list->plural_noun = pcmk__str_copy(plural_noun);
 215 
 216     g_queue_push_tail(priv->parent_q, new_list);
 217 }
 218 
 219 G_GNUC_PRINTF(3, 4)
     /* [previous][next][first][last][top][bottom][index][help] */
 220 static void
 221 text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
 222     private_data_t *priv = NULL;
 223     va_list ap;
 224 
 225     pcmk__assert(out != NULL);
 226 
 227     priv = out->priv;
 228     va_start(ap, format);
 229 
 230     if (priv->fancy) {
 231         if (id != NULL) {
 232             /* Not really a good way to do this all in one call, so make it two.
 233              * The first handles the indentation and list styling.  The second
 234              * just prints right after that one.
 235              */
 236             pcmk__indented_printf(out, "%s: ", id);
 237             vfprintf(out->dest, format, ap);
 238         } else {
 239             pcmk__indented_vprintf(out, format, ap);
 240         }
 241     } else {
 242         pcmk__indented_vprintf(out, format, ap);
 243     }
 244 
 245     fputc('\n', out->dest);
 246     fflush(out->dest);
 247     va_end(ap);
 248 
 249     out->increment_list(out);
 250 }
 251 
 252 static void
 253 text_increment_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 254     private_data_t *priv = NULL;
 255     gpointer tail;
 256 
 257     pcmk__assert((out != NULL) && (out->priv != NULL));
 258     priv = out->priv;
 259 
 260     tail = g_queue_peek_tail(priv->parent_q);
 261     pcmk__assert(tail != NULL);
 262     ((text_list_data_t *) tail)->len++;
 263 }
 264 
 265 static void
 266 text_end_list(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 267     private_data_t *priv = NULL;
 268     text_list_data_t *node = NULL;
 269 
 270     pcmk__assert((out != NULL) && (out->priv != NULL));
 271     priv = out->priv;
 272 
 273     node = g_queue_pop_tail(priv->parent_q);
 274 
 275     if (node->singular_noun != NULL && node->plural_noun != NULL) {
 276         if (node->len == 1) {
 277             pcmk__indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
 278         } else {
 279             pcmk__indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
 280         }
 281     }
 282 
 283     free_list_data(node);
 284 }
 285 
 286 static bool
 287 text_is_quiet(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 288     pcmk__assert(out != NULL);
 289     return out->quiet;
 290 }
 291 
 292 static void
 293 text_spacer(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 294     pcmk__assert(out != NULL);
 295     fprintf(out->dest, "\n");
 296 }
 297 
 298 static void
 299 text_progress(pcmk__output_t *out, bool end) {
     /* [previous][next][first][last][top][bottom][index][help] */
 300     pcmk__assert(out != NULL);
 301 
 302     if (out->dest == stdout) {
 303         fprintf(out->dest, ".");
 304 
 305         if (end) {
 306             fprintf(out->dest, "\n");
 307         }
 308     }
 309 }
 310 
 311 pcmk__output_t *
 312 pcmk__mk_text_output(char **argv) {
     /* [previous][next][first][last][top][bottom][index][help] */
 313     pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
 314 
 315     if (retval == NULL) {
 316         return NULL;
 317     }
 318 
 319     retval->fmt_name = "text";
 320     retval->request = pcmk__quote_cmdline(argv);
 321 
 322     retval->init = text_init;
 323     retval->free_priv = text_free_priv;
 324     retval->finish = text_finish;
 325     retval->reset = text_reset;
 326 
 327     retval->register_message = pcmk__register_message;
 328     retval->message = pcmk__call_message;
 329 
 330     retval->subprocess_output = text_subprocess_output;
 331     retval->version = text_version;
 332     retval->info = text_info;
 333     retval->transient = text_transient;
 334     retval->err = text_err;
 335     retval->output_xml = text_output_xml;
 336 
 337     retval->begin_list = text_begin_list;
 338     retval->list_item = text_list_item;
 339     retval->increment_list = text_increment_list;
 340     retval->end_list = text_end_list;
 341 
 342     retval->is_quiet = text_is_quiet;
 343     retval->spacer = text_spacer;
 344     retval->progress = text_progress;
 345     retval->prompt = pcmk__text_prompt;
 346 
 347     return retval;
 348 }
 349 
 350 /*!
 351  * \internal
 352  * \brief Check whether fancy output is enabled for a text output object
 353  *
 354  * This returns \c false if the output object is not of text format.
 355  *
 356  * \param[in] out  Output object
 357  *
 358  * \return \c true if \p out has fancy output enabled, or \c false otherwise
 359  */
 360 bool
 361 pcmk__output_text_get_fancy(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     pcmk__assert(out != NULL);
 364 
 365     if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) {
 366         private_data_t *priv = out->priv;
 367 
 368         pcmk__assert(priv != NULL);
 369         return priv->fancy;
 370     }
 371     return false;
 372 }
 373 
 374 /*!
 375  * \internal
 376  * \brief Enable or disable fancy output for a text output object
 377  *
 378  * This does nothing if the output object is not of text format.
 379  *
 380  * \param[in,out] out      Output object
 381  * \param[in]     enabled  Whether fancy output should be enabled for \p out
 382  */
 383 void
 384 pcmk__output_text_set_fancy(pcmk__output_t *out, bool enabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 385 {
 386     pcmk__assert(out != NULL);
 387 
 388     if (pcmk__str_eq(out->fmt_name, "text", pcmk__str_none)) {
 389         private_data_t *priv = out->priv;
 390 
 391         pcmk__assert(priv != NULL);
 392         priv->fancy = enabled;
 393     }
 394 }
 395 
 396 G_GNUC_PRINTF(2, 0)
     /* [previous][next][first][last][top][bottom][index][help] */
 397 void
 398 pcmk__formatted_vprintf(pcmk__output_t *out, const char *format, va_list args) {
 399     pcmk__assert(out != NULL);
 400     CRM_CHECK(pcmk__str_eq(out->fmt_name, "text", pcmk__str_none), return);
 401     vfprintf(out->dest, format, args);
 402 }
 403 
 404 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 405 void
 406 pcmk__formatted_printf(pcmk__output_t *out, const char *format, ...) {
 407     va_list ap;
 408 
 409     pcmk__assert(out != NULL);
 410 
 411     va_start(ap, format);
 412     pcmk__formatted_vprintf(out, format, ap);
 413     va_end(ap);
 414 }
 415 
 416 G_GNUC_PRINTF(2, 0)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 void
 418 pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
 419     private_data_t *priv = NULL;
 420 
 421     pcmk__assert(out != NULL);
 422     CRM_CHECK(pcmk__str_eq(out->fmt_name, "text", pcmk__str_none), return);
 423 
 424     priv = out->priv;
 425 
 426     if (priv->fancy) {
 427         int level = 0;
 428         private_data_t *priv = out->priv;
 429 
 430         pcmk__assert(priv != NULL);
 431 
 432         level = g_queue_get_length(priv->parent_q);
 433 
 434         for (int i = 0; i < level; i++) {
 435             fprintf(out->dest, "  ");
 436         }
 437 
 438         if (level > 0) {
 439             fprintf(out->dest, "* ");
 440         }
 441     }
 442 
 443     pcmk__formatted_vprintf(out, format, args);
 444 }
 445 
 446 G_GNUC_PRINTF(2, 3)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 void
 448 pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) {
 449     va_list ap;
 450 
 451     pcmk__assert(out != NULL);
 452 
 453     va_start(ap, format);
 454     pcmk__indented_vprintf(out, format, ap);
 455     va_end(ap);
 456 }
 457 
 458 void
 459 pcmk__text_prompt(const char *prompt, bool echo, char **dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 460 {
 461     int rc = 0;
 462     struct termios settings;
 463     tcflag_t orig_c_lflag = 0;
 464 
 465     pcmk__assert((prompt != NULL) && (dest != NULL));
 466 
 467     if (!echo) {
 468         rc = tcgetattr(0, &settings);
 469         if (rc == 0) {
 470             orig_c_lflag = settings.c_lflag;
 471             settings.c_lflag &= ~ECHO;
 472             rc = tcsetattr(0, TCSANOW, &settings);
 473         }
 474     }
 475 
 476     if (rc == 0) {
 477         fprintf(stderr, "%s: ", prompt);
 478 
 479         if (*dest != NULL) {
 480             free(*dest);
 481             *dest = NULL;
 482         }
 483 
 484 #if HAVE_SSCANF_M
 485         rc = scanf("%ms", dest);
 486 #else
 487         *dest = pcmk__assert_alloc(1, 1024);
 488         rc = scanf("%1023s", *dest);
 489 #endif
 490         fprintf(stderr, "\n");
 491     }
 492 
 493     if (rc < 1) {
 494         free(*dest);
 495         *dest = NULL;
 496     }
 497 
 498     if (orig_c_lflag != 0) {
 499         settings.c_lflag = orig_c_lflag;
 500         /* rc = */ tcsetattr(0, TCSANOW, &settings);
 501     }
 502 }

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