Line data Source code
1 : /*
2 : * Copyright 2004-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>
11 :
12 : #include <sys/param.h>
13 : #include <sys/types.h>
14 : #include <sys/wait.h>
15 : #include <sys/stat.h>
16 : #include <sys/utsname.h>
17 :
18 : #include <stdio.h>
19 : #include <unistd.h>
20 : #include <string.h>
21 : #include <stdlib.h>
22 : #include <limits.h>
23 : #include <ctype.h>
24 : #include <pwd.h>
25 : #include <grp.h>
26 : #include <time.h>
27 : #include <libgen.h>
28 : #include <signal.h>
29 : #include <bzlib.h>
30 :
31 : #include <qb/qbdefs.h>
32 :
33 : #include <crm/crm.h>
34 : #include <crm/common/mainloop.h>
35 :
36 : // Use high-resolution (millisecond) timestamps if libqb supports them
37 : #ifdef QB_FEATURE_LOG_HIRES_TIMESTAMPS
38 : #define TIMESTAMP_FORMAT_SPEC "%%T"
39 : typedef struct timespec *log_time_t;
40 : #else
41 : #define TIMESTAMP_FORMAT_SPEC "%%t"
42 : typedef time_t log_time_t;
43 : #endif
44 :
45 : unsigned int crm_log_level = LOG_INFO;
46 : unsigned int crm_trace_nonlog = 0;
47 : bool pcmk__is_daemon = false;
48 : char *pcmk__our_nodename = NULL;
49 :
50 : static unsigned int crm_log_priority = LOG_NOTICE;
51 : static GLogFunc glib_log_default = NULL;
52 : static pcmk__output_t *logger_out = NULL;
53 :
54 : pcmk__config_error_func pcmk__config_error_handler = NULL;
55 : pcmk__config_warning_func pcmk__config_warning_handler = NULL;
56 : void *pcmk__config_error_context = NULL;
57 : void *pcmk__config_warning_context = NULL;
58 :
59 : static gboolean crm_tracing_enabled(void);
60 :
61 : static void
62 0 : crm_glib_handler(const gchar * log_domain, GLogLevelFlags flags, const gchar * message,
63 : gpointer user_data)
64 : {
65 0 : int log_level = LOG_WARNING;
66 0 : GLogLevelFlags msg_level = (flags & G_LOG_LEVEL_MASK);
67 : static struct qb_log_callsite *glib_cs = NULL;
68 :
69 0 : if (glib_cs == NULL) {
70 0 : glib_cs = qb_log_callsite_get(__func__, __FILE__, "glib-handler",
71 : LOG_DEBUG, __LINE__, crm_trace_nonlog);
72 : }
73 :
74 0 : switch (msg_level) {
75 0 : case G_LOG_LEVEL_CRITICAL:
76 0 : log_level = LOG_CRIT;
77 :
78 0 : if (!crm_is_callsite_active(glib_cs, LOG_DEBUG, crm_trace_nonlog)) {
79 : /* log and record how we got here */
80 0 : crm_abort(__FILE__, __func__, __LINE__, message, TRUE, TRUE);
81 : }
82 0 : break;
83 :
84 0 : case G_LOG_LEVEL_ERROR:
85 0 : log_level = LOG_ERR;
86 0 : break;
87 0 : case G_LOG_LEVEL_MESSAGE:
88 0 : log_level = LOG_NOTICE;
89 0 : break;
90 0 : case G_LOG_LEVEL_INFO:
91 0 : log_level = LOG_INFO;
92 0 : break;
93 0 : case G_LOG_LEVEL_DEBUG:
94 0 : log_level = LOG_DEBUG;
95 0 : break;
96 :
97 0 : case G_LOG_LEVEL_WARNING:
98 : case G_LOG_FLAG_RECURSION:
99 : case G_LOG_FLAG_FATAL:
100 : case G_LOG_LEVEL_MASK:
101 0 : log_level = LOG_WARNING;
102 0 : break;
103 : }
104 :
105 0 : do_crm_log(log_level, "%s: %s", log_domain, message);
106 0 : }
107 :
108 : #ifndef NAME_MAX
109 : # define NAME_MAX 256
110 : #endif
111 :
112 : /*!
113 : * \internal
114 : * \brief Write out a blackbox (enabling blackboxes if needed)
115 : *
116 : * \param[in] nsig Signal number that was received
117 : *
118 : * \note This is a true signal handler, and so must be async-safe.
119 : */
120 : static void
121 0 : crm_trigger_blackbox(int nsig)
122 : {
123 0 : if(nsig == SIGTRAP) {
124 : /* Turn it on if it wasn't already */
125 0 : crm_enable_blackbox(nsig);
126 : }
127 0 : crm_write_blackbox(nsig, NULL);
128 0 : }
129 :
130 : void
131 0 : crm_log_deinit(void)
132 : {
133 0 : if (glib_log_default != NULL) {
134 0 : g_log_set_default_handler(glib_log_default, NULL);
135 : }
136 0 : }
137 :
138 : #define FMT_MAX 256
139 :
140 : /*!
141 : * \internal
142 : * \brief Set the log format string based on the passed-in method
143 : *
144 : * \param[in] method The detail level of the log output
145 : * \param[in] daemon The daemon ID included in error messages
146 : * \param[in] use_pid Cached result of getpid() call, for efficiency
147 : * \param[in] use_nodename Cached result of uname() call, for efficiency
148 : *
149 : */
150 :
151 : /* XXX __attribute__((nonnull)) for use_nodename parameter */
152 : static void
153 0 : set_format_string(int method, const char *daemon, pid_t use_pid,
154 : const char *use_nodename)
155 : {
156 0 : if (method == QB_LOG_SYSLOG) {
157 : // The system log gets a simplified, user-friendly format
158 0 : crm_extended_logging(method, QB_FALSE);
159 0 : qb_log_format_set(method, "%g %p: %b");
160 :
161 : } else {
162 : // Everything else gets more detail, for advanced troubleshooting
163 :
164 0 : int offset = 0;
165 : char fmt[FMT_MAX];
166 :
167 0 : if (method > QB_LOG_STDERR) {
168 : // If logging to file, prefix with timestamp, node name, daemon ID
169 0 : offset += snprintf(fmt + offset, FMT_MAX - offset,
170 : TIMESTAMP_FORMAT_SPEC " %s %-20s[%lu] ",
171 : use_nodename, daemon, (unsigned long) use_pid);
172 : }
173 :
174 : // Add function name (in parentheses)
175 0 : offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%n");
176 0 : if (crm_tracing_enabled()) {
177 : // When tracing, add file and line number
178 0 : offset += snprintf(fmt + offset, FMT_MAX - offset, "@%%f:%%l");
179 : }
180 0 : offset += snprintf(fmt + offset, FMT_MAX - offset, ")");
181 :
182 : // Add tag (if any), severity, and actual message
183 0 : offset += snprintf(fmt + offset, FMT_MAX - offset, " %%g\t%%p: %%b");
184 :
185 0 : CRM_LOG_ASSERT(offset > 0);
186 0 : qb_log_format_set(method, fmt);
187 : }
188 0 : }
189 :
190 : #define DEFAULT_LOG_FILE CRM_LOG_DIR "/pacemaker.log"
191 :
192 : static bool
193 0 : logfile_disabled(const char *filename)
194 : {
195 0 : return pcmk__str_eq(filename, PCMK_VALUE_NONE, pcmk__str_casei)
196 0 : || pcmk__str_eq(filename, "/dev/null", pcmk__str_none);
197 : }
198 :
199 : /*!
200 : * \internal
201 : * \brief Fix log file ownership if group is wrong or doesn't have access
202 : *
203 : * \param[in] filename Log file name (for logging only)
204 : * \param[in] logfd Log file descriptor
205 : *
206 : * \return Standard Pacemaker return code
207 : */
208 : static int
209 0 : chown_logfile(const char *filename, int logfd)
210 : {
211 0 : uid_t pcmk_uid = 0;
212 0 : gid_t pcmk_gid = 0;
213 : struct stat st;
214 : int rc;
215 :
216 : // Get the log file's current ownership and permissions
217 0 : if (fstat(logfd, &st) < 0) {
218 0 : return errno;
219 : }
220 :
221 : // Any other errors don't prevent file from being used as log
222 :
223 0 : rc = pcmk_daemon_user(&pcmk_uid, &pcmk_gid);
224 0 : if (rc != pcmk_ok) {
225 0 : rc = pcmk_legacy2rc(rc);
226 0 : crm_warn("Not changing '%s' ownership because user information "
227 : "unavailable: %s", filename, pcmk_rc_str(rc));
228 0 : return pcmk_rc_ok;
229 : }
230 0 : if ((st.st_gid == pcmk_gid)
231 0 : && ((st.st_mode & S_IRWXG) == (S_IRGRP|S_IWGRP))) {
232 0 : return pcmk_rc_ok;
233 : }
234 0 : if (fchown(logfd, pcmk_uid, pcmk_gid) < 0) {
235 0 : crm_warn("Couldn't change '%s' ownership to user %s gid %d: %s",
236 : filename, CRM_DAEMON_USER, pcmk_gid, strerror(errno));
237 : }
238 0 : return pcmk_rc_ok;
239 : }
240 :
241 : // Reset log file permissions (using environment variable if set)
242 : static void
243 0 : chmod_logfile(const char *filename, int logfd)
244 : {
245 0 : const char *modestr = pcmk__env_option(PCMK__ENV_LOGFILE_MODE);
246 0 : mode_t filemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
247 :
248 0 : if (modestr != NULL) {
249 0 : long filemode_l = strtol(modestr, NULL, 8);
250 :
251 0 : if ((filemode_l != LONG_MIN) && (filemode_l != LONG_MAX)) {
252 0 : filemode = (mode_t) filemode_l;
253 : }
254 : }
255 0 : if ((filemode != 0) && (fchmod(logfd, filemode) < 0)) {
256 0 : crm_warn("Couldn't change '%s' mode to %04o: %s",
257 : filename, filemode, strerror(errno));
258 : }
259 0 : }
260 :
261 : // If we're root, correct a log file's permissions if needed
262 : static int
263 0 : set_logfile_permissions(const char *filename, FILE *logfile)
264 : {
265 0 : if (geteuid() == 0) {
266 0 : int logfd = fileno(logfile);
267 0 : int rc = chown_logfile(filename, logfd);
268 :
269 0 : if (rc != pcmk_rc_ok) {
270 0 : return rc;
271 : }
272 0 : chmod_logfile(filename, logfd);
273 : }
274 0 : return pcmk_rc_ok;
275 : }
276 :
277 : // Enable libqb logging to a new log file
278 : static void
279 0 : enable_logfile(int fd)
280 : {
281 0 : qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_TRUE);
282 : #if 0
283 : qb_log_ctl(fd, QB_LOG_CONF_FILE_SYNC, 1); // Turn on synchronous writes
284 : #endif
285 :
286 : #ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
287 : // Longer than default, for logging long XML lines
288 0 : qb_log_ctl(fd, QB_LOG_CONF_MAX_LINE_LEN, 800);
289 : #endif
290 :
291 0 : crm_update_callsites();
292 0 : }
293 :
294 : static inline void
295 0 : disable_logfile(int fd)
296 : {
297 0 : qb_log_ctl(fd, QB_LOG_CONF_ENABLED, QB_FALSE);
298 0 : }
299 :
300 : static void
301 0 : setenv_logfile(const char *filename)
302 : {
303 : // Some resource agents will log only if environment variable is set
304 0 : if (pcmk__env_option(PCMK__ENV_LOGFILE) == NULL) {
305 0 : pcmk__set_env_option(PCMK__ENV_LOGFILE, filename, true);
306 : }
307 0 : }
308 :
309 : /*!
310 : * \brief Add a file to be used as a Pacemaker detail log
311 : *
312 : * \param[in] filename Name of log file to use
313 : *
314 : * \return Standard Pacemaker return code
315 : */
316 : int
317 0 : pcmk__add_logfile(const char *filename)
318 : {
319 : /* No log messages from this function will be logged to the new log!
320 : * If another target such as syslog has already been added, the messages
321 : * should show up there.
322 : */
323 :
324 0 : int fd = 0;
325 0 : int rc = pcmk_rc_ok;
326 0 : FILE *logfile = NULL;
327 0 : bool is_default = false;
328 :
329 : static int default_fd = -1;
330 : static bool have_logfile = false;
331 :
332 : // Use default if caller didn't specify (and we don't already have one)
333 0 : if (filename == NULL) {
334 0 : if (have_logfile) {
335 0 : return pcmk_rc_ok;
336 : }
337 0 : filename = DEFAULT_LOG_FILE;
338 : }
339 :
340 : // If the user doesn't want logging, we're done
341 0 : if (logfile_disabled(filename)) {
342 0 : return pcmk_rc_ok;
343 : }
344 :
345 : // If the caller wants the default and we already have it, we're done
346 0 : is_default = pcmk__str_eq(filename, DEFAULT_LOG_FILE, pcmk__str_none);
347 0 : if (is_default && (default_fd >= 0)) {
348 0 : return pcmk_rc_ok;
349 : }
350 :
351 : // Check whether we have write access to the file
352 0 : logfile = fopen(filename, "a");
353 0 : if (logfile == NULL) {
354 0 : rc = errno;
355 0 : crm_warn("Logging to '%s' is disabled: %s " CRM_XS " uid=%u gid=%u",
356 : filename, strerror(rc), geteuid(), getegid());
357 0 : return rc;
358 : }
359 :
360 0 : rc = set_logfile_permissions(filename, logfile);
361 0 : if (rc != pcmk_rc_ok) {
362 0 : crm_warn("Logging to '%s' is disabled: %s " CRM_XS " permissions",
363 : filename, strerror(rc));
364 0 : fclose(logfile);
365 0 : return rc;
366 : }
367 :
368 : // Close and reopen as libqb logging target
369 0 : fclose(logfile);
370 0 : fd = qb_log_file_open(filename);
371 0 : if (fd < 0) {
372 0 : crm_warn("Logging to '%s' is disabled: %s " CRM_XS " qb_log_file_open",
373 : filename, strerror(-fd));
374 0 : return -fd; // == +errno
375 : }
376 :
377 0 : if (is_default) {
378 0 : default_fd = fd;
379 0 : setenv_logfile(filename);
380 :
381 0 : } else if (default_fd >= 0) {
382 0 : crm_notice("Switching logging to %s", filename);
383 0 : disable_logfile(default_fd);
384 : }
385 :
386 0 : crm_notice("Additional logging available in %s", filename);
387 0 : enable_logfile(fd);
388 0 : have_logfile = true;
389 0 : return pcmk_rc_ok;
390 : }
391 :
392 : /*!
393 : * \brief Add multiple additional log files
394 : *
395 : * \param[in] log_files Array of log files to add
396 : * \param[in] out Output object to use for error reporting
397 : *
398 : * \return Standard Pacemaker return code
399 : */
400 : void
401 0 : pcmk__add_logfiles(gchar **log_files, pcmk__output_t *out)
402 : {
403 0 : if (log_files == NULL) {
404 0 : return;
405 : }
406 :
407 0 : for (gchar **fname = log_files; *fname != NULL; fname++) {
408 0 : int rc = pcmk__add_logfile(*fname);
409 :
410 0 : if (rc != pcmk_rc_ok) {
411 0 : out->err(out, "Logging to %s is disabled: %s",
412 : *fname, pcmk_rc_str(rc));
413 : }
414 : }
415 : }
416 :
417 : static int blackbox_trigger = 0;
418 : static volatile char *blackbox_file_prefix = NULL;
419 :
420 : static void
421 0 : blackbox_logger(int32_t t, struct qb_log_callsite *cs, log_time_t timestamp,
422 : const char *msg)
423 : {
424 0 : if(cs && cs->priority < LOG_ERR) {
425 0 : crm_write_blackbox(SIGTRAP, cs); /* Bypass the over-dumping logic */
426 : } else {
427 0 : crm_write_blackbox(0, cs);
428 : }
429 0 : }
430 :
431 : static void
432 0 : crm_control_blackbox(int nsig, bool enable)
433 : {
434 0 : int lpc = 0;
435 :
436 0 : if (blackbox_file_prefix == NULL) {
437 0 : pid_t pid = getpid();
438 :
439 0 : blackbox_file_prefix = crm_strdup_printf("%s/%s-%lu",
440 : CRM_BLACKBOX_DIR,
441 : crm_system_name,
442 : (unsigned long) pid);
443 : }
444 :
445 0 : if (enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
446 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_SIZE, 5 * 1024 * 1024); /* Any size change drops existing entries */
447 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE); /* Setting the size seems to disable it */
448 :
449 : /* Enable synchronous logging */
450 0 : for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
451 0 : qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_TRUE);
452 : }
453 :
454 0 : crm_notice("Initiated blackbox recorder: %s", blackbox_file_prefix);
455 :
456 : /* Save to disk on abnormal termination */
457 0 : crm_signal_handler(SIGSEGV, crm_trigger_blackbox);
458 0 : crm_signal_handler(SIGABRT, crm_trigger_blackbox);
459 0 : crm_signal_handler(SIGILL, crm_trigger_blackbox);
460 0 : crm_signal_handler(SIGBUS, crm_trigger_blackbox);
461 0 : crm_signal_handler(SIGFPE, crm_trigger_blackbox);
462 :
463 0 : crm_update_callsites();
464 :
465 0 : blackbox_trigger = qb_log_custom_open(blackbox_logger, NULL, NULL, NULL);
466 0 : qb_log_ctl(blackbox_trigger, QB_LOG_CONF_ENABLED, QB_TRUE);
467 0 : crm_trace("Trigger: %d is %d %d", blackbox_trigger,
468 : qb_log_ctl(blackbox_trigger, QB_LOG_CONF_STATE_GET, 0), QB_LOG_STATE_ENABLED);
469 :
470 0 : crm_update_callsites();
471 :
472 0 : } else if (!enable && qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_STATE_GET, 0) == QB_LOG_STATE_ENABLED) {
473 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
474 :
475 : /* Disable synchronous logging again when the blackbox is disabled */
476 0 : for (lpc = QB_LOG_BLACKBOX; lpc < QB_LOG_TARGET_MAX; lpc++) {
477 0 : qb_log_ctl(lpc, QB_LOG_CONF_FILE_SYNC, QB_FALSE);
478 : }
479 : }
480 0 : }
481 :
482 : void
483 0 : crm_enable_blackbox(int nsig)
484 : {
485 0 : crm_control_blackbox(nsig, TRUE);
486 0 : }
487 :
488 : void
489 0 : crm_disable_blackbox(int nsig)
490 : {
491 0 : crm_control_blackbox(nsig, FALSE);
492 0 : }
493 :
494 : /*!
495 : * \internal
496 : * \brief Write out a blackbox, if blackboxes are enabled
497 : *
498 : * \param[in] nsig Signal that was received
499 : * \param[in] cs libqb callsite
500 : *
501 : * \note This may be called via a true signal handler and so must be async-safe.
502 : * @TODO actually make this async-safe
503 : */
504 : void
505 0 : crm_write_blackbox(int nsig, const struct qb_log_callsite *cs)
506 : {
507 : static volatile int counter = 1;
508 : static volatile time_t last = 0;
509 :
510 : char buffer[NAME_MAX];
511 0 : time_t now = time(NULL);
512 :
513 0 : if (blackbox_file_prefix == NULL) {
514 0 : return;
515 : }
516 :
517 0 : switch (nsig) {
518 0 : case 0:
519 : case SIGTRAP:
520 : /* The graceful case - such as assertion failure or user request */
521 :
522 0 : if (nsig == 0 && now == last) {
523 : /* Prevent over-dumping */
524 0 : return;
525 : }
526 :
527 0 : snprintf(buffer, NAME_MAX, "%s.%d", blackbox_file_prefix, counter++);
528 0 : if (nsig == SIGTRAP) {
529 0 : crm_notice("Blackbox dump requested, please see %s for contents", buffer);
530 :
531 0 : } else if (cs) {
532 0 : syslog(LOG_NOTICE,
533 : "Problem detected at %s:%d (%s), please see %s for additional details",
534 0 : cs->function, cs->lineno, cs->filename, buffer);
535 : } else {
536 0 : crm_notice("Problem detected, please see %s for additional details", buffer);
537 : }
538 :
539 0 : last = now;
540 0 : qb_log_blackbox_write_to_file(buffer);
541 :
542 : /* Flush the existing contents
543 : * A size change would also work
544 : */
545 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
546 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_TRUE);
547 0 : break;
548 :
549 0 : default:
550 : /* Do as little as possible, just try to get what we have out
551 : * We logged the filename when the blackbox was enabled
552 : */
553 0 : crm_signal_handler(nsig, SIG_DFL);
554 0 : qb_log_blackbox_write_to_file((const char *)blackbox_file_prefix);
555 0 : qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
556 0 : raise(nsig);
557 0 : break;
558 : }
559 : }
560 :
561 : static const char *
562 0 : crm_quark_to_string(uint32_t tag)
563 : {
564 0 : const char *text = g_quark_to_string(tag);
565 :
566 0 : if (text) {
567 0 : return text;
568 : }
569 0 : return "";
570 : }
571 :
572 : static void
573 0 : crm_log_filter_source(int source, const char *trace_files, const char *trace_fns,
574 : const char *trace_fmts, const char *trace_tags, const char *trace_blackbox,
575 : struct qb_log_callsite *cs)
576 : {
577 0 : if (qb_log_ctl(source, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
578 0 : return;
579 0 : } else if (cs->tags != crm_trace_nonlog && source == QB_LOG_BLACKBOX) {
580 : /* Blackbox gets everything if enabled */
581 0 : qb_bit_set(cs->targets, source);
582 :
583 0 : } else if (source == blackbox_trigger && blackbox_trigger > 0) {
584 : /* Should this log message result in the blackbox being dumped */
585 0 : if (cs->priority <= LOG_ERR) {
586 0 : qb_bit_set(cs->targets, source);
587 :
588 0 : } else if (trace_blackbox) {
589 0 : char *key = crm_strdup_printf("%s:%d", cs->function, cs->lineno);
590 :
591 0 : if (strstr(trace_blackbox, key) != NULL) {
592 0 : qb_bit_set(cs->targets, source);
593 : }
594 0 : free(key);
595 : }
596 :
597 0 : } else if (source == QB_LOG_SYSLOG) { /* No tracing to syslog */
598 0 : if (cs->priority <= crm_log_priority && cs->priority <= crm_log_level) {
599 0 : qb_bit_set(cs->targets, source);
600 : }
601 : /* Log file tracing options... */
602 0 : } else if (cs->priority <= crm_log_level) {
603 0 : qb_bit_set(cs->targets, source);
604 0 : } else if (trace_files && strstr(trace_files, cs->filename) != NULL) {
605 0 : qb_bit_set(cs->targets, source);
606 0 : } else if (trace_fns && strstr(trace_fns, cs->function) != NULL) {
607 0 : qb_bit_set(cs->targets, source);
608 0 : } else if (trace_fmts && strstr(trace_fmts, cs->format) != NULL) {
609 0 : qb_bit_set(cs->targets, source);
610 0 : } else if (trace_tags
611 0 : && cs->tags != 0
612 0 : && cs->tags != crm_trace_nonlog && g_quark_to_string(cs->tags) != NULL) {
613 0 : qb_bit_set(cs->targets, source);
614 : }
615 : }
616 :
617 : #ifndef HAVE_STRCHRNUL
618 : /* strchrnul() is a GNU extension. If not present, use our own definition.
619 : * The GNU version returns char*, but we only need it to be const char*.
620 : */
621 : static const char *
622 : strchrnul(const char *s, int c)
623 : {
624 : while ((*s != c) && (*s != '\0')) {
625 : ++s;
626 : }
627 : return s;
628 : }
629 : #endif
630 :
631 : static void
632 0 : crm_log_filter(struct qb_log_callsite *cs)
633 : {
634 0 : int lpc = 0;
635 : static int need_init = 1;
636 : static const char *trace_fns = NULL;
637 : static const char *trace_tags = NULL;
638 : static const char *trace_fmts = NULL;
639 : static const char *trace_files = NULL;
640 : static const char *trace_blackbox = NULL;
641 :
642 0 : if (need_init) {
643 0 : need_init = 0;
644 0 : trace_fns = pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS);
645 0 : trace_fmts = pcmk__env_option(PCMK__ENV_TRACE_FORMATS);
646 0 : trace_tags = pcmk__env_option(PCMK__ENV_TRACE_TAGS);
647 0 : trace_files = pcmk__env_option(PCMK__ENV_TRACE_FILES);
648 0 : trace_blackbox = pcmk__env_option(PCMK__ENV_TRACE_BLACKBOX);
649 :
650 0 : if (trace_tags != NULL) {
651 : uint32_t tag;
652 : char token[500];
653 0 : const char *offset = NULL;
654 0 : const char *next = trace_tags;
655 :
656 : do {
657 0 : offset = next;
658 0 : next = strchrnul(offset, ',');
659 0 : snprintf(token, sizeof(token), "%.*s", (int)(next - offset), offset);
660 :
661 0 : tag = g_quark_from_string(token);
662 0 : crm_info("Created GQuark %u from token '%s' in '%s'", tag, token, trace_tags);
663 :
664 0 : if (next[0] != 0) {
665 0 : next++;
666 : }
667 :
668 0 : } while (next != NULL && next[0] != 0);
669 : }
670 : }
671 :
672 0 : cs->targets = 0; /* Reset then find targets to enable */
673 0 : for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
674 0 : crm_log_filter_source(lpc, trace_files, trace_fns, trace_fmts, trace_tags, trace_blackbox,
675 : cs);
676 : }
677 0 : }
678 :
679 : gboolean
680 0 : crm_is_callsite_active(struct qb_log_callsite *cs, uint8_t level, uint32_t tags)
681 : {
682 0 : gboolean refilter = FALSE;
683 :
684 0 : if (cs == NULL) {
685 0 : return FALSE;
686 : }
687 :
688 0 : if (cs->priority != level) {
689 0 : cs->priority = level;
690 0 : refilter = TRUE;
691 : }
692 :
693 0 : if (cs->tags != tags) {
694 0 : cs->tags = tags;
695 0 : refilter = TRUE;
696 : }
697 :
698 0 : if (refilter) {
699 0 : crm_log_filter(cs);
700 : }
701 :
702 0 : if (cs->targets == 0) {
703 0 : return FALSE;
704 : }
705 0 : return TRUE;
706 : }
707 :
708 : void
709 0 : crm_update_callsites(void)
710 : {
711 : static gboolean log = TRUE;
712 :
713 0 : if (log) {
714 0 : log = FALSE;
715 0 : crm_debug
716 : ("Enabling callsites based on priority=%d, files=%s, functions=%s, formats=%s, tags=%s",
717 : crm_log_level, pcmk__env_option(PCMK__ENV_TRACE_FILES),
718 : pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS),
719 : pcmk__env_option(PCMK__ENV_TRACE_FORMATS),
720 : pcmk__env_option(PCMK__ENV_TRACE_TAGS));
721 : }
722 0 : qb_log_filter_fn_set(crm_log_filter);
723 0 : }
724 :
725 : static gboolean
726 0 : crm_tracing_enabled(void)
727 : {
728 0 : return (crm_log_level == LOG_TRACE)
729 0 : || (pcmk__env_option(PCMK__ENV_TRACE_FILES) != NULL)
730 0 : || (pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS) != NULL)
731 0 : || (pcmk__env_option(PCMK__ENV_TRACE_FORMATS) != NULL)
732 0 : || (pcmk__env_option(PCMK__ENV_TRACE_TAGS) != NULL);
733 : }
734 :
735 : static int
736 0 : crm_priority2int(const char *name)
737 : {
738 : struct syslog_names {
739 : const char *name;
740 : int priority;
741 : };
742 : static struct syslog_names p_names[] = {
743 : {"emerg", LOG_EMERG},
744 : {"alert", LOG_ALERT},
745 : {"crit", LOG_CRIT},
746 : {"error", LOG_ERR},
747 : {"warning", LOG_WARNING},
748 : {"notice", LOG_NOTICE},
749 : {"info", LOG_INFO},
750 : {"debug", LOG_DEBUG},
751 : {NULL, -1}
752 : };
753 : int lpc;
754 :
755 0 : for (lpc = 0; name != NULL && p_names[lpc].name != NULL; lpc++) {
756 0 : if (pcmk__str_eq(p_names[lpc].name, name, pcmk__str_none)) {
757 0 : return p_names[lpc].priority;
758 : }
759 : }
760 0 : return crm_log_priority;
761 : }
762 :
763 :
764 : /*!
765 : * \internal
766 : * \brief Set the identifier for the current process
767 : *
768 : * If the identifier crm_system_name is not already set, then it is set as follows:
769 : * - it is passed to the function via the "entity" parameter, or
770 : * - it is derived from the executable name
771 : *
772 : * The identifier can be used in logs, IPC, and more.
773 : *
774 : * This method also sets the PCMK_service environment variable.
775 : *
776 : * \param[in] entity If not NULL, will be assigned to the identifier
777 : * \param[in] argc The number of command line parameters
778 : * \param[in] argv The command line parameter values
779 : */
780 : static void
781 0 : set_identity(const char *entity, int argc, char *const *argv)
782 : {
783 0 : if (crm_system_name != NULL) {
784 0 : return; // Already set, don't overwrite
785 : }
786 :
787 0 : if (entity != NULL) {
788 0 : crm_system_name = pcmk__str_copy(entity);
789 :
790 0 : } else if ((argc > 0) && (argv != NULL)) {
791 0 : char *mutable = strdup(argv[0]);
792 0 : char *modified = basename(mutable);
793 :
794 0 : if (strstr(modified, "lt-") == modified) {
795 0 : modified += 3;
796 : }
797 0 : crm_system_name = pcmk__str_copy(modified);
798 0 : free(mutable);
799 :
800 : } else {
801 0 : crm_system_name = pcmk__str_copy("Unknown");
802 : }
803 :
804 : // Used by fencing.py.py (in fence-agents)
805 0 : pcmk__set_env_option(PCMK__ENV_SERVICE, crm_system_name, false);
806 : }
807 :
808 : void
809 0 : crm_log_preinit(const char *entity, int argc, char *const *argv)
810 : {
811 : /* Configure libqb logging with nothing turned on */
812 :
813 : struct utsname res;
814 0 : int lpc = 0;
815 0 : int32_t qb_facility = 0;
816 0 : pid_t pid = getpid();
817 0 : const char *nodename = "localhost";
818 : static bool have_logging = false;
819 :
820 0 : if (have_logging) {
821 0 : return;
822 : }
823 :
824 0 : have_logging = true;
825 :
826 0 : crm_xml_init(); /* Sets buffer allocation strategy */
827 :
828 0 : if (crm_trace_nonlog == 0) {
829 0 : crm_trace_nonlog = g_quark_from_static_string("Pacemaker non-logging tracepoint");
830 : }
831 :
832 0 : umask(S_IWGRP | S_IWOTH | S_IROTH);
833 :
834 : /* Redirect messages from glib functions to our handler */
835 0 : glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
836 :
837 : /* and for good measure... - this enum is a bit field (!) */
838 0 : g_log_set_always_fatal((GLogLevelFlags) 0); /*value out of range */
839 :
840 : /* Set crm_system_name, which is used as the logging name. It may also
841 : * be used for other purposes such as an IPC client name.
842 : */
843 0 : set_identity(entity, argc, argv);
844 :
845 0 : qb_facility = qb_log_facility2int("local0");
846 0 : qb_log_init(crm_system_name, qb_facility, LOG_ERR);
847 0 : crm_log_level = LOG_CRIT;
848 :
849 : /* Nuke any syslog activity until it's asked for */
850 0 : qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
851 : #ifdef HAVE_qb_log_conf_QB_LOG_CONF_MAX_LINE_LEN
852 : // Shorter than default, generous for what we *should* send to syslog
853 0 : qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_MAX_LINE_LEN, 256);
854 : #endif
855 0 : if (uname(memset(&res, 0, sizeof(res))) == 0 && *res.nodename != '\0') {
856 0 : nodename = res.nodename;
857 : }
858 :
859 : /* Set format strings and disable threading
860 : * Pacemaker and threads do not mix well (due to the amount of forking)
861 : */
862 0 : qb_log_tags_stringify_fn_set(crm_quark_to_string);
863 0 : for (lpc = QB_LOG_SYSLOG; lpc < QB_LOG_TARGET_MAX; lpc++) {
864 0 : qb_log_ctl(lpc, QB_LOG_CONF_THREADED, QB_FALSE);
865 : #ifdef HAVE_qb_log_conf_QB_LOG_CONF_ELLIPSIS
866 : // End truncated lines with '...'
867 0 : qb_log_ctl(lpc, QB_LOG_CONF_ELLIPSIS, QB_TRUE);
868 : #endif
869 0 : set_format_string(lpc, crm_system_name, pid, nodename);
870 : }
871 :
872 : #ifdef ENABLE_NLS
873 : /* Enable translations (experimental). Currently we only have a few
874 : * proof-of-concept translations for some option help. The goal would be to
875 : * offer translations for option help and man pages rather than logs or
876 : * documentation, to reduce the burden of maintaining them.
877 : */
878 :
879 : // Load locale information for the local host from the environment
880 : setlocale(LC_ALL, "");
881 :
882 : // Tell gettext where to find Pacemaker message catalogs
883 : CRM_ASSERT(bindtextdomain(PACKAGE, PCMK__LOCALE_DIR) != NULL);
884 :
885 : // Tell gettext to use the Pacemaker message catalogs
886 : CRM_ASSERT(textdomain(PACKAGE) != NULL);
887 :
888 : // Tell gettext that the translated strings are stored in UTF-8
889 : bind_textdomain_codeset(PACKAGE, "UTF-8");
890 : #endif
891 : }
892 :
893 : gboolean
894 0 : crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_stderr,
895 : int argc, char **argv, gboolean quiet)
896 : {
897 0 : const char *syslog_priority = NULL;
898 0 : const char *facility = pcmk__env_option(PCMK__ENV_LOGFACILITY);
899 0 : const char *f_copy = facility;
900 :
901 0 : pcmk__is_daemon = daemon;
902 0 : crm_log_preinit(entity, argc, argv);
903 :
904 0 : if (level > LOG_TRACE) {
905 0 : level = LOG_TRACE;
906 : }
907 0 : if(level > crm_log_level) {
908 0 : crm_log_level = level;
909 : }
910 :
911 : /* Should we log to syslog */
912 0 : if (facility == NULL) {
913 0 : if (pcmk__is_daemon) {
914 0 : facility = "daemon";
915 : } else {
916 0 : facility = PCMK_VALUE_NONE;
917 : }
918 0 : pcmk__set_env_option(PCMK__ENV_LOGFACILITY, facility, true);
919 : }
920 :
921 0 : if (pcmk__str_eq(facility, PCMK_VALUE_NONE, pcmk__str_casei)) {
922 0 : quiet = TRUE;
923 :
924 :
925 : } else {
926 0 : qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, qb_log_facility2int(facility));
927 : }
928 :
929 0 : if (pcmk__env_option_enabled(crm_system_name, PCMK__ENV_DEBUG)) {
930 : /* Override the default setting */
931 0 : crm_log_level = LOG_DEBUG;
932 : }
933 :
934 : /* What lower threshold do we have for sending to syslog */
935 0 : syslog_priority = pcmk__env_option(PCMK__ENV_LOGPRIORITY);
936 0 : if (syslog_priority) {
937 0 : crm_log_priority = crm_priority2int(syslog_priority);
938 : }
939 0 : qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*",
940 : crm_log_priority);
941 :
942 : // Log to syslog unless requested to be quiet
943 0 : if (!quiet) {
944 0 : qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
945 : }
946 :
947 : /* Should we log to stderr */
948 0 : if (pcmk__env_option_enabled(crm_system_name, PCMK__ENV_STDERR)) {
949 : /* Override the default setting */
950 0 : to_stderr = TRUE;
951 : }
952 0 : crm_enable_stderr(to_stderr);
953 :
954 : // Log to a file if we're a daemon or user asked for one
955 : {
956 0 : const char *logfile = pcmk__env_option(PCMK__ENV_LOGFILE);
957 :
958 0 : if (!pcmk__str_eq(PCMK_VALUE_NONE, logfile, pcmk__str_casei)
959 0 : && (pcmk__is_daemon || (logfile != NULL))) {
960 : // Daemons always get a log file, unless explicitly set to "none"
961 0 : pcmk__add_logfile(logfile);
962 : }
963 : }
964 :
965 0 : if (pcmk__is_daemon
966 0 : && pcmk__env_option_enabled(crm_system_name, PCMK__ENV_BLACKBOX)) {
967 0 : crm_enable_blackbox(0);
968 : }
969 :
970 : /* Summary */
971 0 : crm_trace("Quiet: %d, facility %s", quiet, f_copy);
972 0 : pcmk__env_option(PCMK__ENV_LOGFILE);
973 0 : pcmk__env_option(PCMK__ENV_LOGFACILITY);
974 :
975 0 : crm_update_callsites();
976 :
977 : /* Ok, now we can start logging... */
978 :
979 : // Disable daemon request if user isn't root or Pacemaker daemon user
980 0 : if (pcmk__is_daemon) {
981 0 : const char *user = getenv("USER");
982 :
983 0 : if (user != NULL && !pcmk__strcase_any_of(user, "root", CRM_DAEMON_USER, NULL)) {
984 0 : crm_trace("Not switching to corefile directory for %s", user);
985 0 : pcmk__is_daemon = false;
986 : }
987 : }
988 :
989 0 : if (pcmk__is_daemon) {
990 0 : int user = getuid();
991 0 : struct passwd *pwent = getpwuid(user);
992 :
993 0 : if (pwent == NULL) {
994 0 : crm_perror(LOG_ERR, "Cannot get name for uid: %d", user);
995 :
996 0 : } else if (!pcmk__strcase_any_of(pwent->pw_name, "root", CRM_DAEMON_USER, NULL)) {
997 0 : crm_trace("Don't change active directory for regular user: %s", pwent->pw_name);
998 :
999 0 : } else if (chdir(CRM_CORE_DIR) < 0) {
1000 0 : crm_perror(LOG_INFO, "Cannot change active directory to " CRM_CORE_DIR);
1001 :
1002 : } else {
1003 0 : crm_info("Changed active directory to " CRM_CORE_DIR);
1004 : }
1005 :
1006 : /* Original meanings from signal(7)
1007 : *
1008 : * Signal Value Action Comment
1009 : * SIGTRAP 5 Core Trace/breakpoint trap
1010 : * SIGUSR1 30,10,16 Term User-defined signal 1
1011 : * SIGUSR2 31,12,17 Term User-defined signal 2
1012 : *
1013 : * Our usage is as similar as possible
1014 : */
1015 0 : mainloop_add_signal(SIGUSR1, crm_enable_blackbox);
1016 0 : mainloop_add_signal(SIGUSR2, crm_disable_blackbox);
1017 0 : mainloop_add_signal(SIGTRAP, crm_trigger_blackbox);
1018 :
1019 0 : } else if (!quiet) {
1020 0 : crm_log_args(argc, argv);
1021 : }
1022 :
1023 0 : return TRUE;
1024 : }
1025 :
1026 : /* returns the old value */
1027 : unsigned int
1028 0 : set_crm_log_level(unsigned int level)
1029 : {
1030 0 : unsigned int old = crm_log_level;
1031 :
1032 0 : if (level > LOG_TRACE) {
1033 0 : level = LOG_TRACE;
1034 : }
1035 0 : crm_log_level = level;
1036 0 : crm_update_callsites();
1037 0 : crm_trace("New log level: %d", level);
1038 0 : return old;
1039 : }
1040 :
1041 : void
1042 0 : crm_enable_stderr(int enable)
1043 : {
1044 0 : if (enable && qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0) != QB_LOG_STATE_ENABLED) {
1045 0 : qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
1046 0 : crm_update_callsites();
1047 :
1048 0 : } else if (enable == FALSE) {
1049 0 : qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
1050 : }
1051 0 : }
1052 :
1053 : /*!
1054 : * \brief Make logging more verbose
1055 : *
1056 : * If logging to stderr is not already enabled when this function is called,
1057 : * enable it. Otherwise, increase the log level by 1.
1058 : *
1059 : * \param[in] argc Ignored
1060 : * \param[in] argv Ignored
1061 : */
1062 : void
1063 0 : crm_bump_log_level(int argc, char **argv)
1064 : {
1065 0 : if (qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_STATE_GET, 0)
1066 : != QB_LOG_STATE_ENABLED) {
1067 0 : crm_enable_stderr(TRUE);
1068 : } else {
1069 0 : set_crm_log_level(crm_log_level + 1);
1070 : }
1071 0 : }
1072 :
1073 : unsigned int
1074 0 : get_crm_log_level(void)
1075 : {
1076 0 : return crm_log_level;
1077 : }
1078 :
1079 : /*!
1080 : * \brief Log the command line (once)
1081 : *
1082 : * \param[in] Number of values in \p argv
1083 : * \param[in] Command-line arguments (including command name)
1084 : *
1085 : * \note This function will only log once, even if called with different
1086 : * arguments.
1087 : */
1088 : void
1089 0 : crm_log_args(int argc, char **argv)
1090 : {
1091 : static bool logged = false;
1092 0 : gchar *arg_string = NULL;
1093 :
1094 0 : if ((argc == 0) || (argv == NULL) || logged) {
1095 0 : return;
1096 : }
1097 0 : logged = true;
1098 0 : arg_string = g_strjoinv(" ", argv);
1099 0 : crm_notice("Invoked: %s", arg_string);
1100 0 : g_free(arg_string);
1101 : }
1102 :
1103 : void
1104 0 : crm_log_output_fn(const char *file, const char *function, int line, int level, const char *prefix,
1105 : const char *output)
1106 : {
1107 0 : const char *next = NULL;
1108 0 : const char *offset = NULL;
1109 :
1110 0 : if (level == LOG_NEVER) {
1111 0 : return;
1112 : }
1113 :
1114 0 : if (output == NULL) {
1115 0 : if (level != LOG_STDOUT) {
1116 0 : level = LOG_TRACE;
1117 : }
1118 0 : output = "-- empty --";
1119 : }
1120 :
1121 0 : next = output;
1122 : do {
1123 0 : offset = next;
1124 0 : next = strchrnul(offset, '\n');
1125 0 : do_crm_log_alias(level, file, function, line, "%s [ %.*s ]", prefix,
1126 : (int)(next - offset), offset);
1127 0 : if (next[0] != 0) {
1128 0 : next++;
1129 : }
1130 :
1131 0 : } while (next != NULL && next[0] != 0);
1132 : }
1133 :
1134 : void
1135 0 : pcmk__cli_init_logging(const char *name, unsigned int verbosity)
1136 : {
1137 0 : crm_log_init(name, LOG_ERR, FALSE, FALSE, 0, NULL, TRUE);
1138 :
1139 0 : for (int i = 0; i < verbosity; i++) {
1140 : /* These arguments are ignored, so pass placeholders. */
1141 0 : crm_bump_log_level(0, NULL);
1142 : }
1143 0 : }
1144 :
1145 : /*!
1146 : * \brief Log XML line-by-line in a formatted fashion
1147 : *
1148 : * \param[in] file File name to use for log filtering
1149 : * \param[in] function Function name to use for log filtering
1150 : * \param[in] line Line number to use for log filtering
1151 : * \param[in] tags Logging tags to use for log filtering
1152 : * \param[in] level Priority at which to log the messages
1153 : * \param[in] text Prefix for each line
1154 : * \param[in] xml XML to log
1155 : *
1156 : * \note This does nothing when \p level is \p LOG_STDOUT.
1157 : * \note Do not call this function directly. It should be called only from the
1158 : * \p do_crm_log_xml() macro.
1159 : */
1160 : void
1161 0 : pcmk_log_xml_as(const char *file, const char *function, uint32_t line,
1162 : uint32_t tags, uint8_t level, const char *text, const xmlNode *xml)
1163 : {
1164 0 : if (xml == NULL) {
1165 0 : do_crm_log(level, "%s%sNo data to dump as XML",
1166 : pcmk__s(text, ""), pcmk__str_empty(text)? "" : " ");
1167 :
1168 : } else {
1169 0 : if (logger_out == NULL) {
1170 0 : CRM_CHECK(pcmk__log_output_new(&logger_out) == pcmk_rc_ok, return);
1171 : }
1172 :
1173 0 : pcmk__output_set_log_level(logger_out, level);
1174 0 : pcmk__output_set_log_filter(logger_out, file, function, line, tags);
1175 0 : pcmk__xml_show(logger_out, text, xml, 1,
1176 : pcmk__xml_fmt_pretty
1177 : |pcmk__xml_fmt_open
1178 : |pcmk__xml_fmt_children
1179 : |pcmk__xml_fmt_close);
1180 0 : pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
1181 : }
1182 : }
1183 :
1184 : /*!
1185 : * \internal
1186 : * \brief Log XML changes line-by-line in a formatted fashion
1187 : *
1188 : * \param[in] file File name to use for log filtering
1189 : * \param[in] function Function name to use for log filtering
1190 : * \param[in] line Line number to use for log filtering
1191 : * \param[in] tags Logging tags to use for log filtering
1192 : * \param[in] level Priority at which to log the messages
1193 : * \param[in] xml XML whose changes to log
1194 : *
1195 : * \note This does nothing when \p level is \c LOG_STDOUT.
1196 : */
1197 : void
1198 0 : pcmk__log_xml_changes_as(const char *file, const char *function, uint32_t line,
1199 : uint32_t tags, uint8_t level, const xmlNode *xml)
1200 : {
1201 0 : if (xml == NULL) {
1202 0 : do_crm_log(level, "No XML to dump");
1203 0 : return;
1204 : }
1205 :
1206 0 : if (logger_out == NULL) {
1207 0 : CRM_CHECK(pcmk__log_output_new(&logger_out) == pcmk_rc_ok, return);
1208 : }
1209 0 : pcmk__output_set_log_level(logger_out, level);
1210 0 : pcmk__output_set_log_filter(logger_out, file, function, line, tags);
1211 0 : pcmk__xml_show_changes(logger_out, xml);
1212 0 : pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
1213 : }
1214 :
1215 : /*!
1216 : * \internal
1217 : * \brief Log an XML patchset line-by-line in a formatted fashion
1218 : *
1219 : * \param[in] file File name to use for log filtering
1220 : * \param[in] function Function name to use for log filtering
1221 : * \param[in] line Line number to use for log filtering
1222 : * \param[in] tags Logging tags to use for log filtering
1223 : * \param[in] level Priority at which to log the messages
1224 : * \param[in] patchset XML patchset to log
1225 : *
1226 : * \note This does nothing when \p level is \c LOG_STDOUT.
1227 : */
1228 : void
1229 0 : pcmk__log_xml_patchset_as(const char *file, const char *function, uint32_t line,
1230 : uint32_t tags, uint8_t level, const xmlNode *patchset)
1231 : {
1232 0 : if (patchset == NULL) {
1233 0 : do_crm_log(level, "No patchset to dump");
1234 0 : return;
1235 : }
1236 :
1237 0 : if (logger_out == NULL) {
1238 0 : CRM_CHECK(pcmk__log_output_new(&logger_out) == pcmk_rc_ok, return);
1239 : }
1240 0 : pcmk__output_set_log_level(logger_out, level);
1241 0 : pcmk__output_set_log_filter(logger_out, file, function, line, tags);
1242 0 : logger_out->message(logger_out, "xml-patchset", patchset);
1243 0 : pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
1244 : }
1245 :
1246 : /*!
1247 : * \internal
1248 : * \brief Free the logging library's internal log output object
1249 : */
1250 : void
1251 0 : pcmk__free_common_logger(void)
1252 : {
1253 0 : if (logger_out != NULL) {
1254 0 : logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
1255 0 : pcmk__output_free(logger_out);
1256 0 : logger_out = NULL;
1257 : }
1258 0 : }
1259 :
1260 : // Deprecated functions kept only for backward API compatibility
1261 : // LCOV_EXCL_START
1262 :
1263 : #include <crm/common/logging_compat.h>
1264 :
1265 : gboolean
1266 : crm_log_cli_init(const char *entity)
1267 : {
1268 : pcmk__cli_init_logging(entity, 0);
1269 : return TRUE;
1270 : }
1271 :
1272 : gboolean
1273 : crm_add_logfile(const char *filename)
1274 : {
1275 : return pcmk__add_logfile(filename) == pcmk_rc_ok;
1276 : }
1277 :
1278 : void
1279 : pcmk_log_xml_impl(uint8_t level, const char *text, const xmlNode *xml)
1280 : {
1281 : pcmk_log_xml_as(__FILE__, __func__, __LINE__, 0, level, text, xml);
1282 : }
1283 :
1284 : // LCOV_EXCL_STOP
1285 : // End deprecated API
1286 :
1287 0 : void pcmk__set_config_error_handler(pcmk__config_error_func error_handler, void *error_context)
1288 : {
1289 0 : pcmk__config_error_handler = error_handler;
1290 0 : pcmk__config_error_context = error_context;
1291 0 : }
1292 :
1293 0 : void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler, void *warning_context)
1294 : {
1295 0 : pcmk__config_warning_handler = warning_handler;
1296 0 : pcmk__config_warning_context = warning_context;
1297 0 : }
|