mirror of
https://github.com/baresip/baresip
synced 2026-08-07 00:26:06 -04:00
375 lines
8.3 KiB
C
375 lines
8.3 KiB
C
/**
|
|
* @file debug_cmd.c Debug commands
|
|
*
|
|
* Copyright (C) 2010 - 2016 Alfred E. Heggestad
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#ifdef USE_OPENSSL
|
|
#include <openssl/crypto.h>
|
|
#endif
|
|
#include <re.h>
|
|
#include <rem.h>
|
|
#include <baresip.h>
|
|
|
|
|
|
/**
|
|
* @defgroup debug_cmd debug_cmd
|
|
*
|
|
* Advanced debug commands
|
|
*/
|
|
|
|
|
|
static uint64_t start_ticks; /**< Ticks when app started */
|
|
static time_t start_time; /**< Start time of application */
|
|
static struct play *g_play;
|
|
|
|
|
|
static int cmd_net_debug(struct re_printf *pf, void *unused)
|
|
{
|
|
(void)unused;
|
|
return net_debug(pf, baresip_network());
|
|
}
|
|
|
|
|
|
static int print_system_info(struct re_printf *pf, void *arg)
|
|
{
|
|
uint32_t uptime;
|
|
int err = 0;
|
|
|
|
(void)arg;
|
|
|
|
uptime = (uint32_t)((long long)(tmr_jiffies() - start_ticks)/1000);
|
|
|
|
err |= re_hprintf(pf, "\n--- System info: ---\n");
|
|
|
|
err |= re_hprintf(pf, " Machine: %s/%s\n", sys_arch_get(),
|
|
sys_os_get());
|
|
err |= re_hprintf(pf, " Version: %s (libre v%s)\n",
|
|
baresip_version(), sys_libre_version_get());
|
|
err |= re_hprintf(pf, " Build: %H\n", sys_build_get, NULL);
|
|
err |= re_hprintf(pf, " Kernel: %H\n", sys_kernel_get, NULL);
|
|
err |= re_hprintf(pf, " Uptime: %H\n", fmt_human_time, &uptime);
|
|
err |= re_hprintf(pf, " Started: %s", ctime(&start_time));
|
|
|
|
#ifdef __VERSION__
|
|
err |= re_hprintf(pf, " Compiler: %s\n", __VERSION__);
|
|
#endif
|
|
|
|
#ifdef USE_OPENSSL
|
|
err |= re_hprintf(pf, " OpenSSL: %s\n",
|
|
OpenSSL_version(OPENSSL_VERSION));
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static int cmd_config_print(struct re_printf *pf, void *unused)
|
|
{
|
|
(void)unused;
|
|
return config_print(pf, conf_config());
|
|
}
|
|
|
|
|
|
static int cmd_ua_debug(struct re_printf *pf, void *unused)
|
|
{
|
|
struct le *le;
|
|
int err;
|
|
(void)unused;
|
|
|
|
if (list_isempty(uag_list()))
|
|
return re_hprintf(pf, "(no user-agent)\n");
|
|
|
|
for (le = list_head(uag_list()); le; le = le->next) {
|
|
struct ua *ua = le->data;
|
|
|
|
err = ua_debug(pf, ua);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns all the User-Agents and their general codec state.
|
|
* Formatted as JSON, for use with TCP / MQTT API interface.
|
|
* JSON object with 'cuser' as the key.
|
|
*
|
|
* @return All User-Agents available, NULL if none
|
|
*/
|
|
static int cmd_api_uastate(struct re_printf *pf, void *unused)
|
|
{
|
|
struct odict *od = NULL;
|
|
struct le *le;
|
|
int err;
|
|
(void)unused;
|
|
|
|
err = odict_alloc(&od, 8);
|
|
if (err)
|
|
return err;
|
|
|
|
for (le = list_head(uag_list()); le && !err; le = le->next) {
|
|
const struct ua *ua = le->data;
|
|
struct odict *odua;
|
|
|
|
err = odict_alloc(&odua, 8);
|
|
|
|
err |= ua_state_json_api(odua, ua);
|
|
err |= odict_entry_add(od, account_aor(ua_account(ua)),
|
|
ODICT_OBJECT, odua);
|
|
mem_deref(odua);
|
|
}
|
|
|
|
err |= json_encode_odict(pf, od);
|
|
if (err)
|
|
warning("debug: failed to encode json (%m)\n", err);
|
|
|
|
mem_deref(od);
|
|
|
|
return re_hprintf(pf, "\n");
|
|
}
|
|
|
|
|
|
static int cmd_play_file(struct re_printf *pf, void *arg)
|
|
{
|
|
struct cmd_arg *carg = arg;
|
|
struct config *cfg;
|
|
const char *filename = carg->prm;
|
|
int err = 0;
|
|
|
|
cfg = conf_config();
|
|
|
|
/* Stop the current tone, if any */
|
|
g_play = mem_deref(g_play);
|
|
|
|
if (str_isset(filename))
|
|
{
|
|
err = re_hprintf(pf, "playing audio file \"%s\" ..\n",
|
|
filename);
|
|
if (err)
|
|
return err;
|
|
|
|
err = play_file(&g_play, baresip_player(), filename, 0,
|
|
cfg->audio.alert_mod, cfg->audio.alert_dev);
|
|
if (err)
|
|
{
|
|
warning("debug_cmd: play_file(%s) failed (%m)\n",
|
|
filename, err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static void print_fileinfo(struct ausrc_prm *prm)
|
|
{
|
|
double s = ((float) prm->duration) / 1000;
|
|
|
|
if (prm->duration) {
|
|
info("debug_cmd: length = %1.3lf seconds\n", s);
|
|
module_event("debug_cmd", "aufileinfo", NULL, NULL,
|
|
"length = %lf seconds", s);
|
|
}
|
|
else {
|
|
info("debug_cmd: timeout\n");
|
|
module_event("debug_cmd", "aufileinfo", NULL, NULL,
|
|
"length unknown");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Command aufileinfo reads given audio file with ausrc that is specified in
|
|
* config file_ausrc, computes the length in milli seconds and sends a ua_event
|
|
* to inform about the result. The file has to be located in the path specified
|
|
* by audio_path.
|
|
*
|
|
* Usage:
|
|
* /aufileinfo audiofile
|
|
*
|
|
* @param pf Print handler is used to return length.
|
|
* @param arg Command argument contains the file name.
|
|
*
|
|
* @return 0 if success, otherwise errorcode
|
|
*/
|
|
/* ------------------------------------------------------------------------- */
|
|
static int cmd_aufileinfo(struct re_printf *pf, void *arg)
|
|
{
|
|
const struct cmd_arg *carg = arg;
|
|
int err = 0;
|
|
char *path;
|
|
char aumod[16];
|
|
const char *file = carg->prm;
|
|
struct ausrc_prm prm;
|
|
|
|
if (!str_isset(file)) {
|
|
re_hprintf(pf, "fileplay: filename not specified\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
err = conf_get_str(conf_cur(), "file_ausrc", aumod, sizeof(aumod));
|
|
if (err) {
|
|
warning("debug_cmd: file_ausrc is not set\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
/* absolute path? */
|
|
if (file[0] == '/' ||
|
|
!re_regex(file, strlen(file), "https://") ||
|
|
!re_regex(file, strlen(file), "http://") ||
|
|
!re_regex(file, strlen(file), "file://")) {
|
|
if (re_sdprintf(&path, "%s", file) < 0)
|
|
return ENOMEM;
|
|
}
|
|
else if (re_sdprintf(&path, "%s/%s",
|
|
conf_config()->audio.audio_path, file) < 0)
|
|
return ENOMEM;
|
|
|
|
err = ausrc_info(baresip_ausrcl(), aumod, &prm, path);
|
|
if (err) {
|
|
warning("debug_cmd: %s - ausrc %s does not support info query "
|
|
"or reading source %s failed. (%m)\n",
|
|
__func__, aumod, carg->prm, err);
|
|
goto out;
|
|
}
|
|
|
|
print_fileinfo(&prm);
|
|
out:
|
|
|
|
mem_deref(path);
|
|
return err;
|
|
}
|
|
|
|
|
|
static int cmd_sip_debug(struct re_printf *pf, void *unused)
|
|
{
|
|
(void)unused;
|
|
return sip_debug(pf, uag_sip());
|
|
}
|
|
|
|
static int cmd_sip_trace(struct re_printf *pf, void *arg)
|
|
{
|
|
struct cmd_arg *carg = arg;
|
|
const char *prm = carg->prm;
|
|
|
|
/* assume false since the trace state is not exposed from libre */
|
|
static bool enabled = false;
|
|
|
|
if (prm)
|
|
str_bool(&enabled, prm);
|
|
else
|
|
enabled = !enabled;
|
|
|
|
re_hprintf(pf, "debug_cmd: SIP trace is now %s\n",
|
|
enabled ? "enabled" : "disabled");
|
|
uag_enable_sip_trace(enabled);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int reload_config(struct re_printf *pf, void *arg)
|
|
{
|
|
int err;
|
|
(void)arg;
|
|
|
|
err = re_hprintf(pf, "reloading config file ..\n");
|
|
if (err)
|
|
return err;
|
|
|
|
err = conf_configure();
|
|
if (err) {
|
|
(void)re_hprintf(pf, "reload_config failed: %m\n", err);
|
|
return err;
|
|
}
|
|
|
|
(void)re_hprintf(pf, "done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int cmd_log_level(struct re_printf *pf, void *unused)
|
|
{
|
|
int level;
|
|
(void)unused;
|
|
|
|
level = log_level_get();
|
|
|
|
--level;
|
|
|
|
if (level < LEVEL_DEBUG)
|
|
level = LEVEL_ERROR;
|
|
|
|
log_level_set(level);
|
|
|
|
return re_hprintf(pf, "Log level '%s'\n", log_level_name(level));
|
|
}
|
|
|
|
|
|
static int print_uuid(struct re_printf *pf, void *arg)
|
|
{
|
|
struct config *cfg = conf_config();
|
|
(void)arg;
|
|
|
|
if (cfg)
|
|
re_hprintf(pf, "UUID: %s\n", cfg->sip.uuid);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct cmd debugcmdv[] = {
|
|
{"apistate", 0, 0, "User Agent state", cmd_api_uastate },
|
|
{"aufileinfo", 0, CMD_PRM, "Audio file info", cmd_aufileinfo },
|
|
{"conf_reload", 0, 0, "Reload config file", reload_config },
|
|
{"config", 0, 0, "Print configuration", cmd_config_print },
|
|
{"loglevel", 'v', 0, "Log level toggle", cmd_log_level },
|
|
{"main", 0, 0, "Main loop debug", re_debug },
|
|
{"memstat", 'y', 0, "Memory status", mem_status },
|
|
{"modules", 0, 0, "Module debug", mod_debug },
|
|
{"netstat", 'n', 0, "Network debug", cmd_net_debug },
|
|
{"play", 0, CMD_PRM, "Play audio file", cmd_play_file },
|
|
{"sipstat", 'i', 0, "SIP debug", cmd_sip_debug },
|
|
{"siptrace", 0, CMD_PRM, "SIP trace", cmd_sip_trace },
|
|
{"sysinfo", 's', 0, "System info", print_system_info },
|
|
{"timers", 0, 0, "Timer debug", tmr_status },
|
|
{"uastat", 'u', 0, "UA debug", cmd_ua_debug },
|
|
{"uuid", 0, 0, "Print UUID", print_uuid },
|
|
};
|
|
|
|
|
|
static int module_init(void)
|
|
{
|
|
int err;
|
|
|
|
start_ticks = tmr_jiffies();
|
|
(void)time(&start_time);
|
|
|
|
err = cmd_register(baresip_commands(),
|
|
debugcmdv, RE_ARRAY_SIZE(debugcmdv));
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
static int module_close(void)
|
|
{
|
|
cmd_unregister(baresip_commands(), debugcmdv);
|
|
|
|
g_play = mem_deref(g_play);
|
|
return 0;
|
|
}
|
|
|
|
|
|
const struct mod_export DECL_EXPORTS(debug_cmd) = {
|
|
"debug_cmd",
|
|
"application",
|
|
module_init,
|
|
module_close
|
|
};
|