
Use a static GString which will resize itself if the log message is bigger than the current string size, but will also ensure we don't do a ton of memory reallocation on every log message. Previously all log messages were trucated at 512 bytes due to the log buffer char array being 512 bytes long.
273 lines
7.0 KiB
C
273 lines
7.0 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details:
|
|
*
|
|
* Copyright (C) 2011 Red Hat, Inc.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <syslog.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ModemManager.h>
|
|
#include <mm-errors-types.h>
|
|
|
|
#if defined WITH_QMI
|
|
#include <libqmi-glib.h>
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
#include <libmbim-glib.h>
|
|
#endif
|
|
|
|
#include "mm-log.h"
|
|
|
|
enum {
|
|
TS_FLAG_NONE = 0,
|
|
TS_FLAG_WALL,
|
|
TS_FLAG_REL
|
|
};
|
|
|
|
static gboolean ts_flags = TS_FLAG_NONE;
|
|
static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR;
|
|
static GTimeVal rel_start = { 0, 0 };
|
|
static int logfd = -1;
|
|
static gboolean func_loc = FALSE;
|
|
|
|
typedef struct {
|
|
guint32 num;
|
|
const char *name;
|
|
} LogDesc;
|
|
|
|
static const LogDesc level_descs[] = {
|
|
{ LOGL_ERR, "ERR" },
|
|
{ LOGL_WARN | LOGL_ERR, "WARN" },
|
|
{ LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" },
|
|
{ LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
static GString *msgbuf = NULL;
|
|
static volatile gsize msgbuf_once = 0;
|
|
|
|
void
|
|
_mm_log (const char *loc,
|
|
const char *func,
|
|
guint32 level,
|
|
const char *fmt,
|
|
...)
|
|
{
|
|
va_list args;
|
|
GTimeVal tv;
|
|
int syslog_priority = LOG_INFO;
|
|
ssize_t ign;
|
|
|
|
if (!(log_level & level))
|
|
return;
|
|
|
|
if (g_once_init_enter (&msgbuf_once)) {
|
|
msgbuf = g_string_sized_new (512);
|
|
g_once_init_leave (&msgbuf_once, 1);
|
|
} else
|
|
g_string_truncate (msgbuf, 0);
|
|
|
|
if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG))
|
|
g_string_append (msgbuf, "<debug> ");
|
|
else if ((log_level & LOGL_INFO) && (level == LOGL_INFO))
|
|
g_string_append (msgbuf, "<info> ");
|
|
else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) {
|
|
g_string_append (msgbuf, "<warn> ");
|
|
syslog_priority = LOG_WARNING;
|
|
} else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) {
|
|
g_string_append (msgbuf, "<error> ");
|
|
syslog_priority = LOG_ERR;
|
|
} else
|
|
return;
|
|
|
|
if (ts_flags == TS_FLAG_WALL) {
|
|
g_get_current_time (&tv);
|
|
g_string_append_printf (msgbuf, "[%09ld.%06ld] ", tv.tv_sec, tv.tv_usec);
|
|
} else if (ts_flags == TS_FLAG_REL) {
|
|
glong secs;
|
|
glong usecs;
|
|
|
|
g_get_current_time (&tv);
|
|
secs = tv.tv_sec - rel_start.tv_sec;
|
|
usecs = tv.tv_usec - rel_start.tv_usec;
|
|
if (usecs < 0) {
|
|
secs--;
|
|
usecs += 1000000;
|
|
}
|
|
|
|
g_string_append_printf (msgbuf, "[%06ld.%06ld] ", secs, usecs);
|
|
}
|
|
|
|
if (func_loc && log_level & LOGL_DEBUG)
|
|
g_string_append_printf (msgbuf, "[%s] %s(): ", loc, func);
|
|
|
|
va_start (args, fmt);
|
|
g_string_append_vprintf (msgbuf, fmt, args);
|
|
va_end (args);
|
|
|
|
g_string_append_c (msgbuf, '\n');
|
|
|
|
if (logfd < 0)
|
|
syslog (syslog_priority, "%s", msgbuf->str);
|
|
else {
|
|
ign = write (logfd, msgbuf->str, msgbuf->len);
|
|
if (ign) {} /* whatever; really shut up about unused result */
|
|
|
|
fsync (logfd); /* Make sure output is dumped to disk immediately */
|
|
}
|
|
}
|
|
|
|
static void
|
|
log_handler (const gchar *log_domain,
|
|
GLogLevelFlags level,
|
|
const gchar *message,
|
|
gpointer ignored)
|
|
{
|
|
int syslog_priority;
|
|
ssize_t ign;
|
|
|
|
switch (level) {
|
|
case G_LOG_LEVEL_ERROR:
|
|
syslog_priority = LOG_CRIT;
|
|
break;
|
|
case G_LOG_LEVEL_CRITICAL:
|
|
syslog_priority = LOG_ERR;
|
|
break;
|
|
case G_LOG_LEVEL_WARNING:
|
|
syslog_priority = LOG_WARNING;
|
|
break;
|
|
case G_LOG_LEVEL_MESSAGE:
|
|
syslog_priority = LOG_NOTICE;
|
|
break;
|
|
case G_LOG_LEVEL_DEBUG:
|
|
syslog_priority = LOG_DEBUG;
|
|
break;
|
|
case G_LOG_LEVEL_INFO:
|
|
default:
|
|
syslog_priority = LOG_INFO;
|
|
break;
|
|
}
|
|
|
|
if (logfd < 0)
|
|
syslog (syslog_priority, "%s", message);
|
|
else {
|
|
ign = write (logfd, message, strlen (message));
|
|
if (ign) {} /* whatever; really shut up about unused result */
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
mm_log_set_level (const char *level, GError **error)
|
|
{
|
|
gboolean found = FALSE;
|
|
const LogDesc *diter;
|
|
|
|
for (diter = &level_descs[0]; diter->name; diter++) {
|
|
if (!strcasecmp (diter->name, level)) {
|
|
log_level = diter->num;
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
|
"Unknown log level '%s'", level);
|
|
|
|
#if defined WITH_QMI
|
|
qmi_utils_set_traces_enabled (log_level & LOGL_DEBUG ? TRUE : FALSE);
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
mbim_utils_set_traces_enabled (log_level & LOGL_DEBUG ? TRUE : FALSE);
|
|
#endif
|
|
|
|
return found;
|
|
}
|
|
|
|
gboolean
|
|
mm_log_setup (const char *level,
|
|
const char *log_file,
|
|
gboolean show_timestamps,
|
|
gboolean rel_timestamps,
|
|
gboolean debug_func_loc,
|
|
GError **error)
|
|
{
|
|
/* levels */
|
|
if (level && strlen (level) && !mm_log_set_level (level, error))
|
|
return FALSE;
|
|
|
|
func_loc = debug_func_loc;
|
|
|
|
if (show_timestamps)
|
|
ts_flags = TS_FLAG_WALL;
|
|
else if (rel_timestamps)
|
|
ts_flags = TS_FLAG_REL;
|
|
|
|
/* Grab start time for relative timestamps */
|
|
g_get_current_time (&rel_start);
|
|
|
|
if (log_file == NULL)
|
|
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON);
|
|
else {
|
|
logfd = open (log_file,
|
|
O_CREAT | O_APPEND | O_WRONLY,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
|
|
if (logfd < 0) {
|
|
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
|
"Couldn't open log file: (%d) %s",
|
|
errno, strerror (errno));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
g_log_set_handler (G_LOG_DOMAIN,
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
|
|
#if defined WITH_QMI
|
|
g_log_set_handler ("Qmi",
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
#endif
|
|
|
|
#if defined WITH_MBIM
|
|
g_log_set_handler ("Mbim",
|
|
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
|
|
log_handler,
|
|
NULL);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
mm_log_shutdown (void)
|
|
{
|
|
if (logfd < 0)
|
|
closelog ();
|
|
else
|
|
close (logfd);
|
|
}
|