aboutgitcodebugslistschat
path: root/log.c
diff options
context:
space:
mode:
Diffstat (limited to 'log.c')
-rw-r--r--log.c421
1 files changed, 236 insertions, 185 deletions
diff --git a/log.c b/log.c
index eafaca2..239c8ce 100644
--- a/log.c
+++ b/log.c
@@ -26,17 +26,14 @@
#include <stdarg.h>
#include <sys/socket.h>
+#include "linux_dep.h"
#include "log.h"
#include "util.h"
#include "passt.h"
-/* LOG_EARLY means we don't know yet: log everything. LOG_EMERG is unused */
-#define LOG_EARLY LOG_MASK(LOG_EMERG)
-
static int log_sock = -1; /* Optional socket to system logger */
static char log_ident[BUFSIZ]; /* Identifier string for openlog() */
-static int log_mask = LOG_EARLY; /* Current log priority mask */
-static int log_opt; /* Options for openlog() */
+static int log_mask; /* Current log priority mask */
static int log_file = -1; /* Optional log file descriptor */
static size_t log_size; /* Maximum log file size in bytes */
@@ -44,50 +41,46 @@ static size_t log_written; /* Currently used bytes in log file */
static size_t log_cut_size; /* Bytes to cut at start on rotation */
static char log_header[BUFSIZ]; /* File header, written back on cuts */
-static time_t log_start; /* Start timestamp */
-int log_trace; /* --trace mode enabled */
-int log_to_stdout; /* Print to stdout instead of stderr */
+struct timespec log_start; /* Start timestamp */
-void vlogmsg(int pri, const char *format, va_list ap)
-{
- bool debug_print = (log_mask & LOG_MASK(LOG_DEBUG)) && log_file == -1;
- bool early_print = LOG_PRI(log_mask) == LOG_EARLY;
- FILE *out = log_to_stdout ? stdout : stderr;
- struct timespec tp;
-
- if (debug_print) {
- clock_gettime(CLOCK_REALTIME, &tp);
- fprintf(out, "%lli.%04lli: ",
- (long long int)tp.tv_sec - log_start,
- (long long int)tp.tv_nsec / (100L * 1000));
- }
-
- if ((log_mask & LOG_MASK(LOG_PRI(pri))) || early_print) {
- va_list ap2;
-
- va_copy(ap2, ap); /* Don't clobber ap, we need it again */
- if (log_file != -1)
- logfile_write(pri, format, ap2);
- else if (!(log_mask & LOG_MASK(LOG_DEBUG)))
- passt_vsyslog(pri, format, ap2);
+int log_trace; /* --trace mode enabled */
+bool log_conf_parsed; /* Logging options already parsed */
+bool log_stderr = true; /* Not daemonised, no shell spawned */
- va_end(ap2);
- }
+#define LL_STRLEN (sizeof("-9223372036854775808"))
+#define LOGTIME_STRLEN (LL_STRLEN + 5)
- if (debug_print || (early_print && !(log_opt & LOG_PERROR))) {
- (void)vfprintf(out, format, ap);
- if (format[strlen(format)] != '\n')
- fprintf(out, "\n");
- }
+/**
+ * logtime() - Get the current time for logging purposes
+ * @ts: Buffer into which to store the timestamp
+ *
+ * Return: pointer to @now, or NULL if there was an error retrieving the time
+ */
+const struct timespec *logtime(struct timespec *ts)
+{
+ if (clock_gettime(CLOCK_MONOTONIC, ts))
+ return NULL;
+ return ts;
}
-void logmsg(int pri, const char *format, ...)
+/**
+ * logtime_fmt() - Format timestamp into a string for the log
+ * @buf: Buffer into which to format the time
+ * @size: Size of @buf
+ * @ts: Time to format (or NULL on error)
+ *
+ * Return: number of characters written to @buf (excluding \0)
+ */
+static int logtime_fmt(char *buf, size_t size, const struct timespec *ts)
{
- va_list ap;
+ if (ts) {
+ int64_t delta = timespec_diff_us(ts, &log_start);
- va_start(ap, format);
- vlogmsg(pri, format, ap);
- va_end(ap);
+ return snprintf(buf, size, "%lli.%04lli", delta / 1000000LL,
+ (delta / 100LL) % 10000);
+ }
+
+ return snprintf(buf, size, "<error>");
}
/* Prefixes for log file messages, indexed by priority */
@@ -101,126 +94,11 @@ const char *logfile_prefix[] = {
};
/**
- * trace_init() - Set log_trace depending on trace (debug) mode
- * @enable: Tracing debug mode enabled if non-zero
- */
-void trace_init(int enable)
-{
- log_trace = enable;
-}
-
-/**
- * __openlog() - Non-optional openlog() implementation, for custom vsyslog()
- * @ident: openlog() identity (program name)
- * @option: openlog() options
- * @facility: openlog() facility (LOG_DAEMON)
- */
-void __openlog(const char *ident, int option, int facility)
-{
- struct timespec tp;
-
- clock_gettime(CLOCK_REALTIME, &tp);
- log_start = tp.tv_sec;
-
- if (log_sock < 0) {
- struct sockaddr_un a = { .sun_family = AF_UNIX, };
-
- log_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
- if (log_sock < 0)
- return;
-
- strncpy(a.sun_path, _PATH_LOG, sizeof(a.sun_path));
- if (connect(log_sock, (const struct sockaddr *)&a, sizeof(a))) {
- close(log_sock);
- log_sock = -1;
- return;
- }
- }
-
- log_mask |= facility;
- strncpy(log_ident, ident, sizeof(log_ident) - 1);
- log_opt = option;
-}
-
-/**
- * __setlogmask() - setlogmask() wrapper, to allow custom vsyslog()
- * @mask: Same as setlogmask() mask
- */
-void __setlogmask(int mask)
-{
- log_mask = mask;
- setlogmask(mask);
-}
-
-/**
- * passt_vsyslog() - vsyslog() implementation not using heap memory
- * @pri: Facility and level map, same as priority for vsyslog()
- * @format: Same as vsyslog() format
- * @ap: Same as vsyslog() ap
- */
-void passt_vsyslog(int pri, const char *format, va_list ap)
-{
- int prefix_len, n;
- char buf[BUFSIZ];
-
- /* Send without timestamp, the system logger should add it */
- n = prefix_len = snprintf(buf, BUFSIZ, "<%i> %s: ", pri, log_ident);
-
- n += vsnprintf(buf + n, BUFSIZ - n, format, ap);
-
- if (format[strlen(format)] != '\n')
- n += snprintf(buf + n, BUFSIZ - n, "\n");
-
- if (log_opt & LOG_PERROR)
- fprintf(stderr, "%s", buf + prefix_len);
-
- if (send(log_sock, buf, n, 0) != n)
- fprintf(stderr, "Failed to send %i bytes to syslog\n", n);
-}
-
-/**
- * logfile_init() - Open log file and write header with PID, version, path
- * @name: Identifier for header: passt or pasta
- * @path: Path to log file
- * @size: Maximum size of log file: log_cut_size is calculatd here
- */
-void logfile_init(const char *name, const char *path, size_t size)
-{
- char nl = '\n', exe[PATH_MAX] = { 0 };
- int n;
-
- if (readlink("/proc/self/exe", exe, PATH_MAX - 1) < 0) {
- perror("readlink /proc/self/exe");
- exit(EXIT_FAILURE);
- }
-
- log_file = open(path, O_CREAT | O_TRUNC | O_APPEND | O_RDWR | O_CLOEXEC,
- S_IRUSR | S_IWUSR);
- if (log_file == -1)
- die("Couldn't open log file %s: %s", path, strerror(errno));
-
- log_size = size ? size : LOGFILE_SIZE_DEFAULT;
-
- n = snprintf(log_header, sizeof(log_header), "%s " VERSION ": %s (%i)",
- name, exe, getpid());
-
- if (write(log_file, log_header, n) <= 0 ||
- write(log_file, &nl, 1) <= 0) {
- perror("Couldn't write to log file\n");
- exit(EXIT_FAILURE);
- }
-
- /* For FALLOC_FL_COLLAPSE_RANGE: VFS block size can be up to one page */
- log_cut_size = ROUND_UP(log_size * LOGFILE_CUT_RATIO / 100, PAGE_SIZE);
-}
-
-#ifdef FALLOC_FL_COLLAPSE_RANGE
-/**
* logfile_rotate_fallocate() - Write header, set log_written after fallocate()
* @fd: Log file descriptor
* @now: Current timestamp
*
- * #syscalls lseek ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
+ * #syscalls lseek ppc64le:_llseek ppc64:_llseek arm:_llseek i686:_llseek
*/
static void logfile_rotate_fallocate(int fd, const struct timespec *now)
{
@@ -233,10 +111,8 @@ static void logfile_rotate_fallocate(int fd, const struct timespec *now)
if (read(fd, buf, BUFSIZ) == -1)
return;
- n = snprintf(buf, BUFSIZ,
- "%s - log truncated at %lli.%04lli", log_header,
- (long long int)(now->tv_sec - log_start),
- (long long int)(now->tv_nsec / (100L * 1000)));
+ n = snprintf(buf, BUFSIZ, "%s - log truncated at ", log_header);
+ n += logtime_fmt(buf + n, BUFSIZ - n, now);
/* Avoid partial lines by padding the header with spaces */
nl = memchr(buf + n + 1, '\n', BUFSIZ - n - 1);
@@ -250,14 +126,13 @@ static void logfile_rotate_fallocate(int fd, const struct timespec *now)
log_written -= log_cut_size;
}
-#endif /* FALLOC_FL_COLLAPSE_RANGE */
/**
* logfile_rotate_move() - Fallback: move recent entries toward start, then cut
* @fd: Log file descriptor
* @now: Current timestamp
*
- * #syscalls lseek ppc64le:_llseek ppc64:_llseek armv6l:_llseek armv7l:_llseek
+ * #syscalls lseek ppc64le:_llseek ppc64:_llseek arm:_llseek
* #syscalls ftruncate
*/
static void logfile_rotate_move(int fd, const struct timespec *now)
@@ -266,10 +141,10 @@ static void logfile_rotate_move(int fd, const struct timespec *now)
char buf[BUFSIZ];
const char *nl;
- header_len = snprintf(buf, BUFSIZ,
- "%s - log truncated at %lli.%04lli\n", log_header,
- (long long int)(now->tv_sec - log_start),
- (long long int)(now->tv_nsec / (100L * 1000)));
+ header_len = snprintf(buf, BUFSIZ, "%s - log truncated at ",
+ log_header);
+ header_len += logtime_fmt(buf + header_len, BUFSIZ - header_len, now);
+
if (lseek(fd, 0, SEEK_SET) == -1)
return;
if (write(fd, buf, header_len) == -1)
@@ -322,21 +197,17 @@ out:
*
* Return: 0 on success, negative error code on failure
*
- * #syscalls fcntl
- *
- * fallocate() passed as EXTRA_SYSCALL only if FALLOC_FL_COLLAPSE_RANGE is there
+ * #syscalls fcntl fallocate
*/
static int logfile_rotate(int fd, const struct timespec *now)
{
if (fcntl(fd, F_SETFL, O_RDWR /* Drop O_APPEND: explicit lseek() */))
return -errno;
-#ifdef FALLOC_FL_COLLAPSE_RANGE
/* Only for Linux >= 3.15, extent-based ext4 or XFS, glibc >= 2.18 */
if (!fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, 0, log_cut_size))
logfile_rotate_fallocate(fd, now);
else
-#endif
logfile_rotate_move(fd, now);
if (fcntl(fd, F_SETFL, O_RDWR | O_APPEND))
@@ -347,32 +218,212 @@ static int logfile_rotate(int fd, const struct timespec *now)
/**
* logfile_write() - Write entry to log file, trigger rotation if full
+ * @newline: Append newline at the end of the message, if missing
+ * @cont: Continuation of a previous message, on the same line
* @pri: Facility and level map, same as priority for vsyslog()
+ * @now: Timestamp
* @format: Same as vsyslog() format
* @ap: Same as vsyslog() ap
*/
-void logfile_write(int pri, const char *format, va_list ap)
+static void logfile_write(bool newline, bool cont, int pri,
+ const struct timespec *now,
+ const char *format, va_list ap)
{
- struct timespec now;
char buf[BUFSIZ];
- int n;
-
- if (clock_gettime(CLOCK_REALTIME, &now))
- return;
+ int n = 0;
- n = snprintf(buf, BUFSIZ, "%lli.%04lli: %s",
- (long long int)(now.tv_sec - log_start),
- (long long int)(now.tv_nsec / (100L * 1000)),
- logfile_prefix[pri]);
+ if (!cont) {
+ n += logtime_fmt(buf, BUFSIZ, now);
+ n += snprintf(buf + n, BUFSIZ - n, ": %s", logfile_prefix[pri]);
+ }
n += vsnprintf(buf + n, BUFSIZ - n, format, ap);
- if (format[strlen(format)] != '\n')
+ if (newline && format[strlen(format)] != '\n')
n += snprintf(buf + n, BUFSIZ - n, "\n");
- if ((log_written + n >= log_size) && logfile_rotate(log_file, &now))
+ if ((log_written + n >= log_size) && logfile_rotate(log_file, now))
return;
if ((n = write(log_file, buf, n)) >= 0)
log_written += n;
}
+
+/**
+ * vlogmsg() - Print or send messages to log or output files as configured
+ * @newline: Append newline at the end of the message, if missing
+ * @cont: Continuation of a previous message, on the same line
+ * @pri: Facility and level map, same as priority for vsyslog()
+ * @format: Message
+ * @ap: Variable argument list
+ */
+void vlogmsg(bool newline, bool cont, int pri, const char *format, va_list ap)
+{
+ bool debug_print = (log_mask & LOG_MASK(LOG_DEBUG)) && log_file == -1;
+ const struct timespec *now;
+ struct timespec ts;
+
+ now = logtime(&ts);
+
+ if (debug_print && !cont) {
+ char timestr[LOGTIME_STRLEN];
+
+ logtime_fmt(timestr, sizeof(timestr), now);
+ FPRINTF(stderr, "%s: ", timestr);
+ }
+
+ if ((log_mask & LOG_MASK(LOG_PRI(pri))) || !log_conf_parsed) {
+ va_list ap2;
+
+ va_copy(ap2, ap); /* Don't clobber ap, we need it again */
+ if (log_file != -1)
+ logfile_write(newline, cont, pri, now, format, ap2);
+ else if (!(log_mask & LOG_MASK(LOG_DEBUG)))
+ passt_vsyslog(newline, pri, format, ap2);
+
+ va_end(ap2);
+ }
+
+ if (debug_print || !log_conf_parsed ||
+ (log_stderr && (log_mask & LOG_MASK(LOG_PRI(pri))))) {
+ (void)vfprintf(stderr, format, ap);
+ if (newline && format[strlen(format)] != '\n')
+ FPRINTF(stderr, "\n");
+ }
+}
+
+/**
+ * logmsg() - vlogmsg() wrapper for variable argument lists
+ * @newline: Append newline at the end of the message, if missing
+ * @cont: Continuation of a previous message, on the same line
+ * @pri: Facility and level map, same as priority for vsyslog()
+ * @format: Message
+ */
+void logmsg(bool newline, bool cont, int pri, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vlogmsg(newline, cont, pri, format, ap);
+ va_end(ap);
+}
+
+/**
+ * logmsg_perror() - vlogmsg() wrapper with perror()-like functionality
+ * @pri: Facility and level map, same as priority for vsyslog()
+ * @format: Message
+ */
+void logmsg_perror(int pri, const char *format, ...)
+{
+ int errno_copy = errno;
+ va_list ap;
+
+ va_start(ap, format);
+ vlogmsg(false, false, pri, format, ap);
+ va_end(ap);
+
+ logmsg(true, true, pri, ": %s", strerror(errno_copy));
+}
+
+/**
+ * trace_init() - Set log_trace depending on trace (debug) mode
+ * @enable: Tracing debug mode enabled if non-zero
+ */
+void trace_init(int enable)
+{
+ log_trace = enable;
+}
+
+/**
+ * __openlog() - Non-optional openlog() implementation, for custom vsyslog()
+ * @ident: openlog() identity (program name)
+ * @option: openlog() options, unused
+ * @facility: openlog() facility (LOG_DAEMON)
+ */
+void __openlog(const char *ident, int option, int facility)
+{
+ (void)option;
+
+ if (log_sock < 0) {
+ struct sockaddr_un a = { .sun_family = AF_UNIX, };
+
+ log_sock = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (log_sock < 0)
+ return;
+
+ strncpy(a.sun_path, _PATH_LOG, sizeof(a.sun_path));
+ if (connect(log_sock, (const struct sockaddr *)&a, sizeof(a))) {
+ close(log_sock);
+ log_sock = -1;
+ return;
+ }
+ }
+
+ log_mask |= facility;
+ strncpy(log_ident, ident, sizeof(log_ident) - 1);
+}
+
+/**
+ * __setlogmask() - setlogmask() wrapper, to allow custom vsyslog()
+ * @mask: Same as setlogmask() mask
+ */
+void __setlogmask(int mask)
+{
+ log_mask = mask;
+ setlogmask(mask);
+}
+
+/**
+ * passt_vsyslog() - vsyslog() implementation not using heap memory
+ * @newline: Append newline at the end of the message, if missing
+ * @pri: Facility and level map, same as priority for vsyslog()
+ * @format: Same as vsyslog() format
+ * @ap: Same as vsyslog() ap
+ */
+void passt_vsyslog(bool newline, int pri, const char *format, va_list ap)
+{
+ char buf[BUFSIZ];
+ int n;
+
+ /* Send without timestamp, the system logger should add it */
+ n = snprintf(buf, BUFSIZ, "<%i> %s: ", pri, log_ident);
+
+ n += vsnprintf(buf + n, BUFSIZ - n, format, ap);
+
+ if (newline && format[strlen(format)] != '\n')
+ n += snprintf(buf + n, BUFSIZ - n, "\n");
+
+ if (log_sock >= 0 && send(log_sock, buf, n, 0) != n && log_stderr)
+ FPRINTF(stderr, "Failed to send %i bytes to syslog\n", n);
+}
+
+/**
+ * logfile_init() - Open log file and write header with PID, version, path
+ * @name: Identifier for header: passt or pasta
+ * @path: Path to log file
+ * @size: Maximum size of log file: log_cut_size is calculatd here
+ */
+void logfile_init(const char *name, const char *path, size_t size)
+{
+ char nl = '\n', exe[PATH_MAX] = { 0 };
+ int n;
+
+ if (readlink("/proc/self/exe", exe, PATH_MAX - 1) < 0)
+ die_perror("Failed to read own /proc/self/exe link");
+
+ log_file = output_file_open(path, O_APPEND | O_RDWR);
+ if (log_file == -1)
+ die_perror("Couldn't open log file %s", path);
+
+ log_size = size ? size : LOGFILE_SIZE_DEFAULT;
+
+ n = snprintf(log_header, sizeof(log_header), "%s " VERSION ": %s (%i)",
+ name, exe, getpid());
+
+ if (write(log_file, log_header, n) <= 0 ||
+ write(log_file, &nl, 1) <= 0)
+ die_perror("Couldn't write to log file");
+
+ /* For FALLOC_FL_COLLAPSE_RANGE: VFS block size can be up to one page */
+ log_cut_size = ROUND_UP(log_size * LOGFILE_CUT_RATIO / 100, PAGE_SIZE);
+}