/* SPDX-License-Identifier: LGPL-2.1+ */ #include "nm-default.h" #include #include "devices/bluetooth/nm-bluez5-dun.h" #include "nm-test-utils-core.h" /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_BT #define _NMLOG(level, ...) \ nm_log((level), \ _NMLOG_DOMAIN, \ NULL, \ NULL, \ "bt%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ NM_PRINT_FMT_QUOTED(gl.argv_cmd, "[", gl.argv_cmd, "]", "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)) /*****************************************************************************/ struct { int argc; const char *const *argv; const char * argv_cmd; GMainLoop * loop; } gl; typedef struct _MainCmdInfo { const char *name; int (*main_func)(const struct _MainCmdInfo *main_cmd_info); } MainCmdInfo; /*****************************************************************************/ #if WITH_BLUEZ5_DUN typedef struct { NMBluez5DunContext *dun_context; GCancellable * cancellable; guint timeout_id; guint sig_term_id; guint sig_int_id; } DunConnectData; static void _dun_connect_cb(NMBluez5DunContext *context, const char * rfcomm_dev, GError * error, gpointer user_data) { DunConnectData *dun_connect_data = user_data; g_assert(dun_connect_data); g_assert(!dun_connect_data->dun_context); g_assert((!!error) != (!!rfcomm_dev)); if (rfcomm_dev && !context) { _LOGI("dun-connect notifies path \"%s\". Wait longer...", rfcomm_dev); return; } if (rfcomm_dev) { g_assert(context); _LOGI("dun-connect completed with path \"%s\"", rfcomm_dev); } else { g_assert(!context); _LOGI("dun-connect failed with error: %s", error->message); } dun_connect_data->dun_context = context; g_main_loop_quit(gl.loop); } static void _dun_notify_tty_hangup_cb(NMBluez5DunContext *context, gpointer user_data) { _LOGI("dun-connect: notified TTY hangup"); } static gboolean _timeout_cb(gpointer user_data) { DunConnectData *dun_connect_data = user_data; _LOGI("timeout"); dun_connect_data->timeout_id = 0; if (dun_connect_data->cancellable) g_cancellable_cancel(dun_connect_data->cancellable); return G_SOURCE_REMOVE; } static gboolean _sig_xxx_cb(DunConnectData *dun_connect_data, int sigid) { _LOGI("signal %s received", sigid == SIGTERM ? "SIGTERM" : "SIGINT"); g_main_loop_quit(gl.loop); return G_SOURCE_CONTINUE; } static gboolean _sig_term_cb(gpointer user_data) { return _sig_xxx_cb(user_data, SIGTERM); } static gboolean _sig_int_cb(gpointer user_data) { return _sig_xxx_cb(user_data, SIGINT); } #endif static int do_dun_connect(const MainCmdInfo *main_cmd_info) { #if WITH_BLUEZ5_DUN gs_unref_object GCancellable *cancellable = NULL; gs_free_error GError *error = NULL; const char * adapter; const char * remote; DunConnectData dun_connect_data = {}; if (gl.argc < 4) { _LOGE("missing arguments \"adapter\" and \"remote\""); return -1; } adapter = gl.argv[2]; remote = gl.argv[3]; cancellable = g_cancellable_new(); dun_connect_data.cancellable = cancellable; if (!nm_bluez5_dun_connect(adapter, remote, cancellable, _dun_connect_cb, &dun_connect_data, _dun_notify_tty_hangup_cb, &dun_connect_data, &error)) { _LOGE("connect failed to start: %s", error->message); return -1; } dun_connect_data.timeout_id = g_timeout_add(60000, _timeout_cb, &dun_connect_data); g_main_loop_run(gl.loop); nm_clear_g_source(&dun_connect_data.timeout_id); if (dun_connect_data.dun_context) { dun_connect_data.sig_term_id = g_unix_signal_add(SIGTERM, _sig_term_cb, &dun_connect_data); dun_connect_data.sig_int_id = g_unix_signal_add(SIGINT, _sig_int_cb, &dun_connect_data); g_main_loop_run(gl.loop); nm_clear_g_source(&dun_connect_data.sig_term_id); nm_clear_g_source(&dun_connect_data.sig_int_id); nm_bluez5_dun_disconnect(g_steal_pointer(&dun_connect_data.dun_context)); } return 0; #else _LOGE("compiled without bluetooth DUN support"); return 1; #endif } /*****************************************************************************/ NMTST_DEFINE(); int main(int argc, char **argv) { static const MainCmdInfo main_cmd_infos[] = { { .name = "dun-connect", .main_func = do_dun_connect, }, }; int exit_code = 0; guint i; if (!g_getenv("G_MESSAGES_DEBUG")) g_setenv("G_MESSAGES_DEBUG", "all", TRUE); nmtst_init_with_logging(&argc, &argv, "DEBUG", "ALL"); nm_logging_init(NULL, TRUE); gl.argv = (const char *const *) argv; gl.argc = argc; gl.loop = g_main_loop_new(NULL, FALSE); _LOGI("bluetooth test util start"); gl.argv_cmd = argc >= 2 ? argv[1] : NULL; for (i = 0; i < G_N_ELEMENTS(main_cmd_infos); i++) { if (nm_streq0(main_cmd_infos[i].name, gl.argv_cmd)) { _LOGD("start \"%s\"", gl.argv_cmd); exit_code = main_cmd_infos[i].main_func(&main_cmd_infos[i]); _LOGD("completed with %d", exit_code); break; } } if (gl.argv_cmd && i >= G_N_ELEMENTS(main_cmd_infos)) { nm_log_err(LOGD_BT, "invalid command \"%s\"", gl.argv_cmd); exit_code = -1; } nm_clear_pointer(&gl.loop, g_main_loop_unref); return exit_code; }