libosmocore/src/logging_gsmtap.c

155 lines
4.4 KiB
C

/*! \file logging_gsmtap.c
* libosmocore log output encapsulated in GSMTAP.
*
* Encapsulating the log output inside GSMTAP frames allows us to
* observer protocol traces (of Um, Abis, A or any other interface in
* the Osmocom world) with synchronous interspersed log messages.
*/
/*
* (C) 2016 by Harald Welte <laforge@gnumonks.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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*! \addtogroup logging
* @{
* \file logging_gsmtap.c */
#include "../config.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/byteswap.h>
#define GSMTAP_LOG_MAX_SIZE 4096
static void _gsmtap_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;
struct gsmtap_hdr *gh;
struct gsmtap_osmocore_log_hdr *golh;
const char *subsys_name = log_category_name(subsys);
struct timeval tv;
int rc;
const char *file_basename;
/* get timestamp ASAP */
osmo_gettimeofday(&tv, NULL);
msg = msgb_alloc(sizeof(*gh)+sizeof(*golh)+GSMTAP_LOG_MAX_SIZE,
"GSMTAP logging");
/* GSMTAP header */
gh = (struct gsmtap_hdr *) msgb_put(msg, sizeof(*gh));
memset(gh, 0, sizeof(*gh));
gh->version = GSMTAP_VERSION;
gh->hdr_len = sizeof(*gh)/4;
gh->type = GSMTAP_TYPE_OSMOCORE_LOG;
/* Logging header */
golh = (struct gsmtap_osmocore_log_hdr *) msgb_put(msg, sizeof(*golh));
OSMO_STRLCPY_ARRAY(golh->proc_name, target->tgt_gsmtap.ident);
if (subsys_name)
OSMO_STRLCPY_ARRAY(golh->subsys, subsys_name + 1);
else
golh->subsys[0] = '\0';
/* strip all leading path elements from file, if any. */
file_basename = strrchr(file, '/');
file = (file_basename && file_basename[1])? file_basename + 1 : file;
OSMO_STRLCPY_ARRAY(golh->src_file.name, file);
golh->src_file.line_nr = osmo_htonl(line);
golh->level = level;
/* we always store the timestamp in the message, irrespective
* of hat prrint_[ext_]timestamp say */
golh->ts.sec = osmo_htonl(tv.tv_sec);
golh->ts.usec = osmo_htonl(tv.tv_usec);
rc = vsnprintf((char *) msg->tail, msgb_tailroom(msg), format, ap);
if (rc < 0) {
msgb_free(msg);
return;
} else if (rc >= msgb_tailroom(msg)) {
/* If the output was truncated, vsnprintf() returns the
* number of characters which would have been written
* if enough space had been available (excluding '\0'). */
rc = msgb_tailroom(msg);
msg->tail[rc - 1] = '\0';
}
msgb_put(msg, rc);
rc = gsmtap_sendmsg(target->tgt_gsmtap.gsmtap_inst, msg);
if (rc)
msgb_free(msg);
}
/*! Create a new logging target for GSMTAP logging
* \param[in] ident string identifier
* \returns Log target in case of success, NULL in case of error
*/
struct log_target *log_target_create_gsmtap(const char *host, uint16_t port,
const char *ident,
bool ofd_wq_mode,
bool add_sink)
{
struct log_target *target;
struct gsmtap_inst *gti;
target = log_target_create();
if (!target)
return NULL;
gti = gsmtap_source_init(host, port, ofd_wq_mode);
if (!gti) {
log_target_destroy(target);
return NULL;
}
if (add_sink)
gsmtap_source_add_sink(gti);
target->tgt_gsmtap.gsmtap_inst = gti;
target->tgt_gsmtap.ident = talloc_strdup(target, ident);
target->tgt_gsmtap.hostname = talloc_strdup(target, host);
target->type = LOG_TGT_TYPE_GSMTAP;
target->raw_output = _gsmtap_raw_output;
return target;
}
/* @} */