mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-04-28 12:37:11 +00:00
839 lines
20 KiB
C
839 lines
20 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE 1
|
|
#endif
|
|
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <pthread.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
|
|
#include "caps.h"
|
|
#include "config.h"
|
|
#include "file_utils.h"
|
|
#include "log.h"
|
|
#include "lxccontainer.h"
|
|
#include "utils.h"
|
|
|
|
#ifndef HAVE_STRLCPY
|
|
#include "include/strlcpy.h"
|
|
#endif
|
|
|
|
#if HAVE_DLOG
|
|
#include <dlog.h>
|
|
|
|
#undef LOG_TAG
|
|
#define LOG_TAG "LXC"
|
|
#endif
|
|
|
|
/* We're logging in seconds and nanoseconds. Assuming that the underlying
|
|
* datatype is currently at maximum a 64bit integer, we have a date string that
|
|
* is of maximum length (2^64 - 1) * 2 = (21 + 21) = 42.
|
|
*/
|
|
#define LXC_LOG_TIME_SIZE ((INTTYPE_TO_STRLEN(uint64_t)) * 2)
|
|
|
|
int lxc_log_fd = -1;
|
|
static int syslog_enable = 0;
|
|
int lxc_quiet_specified;
|
|
int lxc_log_use_global_fd;
|
|
static int lxc_loglevel_specified;
|
|
|
|
static char log_prefix[LXC_LOG_PREFIX_SIZE] = "lxc";
|
|
static char *log_fname = NULL;
|
|
static char *log_vmname = NULL;
|
|
|
|
lxc_log_define(log, lxc);
|
|
|
|
static int lxc_log_priority_to_syslog(int priority)
|
|
{
|
|
switch (priority) {
|
|
case LXC_LOG_LEVEL_FATAL:
|
|
return LOG_EMERG;
|
|
case LXC_LOG_LEVEL_ALERT:
|
|
return LOG_ALERT;
|
|
case LXC_LOG_LEVEL_CRIT:
|
|
return LOG_CRIT;
|
|
case LXC_LOG_LEVEL_ERROR:
|
|
return LOG_ERR;
|
|
case LXC_LOG_LEVEL_WARN:
|
|
return LOG_WARNING;
|
|
case LXC_LOG_LEVEL_NOTICE:
|
|
case LXC_LOG_LEVEL_NOTSET:
|
|
return LOG_NOTICE;
|
|
case LXC_LOG_LEVEL_INFO:
|
|
return LOG_INFO;
|
|
case LXC_LOG_LEVEL_TRACE:
|
|
case LXC_LOG_LEVEL_DEBUG:
|
|
return LOG_DEBUG;
|
|
}
|
|
|
|
/* Not reached */
|
|
return LOG_NOTICE;
|
|
}
|
|
|
|
static const char *lxc_log_get_container_name()
|
|
{
|
|
#ifndef NO_LXC_CONF
|
|
if (current_config && !log_vmname)
|
|
return current_config->name;
|
|
#endif
|
|
|
|
return log_vmname;
|
|
}
|
|
|
|
static char *lxc_log_get_va_msg(struct lxc_log_event *event)
|
|
{
|
|
char *msg;
|
|
int rc, len;
|
|
va_list args;
|
|
|
|
if (!event)
|
|
return NULL;
|
|
|
|
va_copy(args, *event->vap);
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
len = vsnprintf(NULL, 0, event->fmt, args) + 1;
|
|
#pragma GCC diagnostic pop
|
|
va_end(args);
|
|
|
|
msg = malloc(len * sizeof(char));
|
|
if (!msg)
|
|
return NULL;
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
rc = vsnprintf(msg, len, event->fmt, *event->vap);
|
|
#pragma GCC diagnostic pop
|
|
if (rc == -1 || rc >= len) {
|
|
free(msg);
|
|
return NULL;
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int log_append_syslog(const struct lxc_log_appender *appender,
|
|
struct lxc_log_event *event)
|
|
{
|
|
char *msg;
|
|
const char *log_container_name;
|
|
|
|
if (!syslog_enable)
|
|
return 0;
|
|
|
|
log_container_name = lxc_log_get_container_name();
|
|
|
|
msg = lxc_log_get_va_msg(event);
|
|
if (!msg)
|
|
return 0;
|
|
|
|
syslog(lxc_log_priority_to_syslog(event->priority),
|
|
"%s%s %s - %s:%s:%d - %s" ,
|
|
log_container_name ? log_container_name : "",
|
|
log_container_name ? ":" : "",
|
|
event->category,
|
|
event->locinfo->file, event->locinfo->func,
|
|
event->locinfo->line,
|
|
msg);
|
|
free(msg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int log_append_stderr(const struct lxc_log_appender *appender,
|
|
struct lxc_log_event *event)
|
|
{
|
|
const char *log_container_name;
|
|
|
|
if (event->priority < LXC_LOG_LEVEL_ERROR)
|
|
return 0;
|
|
|
|
log_container_name = lxc_log_get_container_name();
|
|
|
|
fprintf(stderr, "%s: %s%s", log_prefix,
|
|
log_container_name ? log_container_name : "",
|
|
log_container_name ? ": " : "");
|
|
fprintf(stderr, "%s: %s: %d ", event->locinfo->file,
|
|
event->locinfo->func, event->locinfo->line);
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
vfprintf(stderr, event->fmt, *event->vap);
|
|
#pragma GCC diagnostic pop
|
|
fprintf(stderr, "\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int lxc_unix_epoch_to_utc(char *buf, size_t bufsize, const struct timespec *time)
|
|
{
|
|
int64_t epoch_to_days, z, era, doe, yoe, year, doy, mp, day, month,
|
|
d_in_s, hours, h_in_s, minutes, seconds;
|
|
char nanosec[INTTYPE_TO_STRLEN(int64_t)];
|
|
int ret;
|
|
|
|
/* See https://howardhinnant.github.io/date_algorithms.html for an
|
|
* explanation of the algorithm used here.
|
|
*/
|
|
|
|
/* Convert Epoch in seconds to number of days. */
|
|
epoch_to_days = time->tv_sec / 86400;
|
|
|
|
/* Shift the Epoch from 1970-01-01 to 0000-03-01. */
|
|
z = epoch_to_days + 719468;
|
|
|
|
/* compute the era from the serial date by simply dividing by the number
|
|
* of days in an era (146097).
|
|
*/
|
|
era = (z >= 0 ? z : z - 146096) / 146097;
|
|
|
|
/* The day-of-era (doe) can then be found by subtracting the era number
|
|
* times the number of days per era, from the serial date.
|
|
*/
|
|
doe = (z - era * 146097);
|
|
|
|
/* From the day-of-era (doe), the year-of-era (yoe, range [0, 399]) can
|
|
* be computed.
|
|
*/
|
|
yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
|
|
/* Given year-of-era, and era, one can now compute the year. */
|
|
year = yoe + era * 400;
|
|
|
|
/* Also the day-of-year, again with the year beginning on Mar. 1, can be
|
|
* computed from the day-of-era and year-of-era.
|
|
*/
|
|
doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
|
|
/* Given day-of-year, find the month number. */
|
|
mp = (5 * doy + 2) / 153;
|
|
|
|
/* From day-of-year and month-of-year we can now easily compute
|
|
* day-of-month.
|
|
*/
|
|
day = doy - (153 * mp + 2) / 5 + 1;
|
|
|
|
/* Transform the month number from the [0, 11] / [Mar, Feb] system to
|
|
* the civil system: [1, 12] to find the correct month.
|
|
*/
|
|
month = mp + (mp < 10 ? 3 : -9);
|
|
|
|
/* The algorithm assumes that a year begins on 1 March, so add 1 before
|
|
* that. */
|
|
if (month < 3)
|
|
year++;
|
|
|
|
/* Transform days in the epoch to seconds. */
|
|
d_in_s = epoch_to_days * 86400;
|
|
|
|
/* To find the current hour simply substract the Epoch_to_days from the
|
|
* total Epoch and divide by the number of seconds in an hour.
|
|
*/
|
|
hours = (time->tv_sec - d_in_s) / 3600;
|
|
|
|
/* Transform hours to seconds. */
|
|
h_in_s = hours * 3600;
|
|
|
|
/* Calculate minutes by subtracting the seconds for all days in the
|
|
* epoch and for all hours in the epoch and divide by the number of
|
|
* minutes in an hour.
|
|
*/
|
|
minutes = (time->tv_sec - d_in_s - h_in_s) / 60;
|
|
|
|
/* Calculate the seconds by subtracting the seconds for all days in the
|
|
* epoch, hours in the epoch and minutes in the epoch.
|
|
*/
|
|
seconds = (time->tv_sec - d_in_s - h_in_s - (minutes * 60));
|
|
|
|
/* Make string from nanoseconds. */
|
|
ret = snprintf(nanosec, sizeof(nanosec), "%"PRId64, (int64_t)time->tv_nsec);
|
|
if (ret < 0 || ret >= sizeof(nanosec))
|
|
return -1;
|
|
|
|
/* Create final timestamp for the log and shorten nanoseconds to 3
|
|
* digit precision.
|
|
*/
|
|
ret = snprintf(buf, bufsize,
|
|
"%" PRId64 "%02" PRId64 "%02" PRId64 "%02" PRId64
|
|
"%02" PRId64 "%02" PRId64 ".%.3s",
|
|
year, month, day, hours, minutes, seconds, nanosec);
|
|
if (ret < 0 || (size_t)ret >= bufsize)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This function needs to make extra sure that it is thread-safe. We had some
|
|
* problems with that before. This especially involves time-conversion
|
|
* functions. I don't want to find any localtime() or gmtime() functions or
|
|
* relatives in here. Not even localtime_r() or gmtime_r() or relatives. They
|
|
* all fiddle with global variables and locking in various libcs. They cause
|
|
* deadlocks when liblxc is used multi-threaded and no matter how smart you
|
|
* think you are, you __will__ cause trouble using them.
|
|
* (As a short example how this can cause trouble: LXD uses forkstart to fork
|
|
* off a new process that runs the container. At the same time the go runtime
|
|
* LXD relies on does its own multi-threading thing which we can't control. The
|
|
* fork()ing + threading then seems to mess with the locking states in these
|
|
* time functions causing deadlocks.)
|
|
* The current solution is to be good old unix people and use the Epoch as our
|
|
* reference point and simply use the seconds and nanoseconds that have past
|
|
* since then. This relies on clock_gettime() which is explicitly marked MT-Safe
|
|
* with no restrictions! This way, anyone who is really strongly invested in
|
|
* getting the actual time the log entry was created, can just convert it for
|
|
* themselves. Our logging is mostly done for debugging purposes so don't try
|
|
* to make it pretty. Pretty might cost you thread-safety.
|
|
*/
|
|
static int log_append_logfile(const struct lxc_log_appender *appender,
|
|
struct lxc_log_event *event)
|
|
{
|
|
char buffer[LXC_LOG_BUFFER_SIZE];
|
|
char date_time[LXC_LOG_TIME_SIZE];
|
|
int n;
|
|
ssize_t ret;
|
|
int fd_to_use = -1;
|
|
const char *log_container_name;
|
|
|
|
#ifndef NO_LXC_CONF
|
|
if (current_config)
|
|
if (!lxc_log_use_global_fd)
|
|
fd_to_use = current_config->logfd;
|
|
#endif
|
|
|
|
log_container_name = lxc_log_get_container_name();
|
|
|
|
if (fd_to_use == -1)
|
|
fd_to_use = lxc_log_fd;
|
|
|
|
if (fd_to_use == -1)
|
|
return 0;
|
|
|
|
if (lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp) < 0)
|
|
return -1;
|
|
|
|
n = snprintf(buffer, sizeof(buffer),
|
|
"%s%s%s %s %-8s %s - %s:%s:%d - ",
|
|
log_prefix,
|
|
log_container_name ? " " : "",
|
|
log_container_name ? log_container_name : "",
|
|
date_time,
|
|
lxc_log_priority_to_string(event->priority),
|
|
event->category,
|
|
event->locinfo->file, event->locinfo->func,
|
|
event->locinfo->line);
|
|
if (n < 0)
|
|
return n;
|
|
|
|
if ((size_t)n < STRARRAYLEN(buffer)) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
ret = vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap);
|
|
#pragma GCC diagnostic pop
|
|
if (ret < 0)
|
|
return 0;
|
|
|
|
n += ret;
|
|
}
|
|
|
|
if ((size_t)n >= sizeof(buffer))
|
|
n = STRARRAYLEN(buffer);
|
|
|
|
buffer[n] = '\n';
|
|
|
|
return lxc_write_nointr(fd_to_use, buffer, n + 1);
|
|
}
|
|
|
|
#if HAVE_DLOG
|
|
static int log_append_dlog(const struct lxc_log_appender *appender,
|
|
struct lxc_log_event *event)
|
|
{
|
|
char *msg = lxc_log_get_va_msg(event);
|
|
const char *log_container_name = lxc_log_get_container_name();
|
|
|
|
switch (event->priority) {
|
|
case LXC_LOG_LEVEL_TRACE:
|
|
case LXC_LOG_LEVEL_DEBUG:
|
|
print_log(DLOG_DEBUG, LOG_TAG, "%s: %s(%d) > [%s] %s",
|
|
event->locinfo->file, event->locinfo->func, event->locinfo->line,
|
|
log_container_name ? log_container_name : "-",
|
|
msg ? msg : "-");
|
|
break;
|
|
case LXC_LOG_LEVEL_INFO:
|
|
print_log(DLOG_INFO, LOG_TAG, "%s: %s(%d) > [%s] %s",
|
|
event->locinfo->file, event->locinfo->func, event->locinfo->line,
|
|
log_container_name ? log_container_name : "-",
|
|
msg ? msg : "-");
|
|
break;
|
|
case LXC_LOG_LEVEL_NOTICE:
|
|
case LXC_LOG_LEVEL_WARN:
|
|
print_log(DLOG_WARN, LOG_TAG, "%s: %s(%d) > [%s] %s",
|
|
event->locinfo->file, event->locinfo->func, event->locinfo->line,
|
|
log_container_name ? log_container_name : "-",
|
|
msg ? msg : "-");
|
|
break;
|
|
case LXC_LOG_LEVEL_ERROR:
|
|
print_log(DLOG_ERROR, LOG_TAG, "%s: %s(%d) > [%s] %s",
|
|
event->locinfo->file, event->locinfo->func, event->locinfo->line,
|
|
log_container_name ? log_container_name : "-",
|
|
msg ? msg : "-");
|
|
break;
|
|
case LXC_LOG_LEVEL_CRIT:
|
|
case LXC_LOG_LEVEL_ALERT:
|
|
case LXC_LOG_LEVEL_FATAL:
|
|
print_log(DLOG_FATAL, LOG_TAG, "%s: %s(%d) > [%s] %s",
|
|
event->locinfo->file, event->locinfo->func, event->locinfo->line,
|
|
log_container_name ? log_container_name : "-",
|
|
msg ? msg : "-");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
free(msg);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static struct lxc_log_appender log_appender_syslog = {
|
|
.name = "syslog",
|
|
.append = log_append_syslog,
|
|
.next = NULL,
|
|
};
|
|
|
|
static struct lxc_log_appender log_appender_stderr = {
|
|
.name = "stderr",
|
|
.append = log_append_stderr,
|
|
.next = NULL,
|
|
};
|
|
|
|
static struct lxc_log_appender log_appender_logfile = {
|
|
.name = "logfile",
|
|
.append = log_append_logfile,
|
|
.next = NULL,
|
|
};
|
|
|
|
#if HAVE_DLOG
|
|
static struct lxc_log_appender log_appender_dlog = {
|
|
.name = "dlog",
|
|
.append = log_append_dlog,
|
|
.next = NULL,
|
|
};
|
|
#endif
|
|
|
|
static struct lxc_log_category log_root = {
|
|
.name = "root",
|
|
.priority = LXC_LOG_LEVEL_ERROR,
|
|
.appender = NULL,
|
|
.parent = NULL,
|
|
};
|
|
|
|
#if HAVE_DLOG
|
|
struct lxc_log_category lxc_log_category_lxc = {
|
|
.name = "lxc",
|
|
.priority = LXC_LOG_LEVEL_TRACE,
|
|
.appender = &log_appender_dlog,
|
|
.parent = &log_root
|
|
};
|
|
#else
|
|
struct lxc_log_category lxc_log_category_lxc = {
|
|
.name = "lxc",
|
|
.priority = LXC_LOG_LEVEL_ERROR,
|
|
.appender = &log_appender_logfile,
|
|
.parent = &log_root
|
|
};
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int build_dir(const char *name)
|
|
{
|
|
char *e, *n, *p;
|
|
|
|
/* Make copy of the string since we'll be modifying it. */
|
|
n = strdup(name);
|
|
if (!n)
|
|
return -1;
|
|
|
|
e = &n[strlen(n)];
|
|
for (p = n + 1; p < e; p++) {
|
|
int ret;
|
|
|
|
if (*p != '/')
|
|
continue;
|
|
*p = '\0';
|
|
|
|
ret = lxc_unpriv(mkdir(n, 0755));
|
|
if (ret && errno != EEXIST) {
|
|
SYSERROR("Failed to create directory \"%s\"", n);
|
|
free(n);
|
|
return -1;
|
|
}
|
|
|
|
*p = '/';
|
|
}
|
|
|
|
free(n);
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static int log_open(const char *name)
|
|
{
|
|
int fd;
|
|
int newfd;
|
|
|
|
fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0660));
|
|
if (fd < 0) {
|
|
SYSERROR("Failed to open log file \"%s\"", name);
|
|
return -1;
|
|
}
|
|
|
|
if (fd > 2)
|
|
return fd;
|
|
|
|
newfd = fcntl(fd, F_DUPFD_CLOEXEC, STDERR_FILENO);
|
|
if (newfd == -1)
|
|
SYSERROR("Failed to dup log fd %d", fd);
|
|
|
|
close(fd);
|
|
return newfd;
|
|
}
|
|
|
|
/*
|
|
* Build the path to the log file
|
|
* @name : the name of the container
|
|
* @lxcpath : the lxcpath to use as a basename or NULL to use LOGPATH
|
|
* Returns malloced path on success, or NULL on failure
|
|
*/
|
|
static char *build_log_path(const char *name, const char *lxcpath)
|
|
{
|
|
char *p;
|
|
int ret;
|
|
size_t len;
|
|
bool use_dir;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
#if USE_CONFIGPATH_LOGS
|
|
use_dir = true;
|
|
#else
|
|
use_dir = false;
|
|
#endif
|
|
|
|
/*
|
|
* If USE_CONFIGPATH_LOGS is true or lxcpath is given, the resulting
|
|
* path will be:
|
|
* '$logpath' + '/' + '$name' + '/' + '$name' + '.log' + '\0'
|
|
*
|
|
* If USE_CONFIGPATH_LOGS is false the resulting path will be:
|
|
* '$logpath' + '/' + '$name' + '.log' + '\0'
|
|
*/
|
|
len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */
|
|
if (lxcpath)
|
|
use_dir = true;
|
|
else
|
|
lxcpath = LOGPATH;
|
|
|
|
if (use_dir)
|
|
len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */
|
|
else
|
|
len += strlen(lxcpath) + 1;
|
|
|
|
p = malloc(len);
|
|
if (!p)
|
|
return p;
|
|
|
|
if (use_dir)
|
|
ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name);
|
|
else
|
|
ret = snprintf(p, len, "%s/%s.log", lxcpath, name);
|
|
if (ret < 0 || (size_t)ret >= len) {
|
|
free(p);
|
|
return NULL;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* This can be called:
|
|
* 1. when a program calls lxc_log_init with no logfile parameter (in which
|
|
* case the default is used). In this case lxc.loge can override this.
|
|
* 2. when a program calls lxc_log_init with a logfile parameter. In this
|
|
* case we don't want lxc.log to override this.
|
|
* 3. When a lxc.log entry is found in config file.
|
|
*/
|
|
static int __lxc_log_set_file(const char *fname, int create_dirs)
|
|
{
|
|
/* we are overriding the default. */
|
|
if (lxc_log_fd != -1)
|
|
lxc_log_close();
|
|
|
|
if (!fname)
|
|
return -1;
|
|
|
|
if (strlen(fname) == 0) {
|
|
log_fname = NULL;
|
|
return -1;
|
|
}
|
|
|
|
#if USE_CONFIGPATH_LOGS
|
|
/* We don't build_dir for the default if the default is i.e.
|
|
* /var/lib/lxc/$container/$container.log.
|
|
*/
|
|
if (create_dirs)
|
|
#endif
|
|
if (build_dir(fname)) {
|
|
SYSERROR("Failed to create dir for log file \"%s\"", fname);
|
|
return -1;
|
|
}
|
|
|
|
lxc_log_fd = log_open(fname);
|
|
if (lxc_log_fd == -1)
|
|
return -1;
|
|
|
|
log_fname = strdup(fname);
|
|
return 0;
|
|
}
|
|
|
|
static int _lxc_log_set_file(const char *name, const char *lxcpath, int create_dirs)
|
|
{
|
|
char *logfile;
|
|
int ret;
|
|
|
|
logfile = build_log_path(name, lxcpath);
|
|
if (!logfile) {
|
|
ERROR("Could not build log path");
|
|
return -1;
|
|
}
|
|
|
|
ret = __lxc_log_set_file(logfile, create_dirs);
|
|
free(logfile);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* lxc_log_init:
|
|
* Called from lxc front-end programs (like lxc-create, lxc-start) to
|
|
* initialize the log defaults.
|
|
*/
|
|
int lxc_log_init(struct lxc_log *log)
|
|
{
|
|
int ret;
|
|
int lxc_priority = LXC_LOG_LEVEL_ERROR;
|
|
|
|
if (!log)
|
|
return -1;
|
|
|
|
if (lxc_log_fd != -1) {
|
|
WARN("Log already initialized");
|
|
return 0;
|
|
}
|
|
|
|
if (log->level)
|
|
lxc_priority = lxc_log_priority_to_int(log->level);
|
|
|
|
if (!lxc_loglevel_specified) {
|
|
lxc_log_category_lxc.priority = lxc_priority;
|
|
lxc_loglevel_specified = 1;
|
|
}
|
|
|
|
if (!lxc_quiet_specified)
|
|
if (!log->quiet)
|
|
lxc_log_category_lxc.appender->next = &log_appender_stderr;
|
|
|
|
if (log->prefix)
|
|
lxc_log_set_prefix(log->prefix);
|
|
|
|
if (log->name)
|
|
log_vmname = strdup(log->name);
|
|
|
|
if (log->file) {
|
|
if (strcmp(log->file, "none") == 0)
|
|
return 0;
|
|
|
|
ret = __lxc_log_set_file(log->file, 1);
|
|
if (ret < 0) {
|
|
ERROR("Failed to enable logfile");
|
|
return -1;
|
|
}
|
|
|
|
lxc_log_use_global_fd = 1;
|
|
} else {
|
|
/* if no name was specified, there nothing to do */
|
|
if (!log->name)
|
|
return 0;
|
|
|
|
ret = -1;
|
|
|
|
if (!log->lxcpath)
|
|
log->lxcpath = LOGPATH;
|
|
|
|
/* try LOGPATH if lxcpath is the default for the privileged containers */
|
|
if (!geteuid() && strcmp(LXCPATH, log->lxcpath) == 0)
|
|
ret = _lxc_log_set_file(log->name, NULL, 0);
|
|
|
|
/* try in lxcpath */
|
|
if (ret < 0)
|
|
ret = _lxc_log_set_file(log->name, log->lxcpath, 1);
|
|
|
|
/* try LOGPATH in case its writable by the caller */
|
|
if (ret < 0)
|
|
ret = _lxc_log_set_file(log->name, NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* If !file, that is, if the user did not request this logpath, then
|
|
* ignore failures and continue logging to console
|
|
*/
|
|
if (!log->file && ret != 0) {
|
|
INFO("Ignoring failure to open default logfile");
|
|
ret = 0;
|
|
}
|
|
|
|
if (lxc_log_fd != -1) {
|
|
lxc_log_category_lxc.appender = &log_appender_logfile;
|
|
lxc_log_category_lxc.appender->next = &log_appender_stderr;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void lxc_log_close(void)
|
|
{
|
|
closelog();
|
|
|
|
free(log_vmname);
|
|
log_vmname = NULL;
|
|
|
|
if (lxc_log_fd == -1)
|
|
return;
|
|
|
|
close(lxc_log_fd);
|
|
lxc_log_fd = -1;
|
|
|
|
free(log_fname);
|
|
log_fname = NULL;
|
|
}
|
|
|
|
int lxc_log_syslog(int facility)
|
|
{
|
|
struct lxc_log_appender *appender;
|
|
|
|
openlog(log_prefix, LOG_PID, facility);
|
|
if (!lxc_log_category_lxc.appender) {
|
|
lxc_log_category_lxc.appender = &log_appender_syslog;
|
|
return 0;
|
|
}
|
|
|
|
appender = lxc_log_category_lxc.appender;
|
|
/* Check if syslog was already added, to avoid creating a loop */
|
|
while (appender) {
|
|
if (appender == &log_appender_syslog) {
|
|
/* not an error: openlog re-opened the connection */
|
|
return 0;
|
|
}
|
|
appender = appender->next;
|
|
}
|
|
|
|
appender = lxc_log_category_lxc.appender;
|
|
while (appender->next != NULL)
|
|
appender = appender->next;
|
|
appender->next = &log_appender_syslog;
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline void lxc_log_enable_syslog(void)
|
|
{
|
|
syslog_enable = 1;
|
|
}
|
|
|
|
/*
|
|
* This is called when we read a lxc.log.level entry in a lxc.conf file. This
|
|
* happens after processing command line arguments, which override the .conf
|
|
* settings. So only set the level if previously unset.
|
|
*/
|
|
int lxc_log_set_level(int *dest, int level)
|
|
{
|
|
if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET) {
|
|
ERROR("Invalid log priority %d", level);
|
|
return -1;
|
|
}
|
|
|
|
*dest = level;
|
|
return 0;
|
|
}
|
|
|
|
inline int lxc_log_get_level(void)
|
|
{
|
|
return lxc_log_category_lxc.priority;
|
|
}
|
|
|
|
bool lxc_log_has_valid_level(void)
|
|
{
|
|
int log_level;
|
|
|
|
log_level = lxc_log_get_level();
|
|
if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* This is called when we read a lxc.logfile entry in a lxc.conf file. This
|
|
* happens after processing command line arguments, which override the .conf
|
|
* settings. So only set the file if previously unset.
|
|
*/
|
|
int lxc_log_set_file(int *fd, const char *fname)
|
|
{
|
|
if (*fd >= 0) {
|
|
close(*fd);
|
|
*fd = -1;
|
|
}
|
|
|
|
if (build_dir(fname))
|
|
return -1;
|
|
|
|
*fd = log_open(fname);
|
|
if (*fd < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline const char *lxc_log_get_file(void)
|
|
{
|
|
return log_fname;
|
|
}
|
|
|
|
inline void lxc_log_set_prefix(const char *prefix)
|
|
{
|
|
/* We don't care if the prefix is truncated. */
|
|
(void)strlcpy(log_prefix, prefix, sizeof(log_prefix));
|
|
}
|
|
|
|
inline const char *lxc_log_get_prefix(void)
|
|
{
|
|
return log_prefix;
|
|
}
|
|
|
|
inline void lxc_log_options_no_override()
|
|
{
|
|
lxc_quiet_specified = 1;
|
|
lxc_loglevel_specified = 1;
|
|
}
|