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