root/lib/common/logging.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. crm_glib_handler
  2. crm_trigger_blackbox
  3. crm_log_deinit
  4. set_format_string
  5. logfile_disabled
  6. chown_logfile
  7. chmod_logfile
  8. set_logfile_permissions
  9. enable_logfile
  10. disable_logfile
  11. setenv_logfile
  12. pcmk__add_logfile
  13. pcmk__add_logfiles
  14. blackbox_logger
  15. crm_control_blackbox
  16. crm_enable_blackbox
  17. crm_disable_blackbox
  18. crm_write_blackbox
  19. crm_quark_to_string
  20. crm_log_filter_source
  21. strchrnul
  22. crm_log_filter
  23. crm_is_callsite_active
  24. crm_update_callsites
  25. crm_tracing_enabled
  26. crm_priority2int
  27. set_identity
  28. crm_log_preinit
  29. crm_log_init
  30. set_crm_log_level
  31. crm_enable_stderr
  32. crm_bump_log_level
  33. get_crm_log_level
  34. crm_log_args
  35. crm_log_output_fn
  36. pcmk__cli_init_logging
  37. pcmk_log_xml_as
  38. pcmk__log_xml_changes_as
  39. pcmk__log_xml_patchset_as
  40. pcmk__free_common_logger
  41. pcmk__set_config_error_handler
  42. pcmk__set_config_warning_handler

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

/* [previous][next][first][last][top][bottom][index][help] */