pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmocore/+/41894?usp=email )


Change subject: logging: Move log target file to its own file
......................................................................

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 <[email protected]>
+ * (C) 2008-2010 by Harald Welte <[email protected]>
+ * (C) 2008 by Holger Hans Peter Freyther <[email protected]>
+ * 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 https://gerrit.osmocom.org/c/libosmocore/+/41894?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: I8e32e31c75b66ff0649d92c2f469f8895689fbad
Gerrit-Change-Number: 41894
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <[email protected]>

Reply via email to