pacemaker  2.0.4-2deceaa3ae
Scalable High-Availability cluster resource manager
output_log.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 #ifndef _GNU_SOURCE
10 # define _GNU_SOURCE
11 #endif
12 
13 #include <ctype.h>
14 #include <libxml/HTMLtree.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <crm/crm.h>
19 #include <crm/common/output.h>
20 #include <crm/common/xml.h>
21 
22 GOptionEntry pcmk__log_output_entries[] = {
23  { NULL }
24 };
25 
26 typedef struct private_data_s {
27  /* gathered in log_begin_list */
28  GQueue/*<char*>*/ *prefixes;
30 
31 static void
32 log_subprocess_output(pcmk__output_t *out, int exit_status,
33  const char *proc_stdout, const char *proc_stderr) {
34  /* This function intentionally left blank */
35 }
36 
37 static void
38 log_free_priv(pcmk__output_t *out) {
39  private_data_t *priv = out->priv;
40 
41  if (priv == NULL) {
42  return;
43  }
44 
45  g_queue_free(priv->prefixes);
46  free(priv);
47  out->priv = NULL;
48 }
49 
50 static bool
51 log_init(pcmk__output_t *out) {
52 
53  /* If log_init was previously called on this output struct, just return. */
54  if (out->priv != NULL) {
55  return true;
56  }
57 
58  out->priv = calloc(1, sizeof(private_data_t));
59  if (out->priv == NULL) {
60  return false;
61  }
62  ((private_data_t *)out->priv)->prefixes = g_queue_new();
63  return true;
64 }
65 
66 static void
67 log_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) {
68  /* This function intentionally left blank */
69 }
70 
71 static void
72 log_reset(pcmk__output_t *out) {
73  CRM_ASSERT(out != NULL);
74 
75  out->dest = freopen(NULL, "w", out->dest);
76  CRM_ASSERT(out->dest != NULL);
77 
78  log_free_priv(out);
79  log_init(out);
80 }
81 
82 static void
83 log_version(pcmk__output_t *out, bool extended) {
84  if (extended) {
85  crm_info("Pacemaker %s (Build: %s): %s",
87  } else {
88  crm_info("Pacemaker %s", PACEMAKER_VERSION);
89  crm_info("Written by Andrew Beekhof");
90  }
91 }
92 
93 G_GNUC_PRINTF(2, 3)
94 static void
95 log_err(pcmk__output_t *out, const char *format, ...) {
96  va_list ap;
97  char* buffer = NULL;
98  int len = 0;
99 
100  va_start(ap, format);
101  /* Informational output does not get indented, to separate it from other
102  * potentially indented list output.
103  */
104  len = vasprintf(&buffer, format, ap);
105  CRM_ASSERT(len >= 0);
106  va_end(ap);
107 
108  crm_err("%s", buffer);
109 
110  free(buffer);
111 }
112 
113 static void
114 log_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
115  xmlNodePtr node = NULL;
116  private_data_t *priv = out->priv;
117  CRM_ASSERT(priv != NULL);
118 
119  node = create_xml_node(NULL, name);
120  xmlNodeSetContent(node, (pcmkXmlStr) buf);
121  crm_log_xml_info(node, name);
122  free(node);
123 }
124 
125 G_GNUC_PRINTF(4, 5)
126 static void
127 log_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun,
128  const char *format, ...) {
129  int len = 0;
130  va_list ap;
131  char* buffer = NULL;
132  private_data_t *priv = out->priv;
133  CRM_ASSERT(priv != NULL);
134 
135  va_start(ap, format);
136  len = vasprintf(&buffer, format, ap);
137  CRM_ASSERT(len >= 0);
138  va_end(ap);
139 
140  /* Don't skip empty prefixes,
141  * otherwise there will be mismatch
142  * in the log_end_list */
143  if(strcmp(buffer, "") == 0) {
144  /* nothing */
145  }
146 
147  g_queue_push_tail(priv->prefixes, buffer);
148 }
149 
150 G_GNUC_PRINTF(3, 4)
151 static void
152 log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
153  int len = 0;
154  va_list ap;
155  private_data_t *priv = out->priv;
156  char prefix[LINE_MAX] = { 0 };
157  int offset = 0;
158  char* buffer = NULL;
159 
160  CRM_ASSERT(priv != NULL);
161 
162  for (GList* gIter = priv->prefixes->head; gIter; gIter = gIter->next) {
163  if (strcmp(prefix, "") != 0) {
164  offset += snprintf(prefix + offset, LINE_MAX - offset, ": %s", (char *)gIter->data);
165  } else {
166  offset = snprintf(prefix, LINE_MAX, "%s", (char *)gIter->data);
167  }
168  }
169 
170  va_start(ap, format);
171  len = vasprintf(&buffer, format, ap);
172  CRM_ASSERT(len >= 0);
173  va_end(ap);
174 
175  if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
176  if ((name != NULL) && (strcmp(name, "") != 0)) {
177  if (strcmp(prefix, "") != 0) {
178  crm_info("%s: %s: %s", prefix, name, buffer);
179  } else {
180  crm_info("%s: %s", name, buffer);
181  }
182  } else {
183  if (strcmp(prefix, "") != 0) {
184  crm_info("%s: %s", prefix, buffer);
185  } else {
186  crm_info("%s", buffer);
187  }
188  }
189  }
190  free(buffer);
191 }
192 
193 static void
194 log_end_list(pcmk__output_t *out) {
195  private_data_t *priv = out->priv;
196  CRM_ASSERT(priv != NULL);
197  if (priv->prefixes == NULL) {
198  return;
199  }
200  CRM_ASSERT(priv->prefixes->tail != NULL);
201 
202  free((char *)priv->prefixes->tail->data);
203  g_queue_pop_tail(priv->prefixes);
204 }
205 
206 G_GNUC_PRINTF(2, 3)
207 static void
208 log_info(pcmk__output_t *out, const char *format, ...) {
209  int len = 0;
210  va_list ap;
211  char* buffer = NULL;
212 
213  va_start(ap, format);
214  len = vasprintf(&buffer, format, ap);
215  CRM_ASSERT(len >= 0);
216  va_end(ap);
217 
218  crm_info("%s", buffer);
219 
220  free(buffer);
221 }
222 
224 pcmk__mk_log_output(char **argv) {
225  pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t));
226 
227  if (retval == NULL) {
228  return NULL;
229  }
230 
231  retval->fmt_name = "log";
232  retval->request = argv == NULL ? NULL : g_strjoinv(" ", argv);
233  retval->supports_quiet = false;
234 
235  retval->init = log_init;
236  retval->free_priv = log_free_priv;
237  retval->finish = log_finish;
238  retval->reset = log_reset;
239 
241  retval->message = pcmk__call_message;
242 
243  retval->subprocess_output = log_subprocess_output;
244  retval->version = log_version;
245  retval->info = log_info;
246  retval->err = log_err;
247  retval->output_xml = log_output_xml;
248 
249  retval->begin_list = log_begin_list;
250  retval->list_item = log_list_item;
251  retval->end_list = log_end_list;
252 
253  return retval;
254 }
#define PACEMAKER_VERSION
Definition: config.h:511
#define CRM_FEATURES
Definition: config.h:35
#define BUILD_VERSION
Definition: config.h:8
A dumping ground.
#define crm_log_xml_info(xml, text)
Definition: logging.h:375
#define crm_info(fmt, args...)
Definition: logging.h:366
#define crm_err(fmt, args...)
Definition: logging.h:363
Formatted output for pacemaker tools.
int pcmk__call_message(pcmk__output_t *out, const char *message_id,...)
Definition: output.c:116
void pcmk__register_message(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.c:134
struct private_data_s private_data_t
GOptionEntry pcmk__log_output_entries[]
Definition: output_log.c:22
pcmk__output_t * pcmk__mk_log_output(char **argv)
Definition: output_log.c:224
struct private_data_s private_data_t
char * name
Definition: pcmk_fence.c:30
#define CRM_ASSERT(expr)
Definition: results.h:42
enum crm_exit_e crm_exit_t
This structure contains everything that makes up a single output formatter.
Definition: output.h:153
void(* end_list)(pcmk__output_t *out)
Definition: output.h:432
void(* version)(pcmk__output_t *out, bool extended)
Definition: output.h:333
int(* message)(pcmk__output_t *out, const char *message_id,...)
Definition: output.h:311
bool supports_quiet
Does this formatter support a special quiet mode?
Definition: output.h:174
const char * fmt_name
The name of this output formatter.
Definition: output.h:157
FILE * dest
Where output should be written.
Definition: output.h:182
void(* register_message)(pcmk__output_t *out, const char *message_id, pcmk__message_fn_t fn)
Definition: output.h:293
void(*) void(* list_item)(pcmk__output_t *out, const char *name, const char *format,...) G_GNUC_PRINTF(3
Definition: output.h:405
void(*) void(*) void(* output_xml)(pcmk__output_t *out, const char *name, const char *buf)
Definition: output.h:371
void(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:347
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
Definition: output.h:262
void(* subprocess_output)(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr)
Definition: output.h:322
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
bool(* init)(pcmk__output_t *out)
Definition: output.h:215
void * priv
Implementation-specific private data.
Definition: output.h:199
void(* reset)(pcmk__output_t *out)
Definition: output.h:280
void(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Definition: output.h:361
void(* free_priv)(pcmk__output_t *out)
Definition: output.h:226
gchar * request
A copy of the request that generated this output.
Definition: output.h:165
Wrappers for and extensions to libxml2.
const xmlChar * pcmkXmlStr
Definition: xml.h:51
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1976