-/*
- * lxc: linux Container library
- *
- * (C) Copyright IBM Corp. 2007, 2008
- *
- * Authors:
- * Cedric Le Goater <legoater@free.fr>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+/* SPDX-License-Identifier: LGPL-2.1+ */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#include "caps.h"
#include "config.h"
+#include "file_utils.h"
#include "log.h"
#include "lxccontainer.h"
+#include "memory_utils.h"
#include "utils.h"
#ifndef HAVE_STRLCPY
*/
#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_fd = -EBADF;
+static bool wants_syslog = false;
+static int lxc_quiet_specified;
int lxc_log_use_global_fd;
static int lxc_loglevel_specified;
static char *lxc_log_get_va_msg(struct lxc_log_event *event)
{
- char *msg;
+ __do_free char *msg = NULL;
int rc, len;
va_list args;
if (!event)
- return NULL;
+ return ret_set_errno(NULL, EINVAL);
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;
+ return ret_set_errno(NULL, ENOMEM);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
rc = vsnprintf(msg, len, event->fmt, *event->vap);
- if (rc == -1 || rc >= len) {
- free(msg);
- return NULL;
- }
+#pragma GCC diagnostic pop
+ if (rc < 0 || rc >= len)
+ return ret_set_errno(NULL, EIO);
- return msg;
+ return move_ptr(msg);
}
-/*---------------------------------------------------------------------------*/
static int log_append_syslog(const struct lxc_log_appender *appender,
struct lxc_log_event *event)
{
- char *msg;
+ __do_free char *msg = NULL;
const char *log_container_name;
- if (!syslog_enable)
+ if (!wants_syslog)
return 0;
log_container_name = lxc_log_get_container_name();
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)
{
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,
char nanosec[INTTYPE_TO_STRLEN(int64_t)];
int ret;
- /* See https://howardhinnant.github.io/date_algorithms.html for an
+ /*
+ * See https://howardhinnant.github.io/date_algorithms.html for an
* explanation of the algorithm used here.
*/
/* 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
+ /*
+ * 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
+ /*
+ * 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
+ /*
+ * 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
+ /*
+ * 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
+ /*
+ * 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
+ /*
+ * 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. */
+ /*
+ * 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
+ /*
+ * 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 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, sizeof(nanosec), "%"PRId64, (int64_t)time->tv_nsec);
- if (ret < 0 || ret >= sizeof(nanosec))
- return -1;
+ if (ret < 0 || (size_t)ret >= sizeof(nanosec))
+ return ret_errno(EIO);
- /* Create final timestamp for the log and shorten nanoseconds to 3
+ /*
+ * Create final timestamp for the log and shorten nanoseconds to 3
* digit precision.
*/
ret = snprintf(buf, bufsize,
"%02" PRId64 "%02" PRId64 ".%.3s",
year, month, day, hours, minutes, seconds, nanosec);
if (ret < 0 || (size_t)ret >= bufsize)
- return -1;
+ return ret_errno(EIO);
return 0;
}
-/* This function needs to make extra sure that it is thread-safe. We had some
+/*
+ * 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
* 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
static int log_append_logfile(const struct lxc_log_appender *appender,
struct lxc_log_event *event)
{
+ int fd_to_use = -EBADF;
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;
+ if (current_config && !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)
+ if (fd_to_use < 0)
fd_to_use = lxc_log_fd;
- if (fd_to_use == -1)
+ if (fd_to_use < 0)
return 0;
- if (lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp) < 0)
- return -1;
+ ret = lxc_unix_epoch_to_utc(date_time, LXC_LOG_TIME_SIZE, &event->timestamp);
+ if (ret)
+ return ret;
n = snprintf(buffer, sizeof(buffer),
"%s%s%s %s %-8s %s - %s:%s:%d - ",
event->locinfo->file, event->locinfo->func,
event->locinfo->line);
if (n < 0)
- return n;
+ return ret_errno(EIO);
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;
buffer[n] = '\n';
-again:
- ret = write(fd_to_use, buffer, n + 1);
- if (ret < 0 && errno == EINTR)
- goto again;
-
- return ret;
+ 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();
+ __do_free char *msg = NULL;
+ const char *log_container_name;
+
+ log_container_name = lxc_log_get_container_name();
+ msg = lxc_log_get_va_msg(event);
switch (event->priority) {
case LXC_LOG_LEVEL_TRACE:
break;
}
- free(msg);
return 0;
}
#endif
};
#endif
-/*---------------------------------------------------------------------------*/
static int build_dir(const char *name)
{
- char *e, *n, *p;
+ __do_free char *n = NULL;
+ char *e, *p;
/* Make copy of the string since we'll be modifying it. */
n = strdup(name);
if (!n)
- return -1;
+ return ret_errno(ENOMEM);
e = &n[strlen(n)];
for (p = n + 1; p < e; p++) {
*p = '\0';
ret = lxc_unpriv(mkdir(n, 0755));
- if (ret && errno != EEXIST) {
- SYSERROR("Failed to create directory %s", n);
- free(n);
- return -1;
- }
-
*p = '/';
+ if (ret && errno != EEXIST)
+ return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", n);
}
- free(n);
return 0;
}
-/*---------------------------------------------------------------------------*/
static int log_open(const char *name)
{
- int fd;
+ __do_close int fd = -EBADF;
int newfd;
- fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0666));
- if (fd < 0) {
- SYSERROR("Failed to open log file \"%s\"", name);
- return -1;
- }
+ fd = lxc_unpriv(open(name, O_CREAT | O_WRONLY | O_APPEND | O_CLOEXEC, 0660));
+ if (fd < 0)
+ return log_error_errno(-errno, errno, "Failed to open log file \"%s\"", name);
if (fd > 2)
- return fd;
+ return move_fd(fd);
newfd = fcntl(fd, F_DUPFD_CLOEXEC, STDERR_FILENO);
- if (newfd == -1)
- SYSERROR("Failed to dup log fd %d", fd);
+ if (newfd < 0)
+ return log_error_errno(-errno, errno, "Failed to dup log fd %d", fd);
- close(fd);
return newfd;
}
*/
static char *build_log_path(const char *name, const char *lxcpath)
{
- char *p;
+ __do_free char *p = NULL;
int ret;
size_t len;
bool use_dir;
if (!name)
- return NULL;
+ return ret_set_errno(NULL, EINVAL);
#if USE_CONFIGPATH_LOGS
use_dir = true;
p = malloc(len);
if (!p)
- return p;
+ return ret_set_errno(NULL, ENOMEM);
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;
- }
+ if (ret < 0 || (size_t)ret >= len)
+ return ret_set_errno(NULL, EIO);
- return p;
+ return move_ptr(p);
}
/*
static int __lxc_log_set_file(const char *fname, int create_dirs)
{
/* we are overriding the default. */
- if (lxc_log_fd != -1)
+ if (lxc_log_fd >= 0)
lxc_log_close();
if (!fname)
- return -1;
+ return ret_errno(EINVAL);
if (strlen(fname) == 0) {
log_fname = NULL;
- return -1;
+ return ret_errno(EINVAL);
}
#if USE_CONFIGPATH_LOGS
*/
if (create_dirs)
#endif
- if (build_dir(fname)) {
- SYSERROR("Failed to create dir for log file \"%s\"", fname);
- return -1;
- }
+ if (build_dir(fname))
+ return log_error_errno(-errno, errno, "Failed to create dir for log file \"%s\"", fname);
lxc_log_fd = log_open(fname);
- if (lxc_log_fd == -1)
- return -1;
+ if (lxc_log_fd < 0)
+ return lxc_log_fd;
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;
+ __do_free char *logfile = NULL;
logfile = build_log_path(name, lxcpath);
- if (!logfile) {
- ERROR("Could not build log path");
- return -1;
- }
+ if (!logfile)
+ return log_error_errno(-errno, errno, "Could not build log path");
- ret = __lxc_log_set_file(logfile, create_dirs);
- free(logfile);
- return ret;
+ return __lxc_log_set_file(logfile, create_dirs);
}
/*
* lxc_log_init:
* Called from lxc front-end programs (like lxc-create, lxc-start) to
- * initalize the log defaults.
+ * initialize the log defaults.
*/
int lxc_log_init(struct lxc_log *log)
{
int lxc_priority = LXC_LOG_LEVEL_ERROR;
if (!log)
- return -1;
+ return ret_errno(EINVAL);
- if (lxc_log_fd != -1) {
- WARN("Log already initialized");
- return 0;
- }
+ if (lxc_log_fd >= 0)
+ return log_warn_errno(0, EOPNOTSUPP, "Log already initialized");
if (log->level)
lxc_priority = lxc_log_priority_to_int(log->level);
return 0;
ret = __lxc_log_set_file(log->file, 1);
- if (ret < 0) {
- ERROR("Failed to enable logfile");
- return -1;
- }
+ if (ret < 0)
+ return log_error_errno(-1, errno, "Failed to enable logfile");
lxc_log_use_global_fd = 1;
} else {
ret = 0;
}
- if (lxc_log_fd != -1) {
+ if (lxc_log_fd >= 0) {
lxc_log_category_lxc.appender = &log_appender_logfile;
lxc_log_category_lxc.appender->next = &log_appender_stderr;
}
{
closelog();
- free(log_vmname);
- log_vmname = NULL;
-
- if (lxc_log_fd == -1)
- return;
+ free_disarm(log_vmname);
- close(lxc_log_fd);
- lxc_log_fd = -1;
+ close_prot_errno_disarm(lxc_log_fd);
- free(log_fname);
- log_fname = NULL;
+ free_disarm(log_fname);
}
int lxc_log_syslog(int facility)
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 */
+ /* not an error: openlog re-opened the connection */
+ if (appender == &log_appender_syslog)
return 0;
- }
appender = appender->next;
}
return 0;
}
-inline void lxc_log_enable_syslog(void)
+void lxc_log_syslog_enable(void)
{
- syslog_enable = 1;
+ wants_syslog = true;
+}
+
+void lxc_log_syslog_disable(void)
+{
+ wants_syslog = false;
}
/*
*/
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;
- }
+ if (level < 0 || level >= LXC_LOG_LEVEL_NOTSET)
+ return log_error_errno(-EINVAL, EINVAL, "Invalid log priority %d", level);
*dest = level;
return 0;
log_level = lxc_log_get_level();
if (log_level < 0 || log_level >= LXC_LOG_LEVEL_NOTSET)
- return false;
+ return ret_set_errno(false, EINVAL);
return true;
}
*/
int lxc_log_set_file(int *fd, const char *fname)
{
- if (*fd >= 0) {
- close(*fd);
- *fd = -1;
- }
+ if (*fd >= 0)
+ close_prot_errno_disarm(*fd);
if (build_dir(fname))
- return -1;
+ return -errno;
*fd = log_open(fname);
if (*fd < 0)
- return -1;
+ return -errno;
return 0;
}