pacemaker  2.0.4-2deceaa3ae
Scalable High-Availability cluster resource manager
output_text.c
Go to the documentation of this file.
1 /*
2  * Copyright 2019 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 <stdarg.h>
11 #include <stdlib.h>
12 #include <crm/crm.h>
13 #include <crm/common/output.h>
14 #include <glib.h>
15 
16 static gboolean fancy = FALSE;
17 
18 GOptionEntry pcmk__text_output_entries[] = {
19  { "text-fancy", 0, 0, G_OPTION_ARG_NONE, &fancy,
20  "Use more highly formatted output (requires --output-as=text)",
21  NULL },
22 
23  { NULL }
24 };
25 
26 typedef struct text_list_data_s {
27  unsigned int len;
28  char *singular_noun;
29  char *plural_noun;
31 
32 typedef struct private_data_s {
33  GQueue *parent_q;
35 
36 static void
37 text_free_priv(pcmk__output_t *out) {
38  private_data_t *priv = out->priv;
39 
40  if (priv == NULL) {
41  return;
42  }
43 
44  g_queue_free(priv->parent_q);
45  free(priv);
46  out->priv = NULL;
47 }
48 
49 static bool
50 text_init(pcmk__output_t *out) {
51  private_data_t *priv = NULL;
52 
53  /* If text_init was previously called on this output struct, just return. */
54  if (out->priv != NULL) {
55  return true;
56  } else {
57  out->priv = calloc(1, sizeof(private_data_t));
58  if (out->priv == NULL) {
59  return false;
60  }
61 
62  priv = out->priv;
63  }
64 
65  priv->parent_q = g_queue_new();
66  return true;
67 }
68 
69 static void
70 text_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
71  /* This function intentionally left blank */
72 }
73 
74 static void
75 text_reset(pcmk__output_t *out) {
76  CRM_ASSERT(out != NULL);
77 
78  out->dest = freopen(NULL, "w", out->dest);
79  CRM_ASSERT(out->dest != NULL);
80 
81  text_free_priv(out);
82  text_init(out);
83 }
84 
85 static void
86 text_subprocess_output(pcmk__output_t *out, int exit_status,
87  const char *proc_stdout, const char *proc_stderr) {
88  if (proc_stdout != NULL) {
89  fprintf(out->dest, "%s\n", proc_stdout);
90  }
91 
92  if (proc_stderr != NULL) {
93  fprintf(out->dest, "%s\n", proc_stderr);
94  }
95 }
96 
97 static void
98 text_version(pcmk__output_t *out, bool extended) {
99  if (extended) {
100  fprintf(out->dest, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
101  } else {
102  fprintf(out->dest, "Pacemaker %s\n", PACEMAKER_VERSION);
103  fprintf(out->dest, "Written by Andrew Beekhof\n");
104  }
105 }
106 
107 G_GNUC_PRINTF(2, 3)
108 static void
109 text_err(pcmk__output_t *out, const char *format, ...) {
110  va_list ap;
111  int len = 0;
112 
113  va_start(ap, format);
114 
115  /* Informational output does not get indented, to separate it from other
116  * potentially indented list output.
117  */
118  len = vfprintf(stderr, format, ap);
119  CRM_ASSERT(len >= 0);
120  va_end(ap);
121 
122  /* Add a newline. */
123  fprintf(stderr, "\n");
124 }
125 
126 G_GNUC_PRINTF(2, 3)
127 static void
128 text_info(pcmk__output_t *out, const char *format, ...) {
129  va_list ap;
130  int len = 0;
131 
132  va_start(ap, format);
133 
134  /* Informational output does not get indented, to separate it from other
135  * potentially indented list output.
136  */
137  len = vfprintf(out->dest, format, ap);
138  CRM_ASSERT(len >= 0);
139  va_end(ap);
140 
141  /* Add a newline. */
142  fprintf(out->dest, "\n");
143 }
144 
145 static void
146 text_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
147  private_data_t *priv = out->priv;
148 
149  CRM_ASSERT(priv != NULL);
150  pcmk__indented_printf(out, "%s", buf);
151 }
152 
153 G_GNUC_PRINTF(4, 5)
154 static void
155 text_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
156  const char *format, ...) {
157  private_data_t *priv = out->priv;
158  text_list_data_t *new_list = NULL;
159  va_list ap;
160 
161  CRM_ASSERT(priv != NULL);
162 
163  va_start(ap, format);
164 
165  if (fancy && format) {
166  pcmk__indented_vprintf(out, format, ap);
167  fprintf(out->dest, ":\n");
168  }
169 
170  va_end(ap);
171 
172  new_list = calloc(1, sizeof(text_list_data_t));
173  new_list->len = 0;
174  new_list->singular_noun = singular_noun == NULL ? NULL : strdup(singular_noun);
175  new_list->plural_noun = plural_noun == NULL ? NULL : strdup(plural_noun);
176 
177  g_queue_push_tail(priv->parent_q, new_list);
178 }
179 
180 G_GNUC_PRINTF(3, 4)
181 static void
182 text_list_item(pcmk__output_t *out, const char *id, const char *format, ...) {
183  private_data_t *priv = out->priv;
184  va_list ap;
185 
186  CRM_ASSERT(priv != NULL);
187 
188  va_start(ap, format);
189 
190  if (fancy) {
191  if (id != NULL) {
192  /* Not really a good way to do this all in one call, so make it two.
193  * The first handles the indentation and list styling. The second
194  * just prints right after that one.
195  */
196  pcmk__indented_printf(out, "%s: ", id);
197  vfprintf(out->dest, format, ap);
198  } else {
199  pcmk__indented_vprintf(out, format, ap);
200  }
201  } else {
202  pcmk__indented_vprintf(out, format, ap);
203  }
204 
205  fputc('\n', out->dest);
206  va_end(ap);
207 
208  out->increment_list(out);
209 }
210 
211 static void
212 text_increment_list(pcmk__output_t *out) {
213  private_data_t *priv = out->priv;
214  gpointer tail;
215 
216  CRM_ASSERT(priv != NULL);
217  tail = g_queue_peek_tail(priv->parent_q);
218  CRM_ASSERT(tail != NULL);
219  ((text_list_data_t *) tail)->len++;
220 }
221 
222 static void
223 text_end_list(pcmk__output_t *out) {
224  private_data_t *priv = out->priv;
225  text_list_data_t *node = NULL;
226 
227  CRM_ASSERT(priv != NULL);
228  node = g_queue_pop_tail(priv->parent_q);
229 
230  if (node->singular_noun != NULL && node->plural_noun != NULL) {
231  if (node->len == 1) {
232  pcmk__indented_printf(out, "%d %s found\n", node->len, node->singular_noun);
233  } else {
234  pcmk__indented_printf(out, "%d %s found\n", node->len, node->plural_noun);
235  }
236  }
237 
238  free(node);
239 }
240 
242 pcmk__mk_text_output(char **argv) {
243  pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
244 
245  if (retval == NULL) {
246  return NULL;
247  }
248 
249  retval->fmt_name = "text";
250  retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
251  retval->supports_quiet = true;
252 
253  retval->init = text_init;
254  retval->free_priv = text_free_priv;
255  retval->finish = text_finish;
256  retval->reset = text_reset;
257 
259  retval->message = pcmk__call_message;
260 
261  retval->subprocess_output = text_subprocess_output;
262  retval->version = text_version;
263  retval->info = text_info;
264  retval->err = text_err;
265  retval->output_xml = text_output_xml;
266 
267  retval->begin_list = text_begin_list;
268  retval->list_item = text_list_item;
269  retval->increment_list = text_increment_list;
270  retval->end_list = text_end_list;
271 
272  return retval;
273 }
274 
275 G_GNUC_PRINTF(2, 0)
276 void
277 pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args) {
278  int len = 0;
279 
280  if (fancy) {
281  int level = 0;
282  private_data_t *priv = out->priv;
283 
284  CRM_ASSERT(priv != NULL);
285 
286  level = g_queue_get_length(priv->parent_q);
287 
288  for (int i = 0; i < level; i++) {
289  fprintf(out->dest, " ");
290  }
291 
292  if (level > 0) {
293  fprintf(out->dest, "* ");
294  }
295  }
296 
297  len = vfprintf(out->dest, format, args);
298  CRM_ASSERT(len >= 0);
299 }
300 
301 G_GNUC_PRINTF(2, 3)
302 void
303 pcmk__indented_printf(pcmk__output_t *out, const char *format, ...) {
304  va_list ap;
305 
306  va_start(ap, format);
307  pcmk__indented_vprintf(out, format, ap);
308  va_end(ap);
309 }
pcmk__indented_vprintf
void pcmk__indented_vprintf(pcmk__output_t *out, const char *format, va_list args)
Definition: output_text.c:277
pcmk__output_s::finish
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
Definition: output.h:262
pcmk__output_s::init
bool(* init)(pcmk__output_t *out)
Definition: output.h:215
pcmk__output_s::output_xml
void(*) void(*) void(* output_xml)(pcmk__output_t *out, const char *name, const char *buf)
Definition: output.h:371
pcmk__output_s::free_priv
void(* free_priv)(pcmk__output_t *out)
Definition: output.h:226
BUILD_VERSION
#define BUILD_VERSION
Definition: config.h:8
pcmk__output_s::subprocess_output
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
Definition: output.h:322
pcmk__register_message
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:134
pcmk__output_s::priv
void * priv
Implementation-specific private data.
Definition: output.h:199
pcmk__output_s::version
void(* version)(pcmk__output_t *out, bool extended)
Definition: output.h:333
pcmk__output_s::list_item
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
Definition: output.h:405
pcmk__output_s::message
int(* message)(pcmk__output_t *out, const char *message_id,...)
Definition: output.h:311
private_data_t
struct private_data_s private_data_t
pcmk__call_message
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:116
text_list_data_t
struct text_list_data_s text_list_data_t
pcmk__output_s::err
void(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:361
pcmk__output_s::end_list
void(* end_list)(pcmk__output_t *out)
Definition: output.h:432
pcmk__output_s::increment_list
void(*) void(*) void(* increment_list)(pcmk__output_t *out)
Definition: output.h:420
CRM_FEATURES
#define CRM_FEATURES
Definition: config.h:35
pcmk__output_s::begin_list
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
Definition: output.h:392
pcmk__text_output_entries
GOptionEntry pcmk__text_output_entries[]
Definition: output_text.c:18
pcmk__output_s::reset
void(* reset)(pcmk__output_t *out)
Definition: output.h:280
pcmk__indented_printf
void pcmk__indented_printf(pcmk__output_t *out, const char *format,...)
Definition: output_text.c:303
pcmk__mk_text_output
pcmk__output_t * pcmk__mk_text_output(char **argv)
Definition: output_text.c:242
private_data_t
struct private_data_s private_data_t
pcmk__output_s
This structure contains everything that makes up a single output formatter.
Definition: output.h:153
pcmk__output_s::supports_quiet
bool supports_quiet
Does this formatter support a special quiet mode?
Definition: output.h:174
pcmk__output_s::fmt_name
const char * fmt_name
The name of this output formatter.
Definition: output.h:157
crm_exit_t
enum crm_exit_e crm_exit_t
pcmk__output_s::dest
FILE * dest
Where output should be written.
Definition: output.h:182
CRM_ASSERT
#define CRM_ASSERT(expr)
Definition: results.h:42
pcmk__output_s::info
void(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:347
PACEMAKER_VERSION
#define PACEMAKER_VERSION
Definition: config.h:511
name
char * name
Definition: pcmk_fence.c:30
crm.h
A dumping ground.
pcmk__output_s::request
gchar * request
A copy of the request that generated this output.
Definition: output.h:165
output.h
Formatted output for pacemaker tools.
pcmk__output_s::register_message
void(* register_message)(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.h:293