blob: 7775c27d3188ec48c91a3d165f2638fa24160f2b [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file logging_gsmtap.c
2 * libosmocore log output encapsulated in GSMTAP.
3 *
4 * Encapsulating the log output inside GSMTAP frames allows us to
5 * observer protocol traces (of Um, Abis, A or any other interface in
6 * the Osmocom world) with synchronous interspersed log messages.
7 */
8/*
9 * (C) 2016 by Harald Welte <laforge@gnumonks.org>
Harald Welteaa00f992016-12-02 15:30:02 +010010 * All Rights Reserved
11 *
Harald Weltee08da972017-11-13 01:00:26 +090012 * SPDX-License-Identifier: GPL-2.0+
13 *
Harald Welteaa00f992016-12-02 15:30:02 +010014 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
Harald Welteaa00f992016-12-02 15:30:02 +010024 */
25
26/*! \addtogroup logging
27 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020028 * \file logging_gsmtap.c */
Harald Welteaa00f992016-12-02 15:30:02 +010029
Pau Espin Pedrol88955fb2023-01-18 18:54:00 +010030#include "config.h"
Harald Welteaa00f992016-12-02 15:30:02 +010031
32#include <stdarg.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <string.h>
36#include <stdbool.h>
Pau Espin Pedrolbb149ec2021-02-15 12:59:48 +010037#include <unistd.h>
Harald Welteaa00f992016-12-02 15:30:02 +010038
39#ifdef HAVE_STRINGS_H
40#include <strings.h>
41#endif
42
43#include <osmocom/core/talloc.h>
44#include <osmocom/core/utils.h>
45#include <osmocom/core/gsmtap.h>
46#include <osmocom/core/gsmtap_util.h>
47#include <osmocom/core/logging.h>
48#include <osmocom/core/timer.h>
Harald Welte95871da2017-05-15 12:11:36 +020049#include <osmocom/core/byteswap.h>
Pau Espin Pedrol74dddef2021-02-17 18:55:44 +010050#include <osmocom/core/thread.h>
Harald Welteaa00f992016-12-02 15:30:02 +010051
52#define GSMTAP_LOG_MAX_SIZE 4096
53
Pau Espin Pedrol74dddef2021-02-17 18:55:44 +010054static __thread uint32_t logging_gsmtap_tid;
Pau Espin Pedrolbb149ec2021-02-15 12:59:48 +010055
Harald Welteaa00f992016-12-02 15:30:02 +010056static void _gsmtap_raw_output(struct log_target *target, int subsys,
57 unsigned int level, const char *file,
58 int line, int cont, const char *format,
59 va_list ap)
60{
61 struct msgb *msg;
62 struct gsmtap_hdr *gh;
63 struct gsmtap_osmocore_log_hdr *golh;
64 const char *subsys_name = log_category_name(subsys);
65 struct timeval tv;
66 int rc;
Neels Hofmeyrbb62cbc2018-01-12 05:38:00 +010067 const char *file_basename;
Daniel Willmannf2629672023-11-03 20:04:00 +010068 unsigned int _level;
Harald Welteaa00f992016-12-02 15:30:02 +010069
70 /* get timestamp ASAP */
71 osmo_gettimeofday(&tv, NULL);
72
73 msg = msgb_alloc(sizeof(*gh)+sizeof(*golh)+GSMTAP_LOG_MAX_SIZE,
74 "GSMTAP logging");
75
76 /* GSMTAP header */
77 gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
78 memset(gh, 0, sizeof(*gh));
79 gh->version = GSMTAP_VERSION;
80 gh->hdr_len = sizeof(*gh)/4;
81 gh->type = GSMTAP_TYPE_OSMOCORE_LOG;
82
83 /* Logging header */
84 golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh));
Max18c014d2018-01-30 14:33:01 +010085 OSMO_STRLCPY_ARRAY(golh->proc_name, target->tgt_gsmtap.ident);
Pau Espin Pedrol74dddef2021-02-17 18:55:44 +010086 if (logging_gsmtap_tid == 0)
87 osmo_store32be((uint32_t)osmo_gettid(), &logging_gsmtap_tid);
88 golh->pid = logging_gsmtap_tid;
Harald Welteaa00f992016-12-02 15:30:02 +010089 if (subsys_name)
Max18c014d2018-01-30 14:33:01 +010090 OSMO_STRLCPY_ARRAY(golh->subsys, subsys_name + 1);
Harald Welteaa00f992016-12-02 15:30:02 +010091 else
92 golh->subsys[0] = '\0';
Neels Hofmeyrbb62cbc2018-01-12 05:38:00 +010093
94 /* strip all leading path elements from file, if any. */
95 file_basename = strrchr(file, '/');
96 file = (file_basename && file_basename[1])? file_basename + 1 : file;
Max18c014d2018-01-30 14:33:01 +010097 OSMO_STRLCPY_ARRAY(golh->src_file.name, file);
Harald Welte95871da2017-05-15 12:11:36 +020098 golh->src_file.line_nr = osmo_htonl(line);
Harald Welteaa00f992016-12-02 15:30:02 +010099 golh->level = level;
100 /* we always store the timestamp in the message, irrespective
101 * of hat prrint_[ext_]timestamp say */
Harald Welte95871da2017-05-15 12:11:36 +0200102 golh->ts.sec = osmo_htonl(tv.tv_sec);
103 golh->ts.usec = osmo_htonl(tv.tv_usec);
Harald Welteaa00f992016-12-02 15:30:02 +0100104
105 rc = vsnprintf((char *) msg->tail, msgb_tailroom(msg), format, ap);
Neels Hofmeyr4a8a9f42018-02-16 01:26:30 +0100106 if (rc < 0) {
107 msgb_free(msg);
Harald Welteaa00f992016-12-02 15:30:02 +0100108 return;
Vadim Yanitskiy785ecc92018-12-28 14:34:52 +0100109 } else if (rc >= msgb_tailroom(msg)) {
110 /* If the output was truncated, vsnprintf() returns the
111 * number of characters which would have been written
112 * if enough space had been available (excluding '\0'). */
113 rc = msgb_tailroom(msg);
114 msg->tail[rc - 1] = '\0';
Neels Hofmeyr4a8a9f42018-02-16 01:26:30 +0100115 }
Harald Welteaa00f992016-12-02 15:30:02 +0100116 msgb_put(msg, rc);
117
Daniel Willmannf2629672023-11-03 20:04:00 +0100118 /* Ensure that any error occurring when sending the log message doesn't cause infinite recursion */
119 _level = target->loglevel;
120 target->loglevel = UINT8_MAX;
Neels Hofmeyra4952aa2018-02-16 01:26:00 +0100121 rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg);
Daniel Willmannf2629672023-11-03 20:04:00 +0100122 target->loglevel = _level;
Neels Hofmeyra4952aa2018-02-16 01:26:00 +0100123 if (rc)
124 msgb_free(msg);
Harald Welteaa00f992016-12-02 15:30:02 +0100125}
126
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200127/*! Create a new logging target for GSMTAP logging
Vadim Yanitskiyccf7c7c2019-03-25 21:41:10 +0700128 * \param[in] host remote host to send the logs to
129 * \param[in] port remote port to send the logs to
Harald Welteaa00f992016-12-02 15:30:02 +0100130 * \param[in] ident string identifier
Vadim Yanitskiyccf7c7c2019-03-25 21:41:10 +0700131 * \param[in] ofd_wq_mode register osmo_wqueue (1) or not (0)
132 * \param[in] add_sink add GSMTAP sink or not
Harald Welteaa00f992016-12-02 15:30:02 +0100133 * \returns Log target in case of success, NULL in case of error
134 */
135struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
136 const char *ident,
137 bool ofd_wq_mode,
138 bool add_sink)
139{
140 struct log_target *target;
141 struct gsmtap_inst *gti;
142
143 target = log_target_create();
144 if (!target)
145 return NULL;
146
147 gti = gsmtap_source_init(host, port, ofd_wq_mode);
148 if (!gti) {
149 log_target_destroy(target);
150 return NULL;
151 }
152
153 if (add_sink)
154 gsmtap_source_add_sink(gti);
155
156 target->tgt_gsmtap.gsmtap_inst = gti;
157 target->tgt_gsmtap.ident = talloc_strdup(target, ident);
158 target->tgt_gsmtap.hostname = talloc_strdup(target, host);
159
160 target->type = LOG_TGT_TYPE_GSMTAP;
161 target->raw_output = _gsmtap_raw_output;
162
Harald Welteaa00f992016-12-02 15:30:02 +0100163 return target;
164}
165
166/* @} */