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