* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define _GNU_SOURCE
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
-#include <stdint.h>
-#include <stdio.h>
#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
#include <pthread.h>
-#include <time.h>
-
-#include <syslog.h>
+#include <stdint.h>
#include <stdio.h>
-
-#include <fcntl.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
-#include "log.h"
#include "caps.h"
-#include "utils.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 ((LXC_NUMSTRLEN64)*2)
+#define LXC_LOG_TIME_SIZE ((INTTYPE_TO_STRLEN(uint64_t)) * 2)
int lxc_log_fd = -1;
static int syslog_enable = 0;
static char *log_fname = NULL;
static char *log_vmname = NULL;
-lxc_log_define(lxc_log, lxc);
+lxc_log_define(log, lxc);
static int lxc_log_priority_to_syslog(int priority)
{
return LOG_NOTICE;
}
-/*---------------------------------------------------------------------------*/
-static int log_append_syslog(const struct lxc_log_appender *appender,
- struct lxc_log_event *event)
+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 (!syslog_enable)
- return 0;
+ 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 == NULL)
- return 0;
+ 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 0;
+ 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_vmname ? log_vmname : "",
- log_vmname ? ":" : "",
- event->category,
- event->locinfo->file, event->locinfo->func,
- event->locinfo->line,
- msg);
+ "%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;
- fprintf(stderr, "%s: %s%s", log_prefix, log_vmname ? log_vmname : "", log_vmname ? ": " : "");
- fprintf(stderr, "%s: %s: %d ", event->locinfo->file, event->locinfo->func, event->locinfo->line);
+ 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;
}
/*---------------------------------------------------------------------------*/
-int lxc_unix_epoch_to_utc(char *buf, size_t bufsize, const struct timespec *time)
+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[LXC_NUMSTRLEN64];
+ char nanosec[INTTYPE_TO_STRLEN(int64_t)];
int ret;
/* See https://howardhinnant.github.io/date_algorithms.html for an
/* Transform hours to seconds. */
h_in_s = hours * 3600;
- /* Calculate minutes by substracting the seconds for all days in the
+ /* 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 substracting the seconds for all days in the
+ /* 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, LXC_NUMSTRLEN64, "%ld", time->tv_nsec);
- if (ret < 0 || ret >= LXC_NUMSTRLEN64)
+ 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
* 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 controll. The
+ * 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
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 (!lxc_log_use_global_fd && current_config)
- fd_to_use = current_config->logfd;
+ 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;
return 0;
if (lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp) < 0)
- return 0;
+ return -1;
n = snprintf(buffer, sizeof(buffer),
- "%15s%s%s %s %-8s %s - %s:%s:%d - ",
- log_prefix,
- log_vmname ? " " : "",
- log_vmname ? log_vmname : "",
- date_time,
- lxc_log_priority_to_string(event->priority),
- event->category,
- event->locinfo->file, event->locinfo->func,
- event->locinfo->line);
-
+ "%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 < (sizeof(buffer) - 1))
- n += vsnprintf(buffer + n, sizeof(buffer) - n, event->fmt, *event->vap);
- else
- n = sizeof(buffer) - 1;
+ 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 write(fd_to_use, buffer, n + 1);
+ 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,
};
+#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,
.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 *n = strdup(name); // because we'll be modifying it
- char *p, *e;
- int ret;
+ char *e, *n, *p;
- if (!n) {
- ERROR("Out of memory while creating directory '%s'.", name);
+ /* 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++) {
+ for (p = n + 1; p < e; p++) {
+ int ret;
+
if (*p != '/')
continue;
*p = '\0';
- if (access(n, F_OK)) {
- ret = lxc_unpriv(mkdir(n, 0755));
- if (ret && errno != EEXIST) {
- SYSERROR("failed to create directory '%s'.", n);
- free(n);
- return -1;
- }
+
+ 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;
}
int fd;
int newfd;
- fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY |
- O_APPEND | O_CLOEXEC, 0666));
- if (fd == -1) {
- ERROR("failed to open log file \"%s\" : %s", name,
- strerror(errno));
+ 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, 3);
+ newfd = fcntl(fd, F_DUPFD_CLOEXEC, STDERR_FILENO);
if (newfd == -1)
- ERROR("failed to dup log fd %d : %s", fd, strerror(errno));
+ SYSERROR("Failed to dup log fd %d", fd);
close(fd);
return newfd;
static char *build_log_path(const char *name, const char *lxcpath)
{
char *p;
- int len, ret, use_dir;
+ int ret;
+ size_t len;
+ bool use_dir;
if (!name)
return NULL;
#if USE_CONFIGPATH_LOGS
- use_dir = 1;
+ use_dir = true;
#else
- use_dir = 0;
+ use_dir = false;
#endif
/*
*/
len = strlen(name) + 6; /* 6 == '/' + '.log' + '\0' */
if (lxcpath)
- use_dir = 1;
+ use_dir = true;
else
lxcpath = LOGPATH;
len += strlen(lxcpath) + 1 + strlen(name) + 1; /* add "/$container_name/" */
else
len += strlen(lxcpath) + 1;
+
p = malloc(len);
if (!p)
return p;
ret = snprintf(p, len, "%s/%s/%s.log", lxcpath, name, name);
else
ret = snprintf(p, len, "%s/%s.log", lxcpath, name);
-
- if (ret < 0 || ret >= len) {
+ if (ret < 0 || (size_t)ret >= len) {
free(p);
return NULL;
}
- return p;
-}
-extern 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;
+ 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.logfile can override this.
+ * 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.logfile to override this.
- * 3. When a lxc.logfile entry is found in config file.
+ * 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)
{
- if (lxc_log_fd != -1) {
- // we are overriding the default.
+ /* 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 0;
+ 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
+ /* 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)) {
- ERROR("failed to create dir for log file \"%s\" : %s", fname,
- strerror(errno));
+ SYSERROR("Failed to create dir for log file \"%s\"", fname);
return -1;
}
logfile = build_log_path(name, lxcpath);
if (!logfile) {
- ERROR("could not build log path");
+ ERROR("Could not build log path");
return -1;
}
+
ret = __lxc_log_set_file(logfile, create_dirs);
free(logfile);
return ret;
}
-extern 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;
- while (appender->next != NULL)
- appender = appender->next;
- appender->next = &log_appender_syslog;
-
- return 0;
-}
-
-extern void lxc_log_enable_syslog(void)
-{
- syslog_enable = 1;
-}
-
/*
* lxc_log_init:
* Called from lxc front-end programs (like lxc-create, lxc-start) to
- * initalize the log defaults.
+ * initialize the log defaults.
*/
-extern int lxc_log_init(struct lxc_log *log)
+int lxc_log_init(struct lxc_log *log)
{
- int lxc_priority = LXC_LOG_LEVEL_ERROR;
int ret;
+ int lxc_priority = LXC_LOG_LEVEL_ERROR;
+
+ if (!log)
+ return -1;
if (lxc_log_fd != -1) {
- WARN("lxc_log_init called with log already initialized");
+ WARN("Log already initialized");
return 0;
}
lxc_loglevel_specified = 1;
}
- if (!lxc_quiet_specified) {
+ 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->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 */
* ignore failures and continue logging to console
*/
if (!log->file && ret != 0) {
- INFO("Ignoring failure to open default logfile.");
+ 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.loglevel entry in a lxc.conf file. This
+ * 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.
*/
-extern int lxc_log_set_level(int *dest, int level)
+int lxc_log_set_level(int *dest, int level)
{
if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET) {
- ERROR("invalid log priority %d", level);
+ ERROR("Invalid log priority %d", level);
return -1;
}
+
*dest = level;
return 0;
}
-extern int lxc_log_get_level(void)
+inline int lxc_log_get_level(void)
{
return lxc_log_category_lxc.priority;
}
-extern bool lxc_log_has_valid_level(void)
+bool lxc_log_has_valid_level(void)
{
- int log_level = lxc_log_get_level();
+ int log_level;
+
+ log_level = lxc_log_get_level();
if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET)
return false;
+
return true;
}
* happens after processing command line arguments, which override the .conf
* settings. So only set the file if previously unset.
*/
-extern int lxc_log_set_file(int *fd, const char *fname)
+int lxc_log_set_file(int *fd, const char *fname)
{
- if (*fd != -1) {
+ if (*fd >= 0) {
close(*fd);
*fd = -1;
}
- if (build_dir(fname)) {
- ERROR("failed to create dir for log file \"%s\" : %s", fname,
- strerror(errno));
+ if (build_dir(fname))
return -1;
- }
*fd = log_open(fname);
- if (*fd == -1)
- return -errno;
+ if (*fd < 0)
+ return -1;
+
return 0;
}
-extern const char *lxc_log_get_file(void)
+inline const char *lxc_log_get_file(void)
{
return log_fname;
}
-extern void lxc_log_set_prefix(const char *prefix)
+inline void lxc_log_set_prefix(const char *prefix)
{
- strncpy(log_prefix, prefix, sizeof(log_prefix));
- log_prefix[sizeof(log_prefix) - 1] = 0;
+ /* We don't care if the prefix is truncated. */
+ (void)strlcpy(log_prefix, prefix, sizeof(log_prefix));
}
-extern const char *lxc_log_get_prefix(void)
+inline const char *lxc_log_get_prefix(void)
{
return log_prefix;
}
-extern void lxc_log_options_no_override()
+inline void lxc_log_options_no_override()
{
lxc_quiet_specified = 1;
lxc_loglevel_specified = 1;