pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
output_log.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 <ctype.h>
14 #include <stdarg.h>
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 
19 typedef struct private_data_s {
20  /* gathered in log_begin_list */
21  GQueue/*<char*>*/ *prefixes;
22  uint8_t log_level;
23  const char *function;
24  const char *file;
25  uint32_t line;
26  uint32_t tags;
28 
37 #define logger(priv, fmt, args...) do { \
38  qb_log_from_external_source(pcmk__s((priv)->function, __func__), \
39  pcmk__s((priv)->file, __FILE__), fmt, (priv)->log_level, \
40  (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
41  ##args); \
42  } while (0);
43 
53 #define logger_va(priv, level, fmt, ap) do { \
54  qb_log_from_external_source_va(pcmk__s((priv)->function, __func__), \
55  pcmk__s((priv)->file, __FILE__), fmt, level, \
56  (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
57  ap); \
58  } while (0);
59 
60 static void
61 log_subprocess_output(pcmk__output_t *out, int exit_status,
62  const char *proc_stdout, const char *proc_stderr) {
63  /* This function intentionally left blank */
64 }
65 
66 static void
67 log_free_priv(pcmk__output_t *out) {
68  private_data_t *priv = NULL;
69 
70  if (out == NULL || out->priv == NULL) {
71  return;
72  }
73 
74  priv = out->priv;
75 
76  g_queue_free(priv->prefixes);
77  free(priv);
78  out->priv = NULL;
79 }
80 
81 static bool
82 log_init(pcmk__output_t *out) {
83  private_data_t *priv = NULL;
84 
85  pcmk__assert(out != NULL);
86 
87  /* If log_init was previously called on this output struct, just return. */
88  if (out->priv != NULL) {
89  return true;
90  }
91 
92  out->priv = calloc(1, sizeof(private_data_t));
93  if (out->priv == NULL) {
94  return false;
95  }
96 
97  priv = out->priv;
98 
99  priv->prefixes = g_queue_new();
100  priv->log_level = LOG_INFO;
101 
102  return true;
103 }
104 
105 static void
106 log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
107  /* This function intentionally left blank */
108 }
109 
110 static void
111 log_reset(pcmk__output_t *out) {
112  pcmk__assert(out != NULL);
113 
114  out->dest = freopen(NULL, "w", out->dest);
115  pcmk__assert(out->dest != NULL);
116 
117  log_free_priv(out);
118  log_init(out);
119 }
120 
121 static void
122 log_version(pcmk__output_t *out, bool extended) {
123  private_data_t *priv = NULL;
124 
125  pcmk__assert((out != NULL) && (out->priv != NULL));
126  priv = out->priv;
127 
128  if (extended) {
129  logger(priv, "Pacemaker %s (Build: %s): %s",
131  } else {
132  logger(priv, "Pacemaker " PACEMAKER_VERSION);
133  logger(priv, "Written by Andrew Beekhof and "
134  "the Pacemaker project contributors");
135  }
136 }
137 
138 G_GNUC_PRINTF(2, 3)
139 static void
140 log_err(pcmk__output_t *out, const char *format, ...)
141 {
142  va_list ap;
143  private_data_t *priv = NULL;
144 
145  pcmk__assert((out != NULL) && (out->priv != NULL));
146  priv = out->priv;
147 
148  /* Error output does not get indented, to separate it from other
149  * potentially indented list output.
150  */
151  va_start(ap, format);
152  logger_va(priv, LOG_ERR, format, ap);
153  va_end(ap);
154 }
155 
156 static void
157 log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
158  xmlNodePtr node = NULL;
159  private_data_t *priv = NULL;
160 
161  pcmk__assert((out != NULL) && (out->priv != NULL));
162  priv = out->priv;
163 
164  node = pcmk__xe_create(NULL, name);
165  pcmk__xe_set_content(node, "%s", buf);
166  do_crm_log_xml(priv->log_level, name, node);
167  free(node);
168 }
169 
170 G_GNUC_PRINTF(4, 5)
171 static void
172 log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
173  const char *format, ...) {
174  int len = 0;
175  va_list ap;
176  char* buffer = NULL;
177  private_data_t *priv = NULL;
178 
179  pcmk__assert((out != NULL) && (out->priv != NULL));
180  priv = out->priv;
181 
182  va_start(ap, format);
183  len = vasprintf(&buffer, format, ap);
184  pcmk__assert(len >= 0);
185  va_end(ap);
186 
187  /* Don't skip empty prefixes,
188  * otherwise there will be mismatch
189  * in the log_end_list */
190  if(strcmp(buffer, "") == 0) {
191  /* nothing */
192  }
193 
194  g_queue_push_tail(priv->prefixes, buffer);
195 }
196 
197 G_GNUC_PRINTF(3, 4)
198 static void
199 log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
200  int len = 0;
201  va_list ap;
202  private_data_t *priv = NULL;
203  char prefix[LINE_MAX] = { 0 };
204  int offset = 0;
205  char* buffer = NULL;
206 
207  pcmk__assert((out != NULL) && (out->priv != NULL));
208  priv = out->priv;
209 
210  for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
211  if (strcmp(prefix, "") != 0) {
212  offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
213  } else {
214  offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
215  }
216  }
217 
218  va_start(ap, format);
219  len = vasprintf(&buffer, format, ap);
220  pcmk__assert(len >= 0);
221  va_end(ap);
222 
223  if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
224  if ((name != NULL) && (strcmp(name, "") != 0)) {
225  if (strcmp(prefix, "") != 0) {
226  logger(priv, "%s: %s: %s", prefix, name, buffer);
227  } else {
228  logger(priv, "%s: %s", name, buffer);
229  }
230  } else {
231  if (strcmp(prefix, "") != 0) {
232  logger(priv, "%s: %s", prefix, buffer);
233  } else {
234  logger(priv, "%s", buffer);
235  }
236  }
237  }
238  free(buffer);
239 }
240 
241 static void
242 log_end_list(pcmk__output_t *out) {
243  private_data_t *priv = NULL;
244 
245  pcmk__assert((out != NULL) && (out->priv != NULL));
246  priv = out->priv;
247 
248  if (priv->prefixes == NULL) {
249  return;
250  }
251  pcmk__assert(priv->prefixes->tail != NULL);
252 
253  free((char *)priv->prefixes->tail->data);
254  g_queue_pop_tail(priv->prefixes);
255 }
256 
257 G_GNUC_PRINTF(2, 3)
258 static int
259 log_info(pcmk__output_t *out, const char *format, ...)
260 {
261  va_list ap;
262  private_data_t *priv = NULL;
263 
264  pcmk__assert((out != NULL) && (out->priv != NULL));
265  priv = out->priv;
266 
267  /* Informational output does not get indented, to separate it from other
268  * potentially indented list output.
269  */
270  va_start(ap, format);
271  logger_va(priv, priv->log_level, format, ap);
272  va_end(ap);
273 
274  return pcmk_rc_ok;
275 }
276 
277 G_GNUC_PRINTF(2, 3)
278 static int
279 log_transient(pcmk__output_t *out, const char *format, ...)
280 {
281  va_list ap;
282  private_data_t *priv = NULL;
283 
284  pcmk__assert((out != NULL) && (out->priv != NULL));
285  priv = out->priv;
286 
287  va_start(ap, format);
288  logger_va(priv, QB_MAX(priv->log_level, LOG_DEBUG), format, ap);
289  va_end(ap);
290 
291  return pcmk_rc_ok;
292 }
293 
294 static bool
295 log_is_quiet(pcmk__output_t *out) {
296  return false;
297 }
298 
299 static void
300 log_spacer(pcmk__output_t *out) {
301  /* This function intentionally left blank */
302 }
303 
304 static void
305 log_progress(pcmk__output_t *out, bool end) {
306  /* This function intentionally left blank */
307 }
308 
309 static void
310 log_prompt(const char *prompt, bool echo, char **dest) {
311  /* This function intentionally left blank */
312 }
313 
315 pcmk__mk_log_output(char **argv) {
316  pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
317 
318  if (retval == NULL) {
319  return NULL;
320  }
321 
322  retval->fmt_name = "log";
323  retval->request = pcmk__quote_cmdline(argv);
324 
325  retval->init = log_init;
326  retval->free_priv = log_free_priv;
327  retval->finish = log_finish;
328  retval->reset = log_reset;
329 
331  retval->message = pcmk__call_message;
332 
333  retval->subprocess_output = log_subprocess_output;
334  retval->version = log_version;
335  retval->info = log_info;
336  retval->transient = log_transient;
337  retval->err = log_err;
338  retval->output_xml = log_output_xml;
339 
340  retval->begin_list = log_begin_list;
341  retval->list_item = log_list_item;
342  retval->end_list = log_end_list;
343 
344  retval->is_quiet = log_is_quiet;
345  retval->spacer = log_spacer;
346  retval->progress = log_progress;
347  retval->prompt = log_prompt;
348 
349  return retval;
350 }
351 
362 uint8_t
364 {
365  pcmk__assert(out != NULL);
366 
367  if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
368  private_data_t *priv = out->priv;
369 
370  pcmk__assert(priv != NULL);
371  return priv->log_level;
372  }
373  return 0;
374 }
375 
389 void
391 {
392  pcmk__assert(out != NULL);
393 
394  if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
395  private_data_t *priv = out->priv;
396 
397  pcmk__assert(priv != NULL);
398  priv->log_level = log_level;
399  }
400 }
401 
418 void
420  const char *function, uint32_t line, uint32_t tags)
421 {
422  pcmk__assert(out != NULL);
423 
424  if (pcmk__str_eq(out->fmt_name, "log", pcmk__str_none)) {
425  private_data_t *priv = out->priv;
426 
427  pcmk__assert(priv != NULL);
428  priv->file = file;
429  priv->function = function;
430  priv->line = line;
431  priv->tags = tags;
432  }
433 }
void(* end_list)(pcmk__output_t *out)
const char * name
Definition: cib.c:26
int(* message)(pcmk__output_t *out, const char *message_id,...)
void pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level)
Definition: output_log.c:390
const char * fmt_name
The name of this output formatter.
bool(* is_quiet)(pcmk__output_t *out)
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
#define PACEMAKER_VERSION
Definition: config.h:517
void pcmk__output_set_log_filter(pcmk__output_t *out, const char *file, const char *function, uint32_t line, uint32_t tags)
Definition: output_log.c:419
#define logger(priv, fmt, args...)
Definition: output_log.c:37
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:174
void pcmk__xe_set_content(xmlNode *node, const char *format,...) G_GNUC_PRINTF(2
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:8
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)
#define do_crm_log_xml(level, text, xml)
Log XML line-by-line in a formatted fashion.
Definition: logging.h:268
int(*) int(* transient)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
FILE * dest
Where output should be written.
#define pcmk__assert(expr)
#define logger_va(priv, level, fmt, ap)
Definition: output_log.c:53
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
struct private_data_s private_data_t
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
void(* reset)(pcmk__output_t *out)
void(* progress)(pcmk__output_t *out, bool end)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
#define CRM_FEATURES
Definition: config.h:33
gchar * pcmk__quote_cmdline(gchar **argv)
Definition: cmdline.c:163
pcmk__output_t * pcmk__mk_log_output(char **argv)
Definition: output_log.c:315
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
uint8_t pcmk__output_get_log_level(const pcmk__output_t *out)
Definition: output_log.c:363