pespin has uploaded this change for review.
logging: Move log target file to its own file
We already have all other log_target implementations each on its own
file. Move the file (and stderr, a specific case of file) into its own
file too, properly separating most file-specific logic from general
logging logic.
Change-Id: I8e32e31c75b66ff0649d92c2f469f8895689fbad
---
M include/osmocom/core/logging_internal.h
M src/core/Makefile.am
M src/core/logging.c
A src/core/logging_file.c
4 files changed, 384 insertions(+), 326 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/94/41894/1
diff --git a/include/osmocom/core/logging_internal.h b/include/osmocom/core/logging_internal.h
index 6fc9b2a..cadd4df 100644
--- a/include/osmocom/core/logging_internal.h
+++ b/include/osmocom/core/logging_internal.h
@@ -10,6 +10,9 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
+/* maximum length of the log string of a single log event (typically line) */
+#define MAX_LOG_SIZE 4096
+
/*! Log context information, passed to filter */
struct log_context {
void *ctx[LOG_MAX_CTX+1] OSMO_DEPRECATED_OUTSIDE("Accessing struct log_context members directly is deprecated");
@@ -119,4 +122,10 @@
void assert_loginfo(const char *src);
+int log_output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap);
+
+void log_target_file_destroy(struct log_target *target);
+
/*! @} */
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index f2cf138..1c0bfed 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -44,6 +44,7 @@
isdnhdlc.c \
it_q.c \
logging.c \
+ logging_file.c \
logging_gsmtap.c \
loggingrb.c \
macaddr.c \
diff --git a/src/core/logging.c b/src/core/logging.c
index e8c48b8..0482342 100644
--- a/src/core/logging.c
+++ b/src/core/logging.c
@@ -71,12 +71,6 @@
#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
-/* maximum length of the log string of a single log event (typically line) */
-#define MAX_LOG_SIZE 4096
-
-/* maximum number of log statements we queue in file/stderr target write queue */
-#define LOG_WQUEUE_LEN 156
-
osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx),
enum_logging_ctx_items_fit_in_struct_log_context);
osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data),
@@ -566,9 +560,9 @@
* \param[in] format format string
* \param[in] ap variable argument list for format
* \returns number of bytes written to out */
-static int _output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys,
- unsigned int level, const char *file, int line, int cont,
- const char *format, va_list ap)
+int log_output_buf(char *buf, int buf_len, struct log_target *target, unsigned int subsys,
+ unsigned int level, const char *file, int line, int cont,
+ const char *format, va_list ap)
{
int ret;
const char *c_subsys = NULL;
@@ -679,19 +673,6 @@
return OSMO_STRBUF_CHAR_COUNT(sb);
}
-/* Format the log line for given target; use a stack buffer and call target->output */
-static void _output(struct log_target *target, unsigned int subsys,
- unsigned int level, const char *file, int line, int cont,
- const char *format, va_list ap)
-{
- char buf[MAX_LOG_SIZE];
- int rc;
-
- rc = _output_buf(buf, sizeof(buf), target, subsys, level, file, line, cont, format, ap);
- if (rc > 0)
- target->output(target, level, buf);
-}
-
/* Catch internal logging category indexes as well as out-of-bounds indexes.
* For internal categories, the ID is negative starting with -1; and internal
* logging categories are added behind the user categories. For out-of-bounds
@@ -781,10 +762,16 @@
* in undefined state. Since _output uses vsnprintf and it may
* be called several times, we have to pass a copy of ap. */
va_copy(bp, ap);
- if (tar->raw_output)
+ if (tar->raw_output) {
tar->raw_output(tar, subsys, level, file, line, cont, format, bp);
- else
- _output(tar, subsys, level, file, line, cont, format, bp);
+ } else {
+ /* Format the log line for given target; use a stack buffer and call target->output */
+ char buf[MAX_LOG_SIZE];
+ int rc = log_output_buf(buf, sizeof(buf), tar, subsys, level,
+ file, line, cont, format, bp);
+ if (rc > 0)
+ tar->output(tar, level, buf);
+ }
va_end(bp);
}
@@ -1138,69 +1125,6 @@
#endif
}
-#if (!EMBEDDED)
-/* write-queue tells us we should write another msgb (log line) to the output fd */
-static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg)
-{
- int rc;
-
- rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
- if (rc < 0)
- return rc;
- if (rc != msgb_length(msg)) {
- /* pull the number of bytes we have already written */
- msgb_pull(msg, rc);
- /* ask write_queue to re-insert the msgb at the head of the queue */
- return -EAGAIN;
- }
- return 0;
-}
-
-/* output via buffered, blocking stdio streams */
-static void _file_output_stream(struct log_target *target, unsigned int level,
- const char *log)
-{
- OSMO_ASSERT(target->tgt_file.out);
- fputs(log, target->tgt_file.out);
- fflush(target->tgt_file.out);
-}
-
-/* output via non-blocking write_queue, doing internal buffering */
-static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file,
- int line, int cont, const char *format, va_list ap)
-{
- struct msgb *msg;
- int rc;
-
- OSMO_ASSERT(target->tgt_file.wqueue);
- msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg");
- if (!msg)
- return;
-
- /* we simply enqueue the log message to a write queue here, to avoid any blocking
- * writes on the output file. The write queue will tell us once the file is writable
- * and call _file_wq_write_cb() */
- rc = _output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap);
- msgb_put(msg, rc);
-
- /* attempt a synchronous, non-blocking write, if the write queue is empty */
- if (target->tgt_file.wqueue->current_length == 0) {
- rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg);
- if (rc == 0) {
- /* the write was complete, we can exit early */
- msgb_free(msg);
- return;
- }
- }
- /* if we reach here, either we already had elements in the write_queue, or the synchronous write
- * failed: enqueue the message to the write_queue (backlog) */
- if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) {
- msgb_free(msg);
- /* TODO: increment some counter so we can see that messages were dropped */
- }
-}
-#endif
-
/*! Create a new log target skeleton
* \returns dynamically-allocated log target
* This funcition allocates a \ref log_target and initializes it
@@ -1256,188 +1180,6 @@
return target;
}
-/*! Create the STDERR log target
- * \returns dynamically-allocated \ref log_target for STDERR */
-struct log_target *log_target_create_stderr(void)
-{
-/* since C89/C99 says stderr is a macro, we can safely do this! */
-#if !EMBEDDED && defined(stderr)
- struct log_target *target;
-
- target = log_target_create();
- if (!target)
- return NULL;
-
- target->type = LOG_TGT_TYPE_STDERR;
- target->tgt_file.out = stderr;
- target->output = _file_output_stream;
- return target;
-#else
- return NULL;
-#endif /* stderr */
-}
-
-#if (!EMBEDDED)
-
-/*! switch from non-blocking/write-queue to blocking + buffered stream output
- * \param[in] target log target which we should switch
- * \return 0 on success; 1 if already switched before; negative on error
- * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
- */
-int log_target_file_switch_to_stream(struct log_target *target)
-{
- struct osmo_wqueue *wq;
-
- if (!target)
- return -ENODEV;
-
- if (target->tgt_file.out) {
- /* target has already been switched over */
- return 1;
- }
-
- wq = target->tgt_file.wqueue;
- OSMO_ASSERT(wq);
-
- /* re-open output as stream */
- if (target->type == LOG_TGT_TYPE_STDERR)
- target->tgt_file.out = stderr;
- else
- target->tgt_file.out = fopen(target->tgt_file.fname, "a");
- if (!target->tgt_file.out) {
- return -EIO;
- }
-
- /* synchronously write anything left in the queue */
- while (!llist_empty(&wq->msg_queue)) {
- struct msgb *msg = msgb_dequeue(&wq->msg_queue);
- fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out);
- msgb_free(msg);
- }
-
- /* now that everything succeeded, we can finally close the old output fd */
- if (target->type == LOG_TGT_TYPE_FILE) {
- osmo_fd_unregister(&wq->bfd);
- close(wq->bfd.fd);
- wq->bfd.fd = -1;
- }
-
- /* release the queue itself */
- talloc_free(wq);
- target->tgt_file.wqueue = NULL;
- target->output = _file_output_stream;
- target->raw_output = NULL;
-
- return 0;
-}
-
-/*! switch from blocking + buffered file output to non-blocking write-queue based output.
- * \param[in] target log target which we should switch
- * \return 0 on success; 1 if already switched before; negative on error
- * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
- */
-int log_target_file_switch_to_wqueue(struct log_target *target)
-{
- struct osmo_wqueue *wq;
- int rc;
-
- if (!target)
- return -ENODEV;
-
- if (!target->tgt_file.out) {
- /* target has already been switched over */
- return 1;
- }
-
- /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
- * log lines (stored as msgbs) will not put result in malloc() calls, and also to
- * reduce the OOM probability within logging, as the pool is already allocated */
- wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
- LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
- if (!wq)
- return -ENOMEM;
- osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
-
- fflush(target->tgt_file.out);
- if (target->type == LOG_TGT_TYPE_FILE) {
- rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
- if (rc < 0) {
- talloc_free(wq);
- return -errno;
- }
- } else {
- rc = STDERR_FILENO;
- }
- wq->bfd.fd = rc;
- wq->bfd.when = OSMO_FD_WRITE;
- wq->write_cb = _file_wq_write_cb;
-
- rc = osmo_fd_register(&wq->bfd);
- if (rc < 0) {
- talloc_free(wq);
- return -EIO;
- }
- target->tgt_file.wqueue = wq;
- target->raw_output = _file_raw_output;
- target->output = NULL;
-
- /* now that everything succeeded, we can finally close the old output stream */
- if (target->type == LOG_TGT_TYPE_FILE)
- fclose(target->tgt_file.out);
- target->tgt_file.out = NULL;
-
- return 0;
-}
-
-/*! Create a new file-based log target using non-blocking write_queue
- * \param[in] fname File name of the new log file
- * \returns Log target in case of success, NULL otherwise
- */
-struct log_target *log_target_create_file(const char *fname)
-{
- struct log_target *target;
- struct osmo_wqueue *wq;
- int rc;
-
- target = log_target_create();
- if (!target)
- return NULL;
-
- target->type = LOG_TGT_TYPE_FILE;
- /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
- * log lines (stored as msgbs) will not put result in malloc() calls, and also to
- * reduce the OOM probability within logging, as the pool is already allocated */
- wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
- LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
- if (!wq) {
- log_target_destroy(target);
- return NULL;
- }
- osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
- wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
- if (wq->bfd.fd < 0) {
- talloc_free(wq);
- log_target_destroy(target);
- return NULL;
- }
- wq->bfd.when = OSMO_FD_WRITE;
- wq->write_cb = _file_wq_write_cb;
-
- rc = osmo_fd_register(&wq->bfd);
- if (rc < 0) {
- talloc_free(wq);
- log_target_destroy(target);
- return NULL;
- }
-
- target->tgt_file.wqueue = wq;
- target->raw_output = _file_raw_output;
- target->tgt_file.fname = talloc_strdup(target, fname);
-
- return target;
-}
-#endif
-
/*! Find a registered log target
* \param[in] type Log target type
* \param[in] fname File name
@@ -1475,29 +1217,10 @@
log_del_target(target);
#if (!EMBEDDED)
- struct osmo_wqueue *wq;
switch (target->type) {
case LOG_TGT_TYPE_FILE:
case LOG_TGT_TYPE_STDERR:
- if (target->tgt_file.out) {
- if (target->type == LOG_TGT_TYPE_FILE)
- fclose(target->tgt_file.out);
- target->tgt_file.out = NULL;
- }
- wq = target->tgt_file.wqueue;
- if (wq) {
- if (wq->bfd.fd >= 0) {
- osmo_fd_unregister(&wq->bfd);
- if (target->type == LOG_TGT_TYPE_FILE)
- close(wq->bfd.fd);
- wq->bfd.fd = -1;
- }
- osmo_wqueue_clear(wq);
- talloc_free(wq);
- target->tgt_file.wqueue = NULL;
- }
- talloc_free((void *)target->tgt_file.fname);
- target->tgt_file.fname = NULL;
+ log_target_file_destroy(target);
break;
case LOG_TGT_TYPE_GSMTAP:
gsmtap_source_free(target->tgt_gsmtap.gsmtap_inst);
@@ -1516,42 +1239,6 @@
talloc_free(target);
}
-/*! close and re-open a log file (for log file rotation)
- * \param[in] target log target to re-open
- * \returns 0 in case of success; negative otherwise */
-int log_target_file_reopen(struct log_target *target)
-{
- struct osmo_wqueue *wq;
- int rc;
-
- OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR);
- OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue);
-
- if (target->tgt_file.out) {
- fclose(target->tgt_file.out);
- target->tgt_file.out = fopen(target->tgt_file.fname, "a");
- if (!target->tgt_file.out)
- return -errno;
- } else {
- wq = target->tgt_file.wqueue;
- if (wq->bfd.fd >= 0) {
- osmo_fd_unregister(&wq->bfd);
- close(wq->bfd.fd);
- wq->bfd.fd = -1;
- }
-
- rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
- if (rc < 0)
- return -errno;
- wq->bfd.fd = rc;
- rc = osmo_fd_register(&wq->bfd);
- if (rc < 0)
- return rc;
- }
-
- return 0;
-}
-
/*! close and re-open all log files (for log file rotation)
* \returns 0 in case of success; negative otherwise */
int log_targets_reopen(void)
diff --git a/src/core/logging_file.c b/src/core/logging_file.c
new file mode 100644
index 0000000..d7dfcd0
--- /dev/null
+++ b/src/core/logging_file.c
@@ -0,0 +1,361 @@
+/*! \file logging_file.c
+ * Syslog logging support code. */
+/*
+ * (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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.
+ *
+ */
+
+/*! \addtogroup logging
+ * @{
+ * \file logging_file.c */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/logging_internal.h>
+
+/* maximum number of log statements we queue in file/stderr target write queue */
+#define LOG_WQUEUE_LEN 156
+
+
+/*! close and re-open a log file (for log file rotation)
+ * \param[in] target log target to re-open
+ * \returns 0 in case of success; negative otherwise */
+int log_target_file_reopen(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+ int rc;
+
+ OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE || target->type == LOG_TGT_TYPE_STDERR);
+ OSMO_ASSERT(target->tgt_file.out || target->tgt_file.wqueue);
+
+ if (target->tgt_file.out) {
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -errno;
+ } else {
+ wq = target->tgt_file.wqueue;
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0)
+ return -errno;
+ wq->bfd.fd = rc;
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+#if (!EMBEDDED)
+
+/* This is the file-specific subclass destructor logic, called from
+ * log_target_destroy(). User should call log_target_destroy() to destroy this
+ * object. */
+void log_target_file_destroy(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+
+ OSMO_ASSERT(target->type == LOG_TGT_TYPE_FILE ||
+ target->type == LOG_TGT_TYPE_STDERR);
+
+ if (target->tgt_file.out) {
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+ }
+ wq = target->tgt_file.wqueue;
+ if (wq) {
+ if (wq->bfd.fd >= 0) {
+ osmo_fd_unregister(&wq->bfd);
+ if (target->type == LOG_TGT_TYPE_FILE)
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+ osmo_wqueue_clear(wq);
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ }
+ talloc_free((void *)target->tgt_file.fname);
+ target->tgt_file.fname = NULL;
+}
+
+/* write-queue tells us we should write another msgb (log line) to the output fd */
+static int _file_wq_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ int rc;
+
+ rc = write(ofd->fd, msgb_data(msg), msgb_length(msg));
+ if (rc < 0)
+ return rc;
+ if (rc != msgb_length(msg)) {
+ /* pull the number of bytes we have already written */
+ msgb_pull(msg, rc);
+ /* ask write_queue to re-insert the msgb at the head of the queue */
+ return -EAGAIN;
+ }
+ return 0;
+}
+
+/* output via buffered, blocking stdio streams */
+static void _file_output_stream(struct log_target *target, unsigned int level,
+ const char *log)
+{
+ OSMO_ASSERT(target->tgt_file.out);
+ fputs(log, target->tgt_file.out);
+ fflush(target->tgt_file.out);
+}
+
+/* output via non-blocking write_queue, doing internal buffering */
+static void _file_raw_output(struct log_target *target, int subsys, unsigned int level, const char *file,
+ int line, int cont, const char *format, va_list ap)
+{
+ struct msgb *msg;
+ int rc;
+
+ OSMO_ASSERT(target->tgt_file.wqueue);
+ msg = msgb_alloc_c(target->tgt_file.wqueue, MAX_LOG_SIZE, "log_file_msg");
+ if (!msg)
+ return;
+
+ /* we simply enqueue the log message to a write queue here, to avoid any blocking
+ * writes on the output file. The write queue will tell us once the file is writable
+ * and call _file_wq_write_cb() */
+ rc = log_output_buf((char *)msgb_data(msg), msgb_tailroom(msg), target, subsys, level, file, line, cont, format, ap);
+ msgb_put(msg, rc);
+
+ /* attempt a synchronous, non-blocking write, if the write queue is empty */
+ if (target->tgt_file.wqueue->current_length == 0) {
+ rc = _file_wq_write_cb(&target->tgt_file.wqueue->bfd, msg);
+ if (rc == 0) {
+ /* the write was complete, we can exit early */
+ msgb_free(msg);
+ return;
+ }
+ }
+ /* if we reach here, either we already had elements in the write_queue, or the synchronous write
+ * failed: enqueue the message to the write_queue (backlog) */
+ if (osmo_wqueue_enqueue_quiet(target->tgt_file.wqueue, msg) < 0) {
+ msgb_free(msg);
+ /* TODO: increment some counter so we can see that messages were dropped */
+ }
+}
+
+/*! switch from non-blocking/write-queue to blocking + buffered stream output
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_stream(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+
+ if (!target)
+ return -ENODEV;
+
+ if (target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ wq = target->tgt_file.wqueue;
+ OSMO_ASSERT(wq);
+
+ /* re-open output as stream */
+ if (target->type == LOG_TGT_TYPE_STDERR)
+ target->tgt_file.out = stderr;
+ else
+ target->tgt_file.out = fopen(target->tgt_file.fname, "a");
+ if (!target->tgt_file.out)
+ return -EIO;
+
+ /* synchronously write anything left in the queue */
+ while (!llist_empty(&wq->msg_queue)) {
+ struct msgb *msg = msgb_dequeue(&wq->msg_queue);
+ fwrite(msgb_data(msg), msgb_length(msg), 1, target->tgt_file.out);
+ msgb_free(msg);
+ }
+
+ /* now that everything succeeded, we can finally close the old output fd */
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ osmo_fd_unregister(&wq->bfd);
+ close(wq->bfd.fd);
+ wq->bfd.fd = -1;
+ }
+
+ /* release the queue itself */
+ talloc_free(wq);
+ target->tgt_file.wqueue = NULL;
+ target->output = _file_output_stream;
+ target->raw_output = NULL;
+
+ return 0;
+}
+
+/*! switch from blocking + buffered file output to non-blocking write-queue based output.
+ * \param[in] target log target which we should switch
+ * \return 0 on success; 1 if already switched before; negative on error
+ * Must be called with mutex osmo_log_tgt_mutex held, see log_tgt_mutex_lock.
+ */
+int log_target_file_switch_to_wqueue(struct log_target *target)
+{
+ struct osmo_wqueue *wq;
+ int rc;
+
+ if (!target)
+ return -ENODEV;
+
+ if (!target->tgt_file.out) {
+ /* target has already been switched over */
+ return 1;
+ }
+
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq)
+ return -ENOMEM;
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+
+ fflush(target->tgt_file.out);
+ if (target->type == LOG_TGT_TYPE_FILE) {
+ rc = open(target->tgt_file.fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -errno;
+ }
+ } else {
+ rc = STDERR_FILENO;
+ }
+ wq->bfd.fd = rc;
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
+
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ return -EIO;
+ }
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
+ target->output = NULL;
+
+ /* now that everything succeeded, we can finally close the old output stream */
+ if (target->type == LOG_TGT_TYPE_FILE)
+ fclose(target->tgt_file.out);
+ target->tgt_file.out = NULL;
+
+ return 0;
+}
+
+/*! Create a new file-based log target using non-blocking write_queue
+ * \param[in] fname File name of the new log file
+ * \returns Log target in case of success, NULL otherwise
+ */
+struct log_target *log_target_create_file(const char *fname)
+{
+ struct log_target *target;
+ struct osmo_wqueue *wq;
+ int rc;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_FILE;
+ /* we create a ~640kB sized talloc pool within the write-queue to ensure individual
+ * log lines (stored as msgbs) will not put result in malloc() calls, and also to
+ * reduce the OOM probability within logging, as the pool is already allocated */
+ wq = talloc_pooled_object(target, struct osmo_wqueue, LOG_WQUEUE_LEN,
+ LOG_WQUEUE_LEN*(sizeof(struct msgb)+MAX_LOG_SIZE));
+ if (!wq) {
+ log_target_destroy(target);
+ return NULL;
+ }
+ osmo_wqueue_init(wq, LOG_WQUEUE_LEN);
+ wq->bfd.fd = open(fname, O_WRONLY|O_APPEND|O_CREAT|O_NONBLOCK, 0660);
+ if (wq->bfd.fd < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+ wq->bfd.when = OSMO_FD_WRITE;
+ wq->write_cb = _file_wq_write_cb;
+
+ rc = osmo_fd_register(&wq->bfd);
+ if (rc < 0) {
+ talloc_free(wq);
+ log_target_destroy(target);
+ return NULL;
+ }
+
+ target->tgt_file.wqueue = wq;
+ target->raw_output = _file_raw_output;
+ target->tgt_file.fname = talloc_strdup(target, fname);
+
+ return target;
+}
+#endif
+
+/*! Create the STDERR log target
+ * \returns dynamically-allocated \ref log_target for STDERR */
+struct log_target *log_target_create_stderr(void)
+{
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#if !EMBEDDED && defined(stderr)
+ struct log_target *target;
+
+ target = log_target_create();
+ if (!target)
+ return NULL;
+
+ target->type = LOG_TGT_TYPE_STDERR;
+ target->tgt_file.out = stderr;
+ target->output = _file_output_stream;
+ return target;
+#else
+ return NULL;
+#endif /* stderr */
+}
+
+/* @} */
To view, visit change 41894. To unsubscribe, or for help writing mail filters, visit settings.