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