LCOV - code coverage report
Current view: top level - common - logging.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 495 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 41 0.0 %

          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 : }

Generated by: LCOV version 1.14