Blame src/lib/logging.c

Packit 4f15d5
/*
Packit 4f15d5
    Copyright (C) 2010  ABRT team
Packit 4f15d5
    Copyright (C) 2010, 2014  RedHat Inc
Packit 4f15d5
Packit 4f15d5
    This program is free software; you can redistribute it and/or modify
Packit 4f15d5
    it under the terms of the GNU General Public License as published by
Packit 4f15d5
    the Free Software Foundation; either version 2 of the License, or
Packit 4f15d5
    (at your option) any later version.
Packit 4f15d5
Packit 4f15d5
    This program is distributed in the hope that it will be useful,
Packit 4f15d5
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4f15d5
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4f15d5
    GNU General Public License for more details.
Packit 4f15d5
Packit 4f15d5
    You should have received a copy of the GNU General Public License along
Packit 4f15d5
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit 4f15d5
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 4f15d5
*/
Packit 4f15d5
#include <syslog.h>
Packit 4f15d5
/* Suppress automatic CODE_* fields as we handle those here */
Packit 4f15d5
#define SD_JOURNAL_SUPPRESS_LOCATION
Packit 4f15d5
#include <systemd/sd-journal.h>
Packit 4f15d5
#include "internal_libreport.h"
Packit 4f15d5
Packit 4f15d5
void (*g_custom_logger)(const char*);
Packit 4f15d5
const char *msg_prefix = "";
Packit 4f15d5
const char *msg_eol = "\n";
Packit 4f15d5
int logmode = LOGMODE_STDIO;
Packit 4f15d5
int xfunc_error_retval = EXIT_FAILURE;
Packit 4f15d5
static enum libreport_diemode xfunc_diemode = DIEMODE_EXIT;
Packit 4f15d5
int g_verbose;
Packit 4f15d5
Packit 4f15d5
void set_xfunc_error_retval(int retval)
Packit 4f15d5
{
Packit 4f15d5
    xfunc_error_retval = retval;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void set_xfunc_diemode(enum libreport_diemode mode)
Packit 4f15d5
{
Packit 4f15d5
    xfunc_diemode = mode;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
/* [p]error_msg[_and_die] must be safe after fork in multi-threaded programs.
Packit 4f15d5
 * Therefore we avoid stdio, fflush(), and use _exit() instead of exit().
Packit 4f15d5
 *
Packit 4f15d5
 */
Packit 4f15d5
void xfunc_die(void)
Packit 4f15d5
{
Packit 4f15d5
    char *const envmode = getenv("LIBREPORT_DIEMODE");
Packit 4f15d5
    if (   xfunc_diemode == DIEMODE_ABORT
Packit 4f15d5
        || (envmode != NULL && strcmp("abort", envmode) == 0))
Packit 4f15d5
        abort();
Packit 4f15d5
Packit 4f15d5
    _exit(xfunc_error_retval);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
static bool should_log(int level)
Packit 4f15d5
{
Packit 4f15d5
    // LOG_DEBUG = 7, LOG_INFO = 6, LOG_NOTICE = 5, LOG_WARNING = 4, LOG_ERR = 3
Packit 4f15d5
    // output only messages with LOG_ERR by default, overridden by g_verbose
Packit 4f15d5
    if(
Packit 4f15d5
          (g_verbose == 0 && level <= LOG_WARNING) ||
Packit 4f15d5
          (g_verbose == 1 && level <= LOG_NOTICE) ||
Packit 4f15d5
          (g_verbose == 2 && level <= LOG_INFO) ||
Packit 4f15d5
          (g_verbose == 3)
Packit 4f15d5
      )
Packit 4f15d5
      return true;
Packit 4f15d5
Packit 4f15d5
    return false;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
static void log_handler(int level,
Packit 4f15d5
                        const char *format,
Packit 4f15d5
                        va_list p,
Packit 4f15d5
                        const char *strerr, /* perror messages */
Packit 4f15d5
                        int flags,
Packit 4f15d5
                        const char *file,
Packit 4f15d5
                        int line,
Packit 4f15d5
                        const char *func)
Packit 4f15d5
{
Packit 4f15d5
    if (!logmode || !should_log(level))
Packit 4f15d5
        return;
Packit 4f15d5
Packit 4f15d5
    /* This is ugly and costs +60 bytes compared to multiple
Packit 4f15d5
     * fprintf's, but is guaranteed to do a single write.
Packit 4f15d5
     * This is needed for e.g. when multiple children
Packit 4f15d5
     * can produce log messages simultaneously. */
Packit 4f15d5
Packit 4f15d5
    int prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
Packit 4f15d5
    int strerr_len = strerr ? strlen(strerr) : 0;
Packit 4f15d5
    int msgeol_len = strlen(msg_eol);
Packit 4f15d5
    int used;
Packit 4f15d5
Packit 4f15d5
    /* Try to format the message in a fixed buffer.
Packit 4f15d5
     * This eliminates malloc.
Packit 4f15d5
     * Main reason isn't the speed optimization, but to make
Packit 4f15d5
     * short logging safe after fork in multithreaded libraries.
Packit 4f15d5
     */
Packit 4f15d5
    char buf[1024];
Packit 4f15d5
    va_list p2;
Packit 4f15d5
    va_copy(p2, p);
Packit 4f15d5
    if (prefix_len < sizeof(buf))
Packit 4f15d5
        used = vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, format, p2);
Packit 4f15d5
    else
Packit 4f15d5
        used = vsnprintf(buf, 0, format, p2);
Packit 4f15d5
    va_end(p2);
Packit 4f15d5
Packit 4f15d5
    char *msg = buf;
Packit 4f15d5
Packit 4f15d5
    /* +3 is for ": " before strerr and for terminating NUL */
Packit 4f15d5
    unsigned total_len = prefix_len + used + strerr_len + msgeol_len + 3;
Packit 4f15d5
Packit 4f15d5
    if (total_len >= sizeof(buf))
Packit 4f15d5
    {
Packit 4f15d5
        msg = alloca(total_len);
Packit 4f15d5
        used = vsnprintf(msg + prefix_len, total_len - prefix_len, format, p);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (prefix_len) {
Packit 4f15d5
        char *p;
Packit 4f15d5
        used += prefix_len;
Packit 4f15d5
        p = stpcpy(msg, msg_prefix);
Packit 4f15d5
        p[0] = ':';
Packit 4f15d5
        p[1] = ' ';
Packit 4f15d5
    }
Packit 4f15d5
    if (strerr) {
Packit 4f15d5
        if (format[0]) {
Packit 4f15d5
            msg[used++] = ':';
Packit 4f15d5
            msg[used++] = ' ';
Packit 4f15d5
        }
Packit 4f15d5
        strcpy(&msg[used], strerr);
Packit 4f15d5
        used += strerr_len;
Packit 4f15d5
    }
Packit 4f15d5
    strcpy(&msg[used], msg_eol);
Packit 4f15d5
Packit 4f15d5
    if (flags & LOGMODE_STDIO) {
Packit 4f15d5
        full_write(STDERR_FILENO, msg, used + msgeol_len);
Packit 4f15d5
    }
Packit 4f15d5
    msg[used] = '\0'; /* remove msg_eol (usually "\n") */
Packit 4f15d5
    if (flags & LOGMODE_SYSLOG) {
Packit 4f15d5
        syslog(level, "%s", msg + prefix_len);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if ((flags & LOGMODE_CUSTOM) && g_custom_logger) {
Packit 4f15d5
        g_custom_logger(msg + prefix_len);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (flags & LOGMODE_JOURNAL) {
Packit 4f15d5
        sd_journal_send("MESSAGE=%s", msg + prefix_len,
Packit 4f15d5
                        "PRIORITY=%d", level,
Packit 4f15d5
                        "CODE_FILE=%s", file,
Packit 4f15d5
                        "CODE_LINE=%d", line,
Packit 4f15d5
                        "CODE_FUNC=%s", func,
Packit 4f15d5
                        "SYSLOG_FACILITY=1",
Packit 4f15d5
                        NULL);
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void log_wrapper(int level,
Packit 4f15d5
                 const char *file,
Packit 4f15d5
                 int line,
Packit 4f15d5
                 const char *func,
Packit 4f15d5
                 bool process_perror,
Packit 4f15d5
                 bool use_custom_logger,
Packit 4f15d5
                 const char *format,
Packit 4f15d5
                 ...)
Packit 4f15d5
{
Packit 4f15d5
    va_list p;
Packit 4f15d5
Packit 4f15d5
    va_start(p, format);
Packit 4f15d5
    log_handler(level,
Packit 4f15d5
                format,
Packit 4f15d5
                p,
Packit 4f15d5
                (process_perror && errno) ? strerror(errno) : NULL, /* Guard against "<error message>: Success" */
Packit 4f15d5
                logmode | (use_custom_logger ? LOGMODE_CUSTOM : 0),
Packit 4f15d5
                file,
Packit 4f15d5
                line,
Packit 4f15d5
                func);
Packit 4f15d5
    va_end(p);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void log_and_die_wrapper(int level,
Packit 4f15d5
                         const char *file,
Packit 4f15d5
                         int line,
Packit 4f15d5
                         const char *func,
Packit 4f15d5
                         bool process_perror,
Packit 4f15d5
                         bool use_custom_logger,
Packit 4f15d5
                         const char *format,
Packit 4f15d5
                         ...)
Packit 4f15d5
{
Packit 4f15d5
    va_list p;
Packit 4f15d5
Packit 4f15d5
    va_start(p, format);
Packit 4f15d5
    log_handler(level,
Packit 4f15d5
                format,
Packit 4f15d5
                p,
Packit 4f15d5
                (process_perror && errno) ? strerror(errno) : NULL, /* Guard against "<error message>: Success" */
Packit 4f15d5
                logmode | (use_custom_logger ? LOGMODE_CUSTOM : 0),
Packit 4f15d5
                file,
Packit 4f15d5
                line,
Packit 4f15d5
                func);
Packit 4f15d5
    va_end(p);
Packit 4f15d5
    xfunc_die();
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
void die_out_of_memory(void)
Packit 4f15d5
{
Packit 4f15d5
    error_msg_and_die("Out of memory, exiting");
Packit 4f15d5
}