mirror of
https://github.com/albfan/miraclecast.git
synced 2025-03-09 23:38:56 +00:00
We should notify miraclectl about ScanStopped events so we can correctly track active scans. If we don't do that, we would have to explicitly track it on connect/disconnect/etc. commands, which seems cumbersome given the undeterminisc actions they cause. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
2173 lines
46 KiB
C
2173 lines
46 KiB
C
/*
|
|
* MiracleCast - Wifi-Display/Miracast Implementation
|
|
*
|
|
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <locale.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/signalfd.h>
|
|
#include <systemd/sd-bus.h>
|
|
#include "miracle.h"
|
|
#include "shl_macro.h"
|
|
#include "shl_util.h"
|
|
|
|
/* *sigh* readline doesn't include all their deps, so put them last */
|
|
#include <readline/history.h>
|
|
#include <readline/readline.h>
|
|
|
|
static sd_bus *bus;
|
|
static char *selected_link;
|
|
|
|
/*
|
|
* Helpers for interactive commands
|
|
*/
|
|
|
|
static sd_event *cli_event;
|
|
static sd_event_source *cli_sigs[_NSIG];
|
|
static sd_event_source *cli_stdin;
|
|
static bool cli_rl;
|
|
static int cli_max_sev = LOG_NOTICE;
|
|
|
|
#define CLI_DEFAULT "\x1B[0m"
|
|
#define CLI_RED "\x1B[0;91m"
|
|
#define CLI_GREEN "\x1B[0;92m"
|
|
#define CLI_YELLOW "\x1B[0;93m"
|
|
#define CLI_BLUE "\x1B[0;94m"
|
|
#define CLI_BOLDGRAY "\x1B[1;30m"
|
|
#define CLI_BOLDWHITE "\x1B[1;37m"
|
|
#define CLI_PROMPT CLI_BLUE "[miraclectl] # " CLI_DEFAULT
|
|
|
|
static const struct cli_cmd {
|
|
const char *cmd;
|
|
const char *args;
|
|
enum {
|
|
CLI_N, /* no */
|
|
CLI_M, /* maybe */
|
|
CLI_Y, /* yes */
|
|
} cli_cmp;
|
|
enum {
|
|
MORE,
|
|
LESS,
|
|
EQUAL,
|
|
} argc_cmp;
|
|
int argc;
|
|
int (*fn) (char **args, unsigned int n);
|
|
const char *desc;
|
|
} cli_cmds[];
|
|
|
|
static bool is_cli(void)
|
|
{
|
|
return cli_rl;
|
|
}
|
|
|
|
static void cli_printv(const char *fmt, va_list args)
|
|
{
|
|
SHL_PROTECT_ERRNO;
|
|
_shl_cleanup_free_ char *line = NULL;
|
|
int point;
|
|
bool async;
|
|
|
|
/* In case we print messages during readline() activity, we need to
|
|
* correctly save and restore RL-internal state. */
|
|
async = is_cli() && !RL_ISSTATE(RL_STATE_DONE);
|
|
|
|
if (async) {
|
|
point = rl_point;
|
|
line = rl_copy_text(0, rl_end);
|
|
rl_save_prompt();
|
|
rl_replace_line("", 0);
|
|
rl_redisplay();
|
|
}
|
|
|
|
vprintf(fmt, args);
|
|
|
|
if (async) {
|
|
rl_restore_prompt();
|
|
rl_replace_line(line, 0);
|
|
rl_point = point;
|
|
rl_redisplay();
|
|
}
|
|
}
|
|
|
|
static void cli_printf(const char *fmt, ...)
|
|
{
|
|
SHL_PROTECT_ERRNO;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
cli_printv(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
#define cli_log(_fmt, ...) \
|
|
cli_printf(_fmt "\n", ##__VA_ARGS__)
|
|
#define cli_log_fn(_fmt, ...) \
|
|
cli_printf(_fmt " (%s() in %s:%d)\n", ##__VA_ARGS__, __func__, __FILE__, __LINE__)
|
|
#define cli_error(_fmt, ...) \
|
|
((LOG_ERROR <= cli_max_sev) ? \
|
|
cli_log_fn("ERROR: " _fmt, ##__VA_ARGS__) : (void)0)
|
|
#define cli_warning(_fmt, ...) \
|
|
((LOG_WARNING <= cli_max_sev) ? \
|
|
cli_log_fn("WARNING: " _fmt, ##__VA_ARGS__) : (void)0)
|
|
#define cli_notice(_fmt, ...) \
|
|
((LOG_NOTICE <= cli_max_sev) ? \
|
|
cli_log("NOTICE: " _fmt, ##__VA_ARGS__) : (void)0)
|
|
#define cli_debug(_fmt, ...) \
|
|
((LOG_DEBUG <= cli_max_sev) ? \
|
|
cli_log_fn("DEBUG: " _fmt, ##__VA_ARGS__) : (void)0)
|
|
|
|
#define cli_EINVAL() \
|
|
(cli_error("invalid arguments"), -EINVAL)
|
|
#define cli_vEINVAL() \
|
|
((void)cli_EINVAL())
|
|
|
|
#define cli_EFAULT() \
|
|
(cli_error("internal operation failed"), -EFAULT)
|
|
#define cli_vEFAULT() \
|
|
((void)cli_EFAULT())
|
|
|
|
#define cli_ENOMEM() \
|
|
(cli_error("out of memory"), -ENOMEM)
|
|
#define cli_vENOMEM() \
|
|
((void)cli_ENOMEM())
|
|
|
|
#define cli_EPIPE() \
|
|
(cli_error("fd closed unexpectedly"), -EPIPE)
|
|
#define cli_vEPIPE() \
|
|
((void)cli_EPIPE())
|
|
|
|
#define cli_ERRNO() \
|
|
(cli_error("syscall failed (%d): %m", errno), -errno)
|
|
#define cli_vERRNO() \
|
|
((void)cli_ERRNO())
|
|
|
|
#define cli_ERR(_r) \
|
|
(errno = -(_r), cli_error("syscall failed (%d): %m", (_r)), (_r))
|
|
#define cli_vERR(_r) \
|
|
((void)cli_ERR(_r))
|
|
|
|
#define cli_log_parser(_r) \
|
|
(cli_error("cannot parse dbus message: %s", \
|
|
strerror((_r) < 0 ? -(_r) : (_r))), (_r))
|
|
|
|
static int cli_help(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (is_cli()) {
|
|
cli_printf("Available commands:\n");
|
|
} else {
|
|
/*
|
|
* 80-char barrier:
|
|
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
|
*/
|
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
|
"Send control command to or query the MiracleCast manager. If no arguments are\n"
|
|
"given, an interactive command-line tool is provided.\n\n"
|
|
" -h --help Show this help\n"
|
|
" --version Show package version\n"
|
|
" --log-level <lvl> Maximum level for log messages\n"
|
|
"\n"
|
|
"Commands:\n"
|
|
, program_invocation_short_name);
|
|
/*
|
|
* 80-char barrier:
|
|
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
|
*/
|
|
}
|
|
|
|
for (i = 0; cli_cmds[i].cmd; ++i) {
|
|
if (!cli_cmds[i].desc)
|
|
continue;
|
|
if (is_cli() && cli_cmds[i].cli_cmp == CLI_N)
|
|
continue;
|
|
if (!is_cli() && cli_cmds[i].cli_cmp == CLI_Y)
|
|
continue;
|
|
|
|
cli_printf(" %s %-*s %s\n",
|
|
cli_cmds[i].cmd,
|
|
(int)(25 - strlen(cli_cmds[i].cmd)),
|
|
cli_cmds[i].args ? : "",
|
|
cli_cmds[i].desc ? : "");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cli_do(char **args, unsigned int n)
|
|
{
|
|
unsigned int i;
|
|
const char *cmd;
|
|
int r;
|
|
|
|
if (!n)
|
|
return -EAGAIN;
|
|
|
|
cmd = *args++;
|
|
--n;
|
|
|
|
for (i = 0; cli_cmds[i].cmd; ++i) {
|
|
if (strcmp(cmd, cli_cmds[i].cmd))
|
|
continue;
|
|
if (is_cli() && cli_cmds[i].cli_cmp == CLI_N)
|
|
continue;
|
|
if (!is_cli() && cli_cmds[i].cli_cmp == CLI_Y)
|
|
continue;
|
|
|
|
switch (cli_cmds[i].argc_cmp) {
|
|
case EQUAL:
|
|
if (n != cli_cmds[i].argc) {
|
|
cli_printf("Invalid number of arguments\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
case MORE:
|
|
if (n < cli_cmds[i].argc) {
|
|
cli_printf("too few arguments\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
case LESS:
|
|
if (n > cli_cmds[i].argc) {
|
|
cli_printf("too many arguments\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (cli_cmds[i].fn) {
|
|
r = cli_cmds[i].fn(args, n);
|
|
return (r == -EAGAIN) ? -EINVAL : r;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!strcmp(cmd, "help"))
|
|
return cli_help();
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
static void cli_handler_fn(char *input)
|
|
{
|
|
_shl_cleanup_free_ char *original = input;
|
|
_shl_cleanup_strv_ char **args = NULL;
|
|
int r;
|
|
|
|
if (!input) {
|
|
rl_insert_text("quit");
|
|
rl_redisplay();
|
|
rl_crlf();
|
|
sd_event_exit(cli_event, 0);
|
|
return;
|
|
}
|
|
|
|
r = shl_qstr_tokenize(input, &args);
|
|
if (r < 0)
|
|
return cli_vENOMEM();
|
|
else if (!r)
|
|
return;
|
|
|
|
add_history(original);
|
|
r = cli_do(args, r);
|
|
if (r != -EAGAIN)
|
|
return;
|
|
|
|
cli_printf("Command not found\n");
|
|
}
|
|
|
|
static int cli_stdin_fn(sd_event_source *source,
|
|
int fd,
|
|
uint32_t mask,
|
|
void *data)
|
|
{
|
|
if (mask & EPOLLIN) {
|
|
rl_callback_read_char();
|
|
return 0;
|
|
}
|
|
|
|
if (mask & (EPOLLHUP | EPOLLERR))
|
|
sd_event_exit(cli_event, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cli_signal_fn(sd_event_source *source,
|
|
const struct signalfd_siginfo *ssi,
|
|
void *data)
|
|
{
|
|
if (ssi->ssi_signo == SIGCHLD) {
|
|
cli_debug("caught SIGCHLD for %d", (int)ssi->ssi_pid);
|
|
} else if (ssi->ssi_signo == SIGINT) {
|
|
rl_replace_line("", 0);
|
|
rl_crlf();
|
|
rl_on_new_line();
|
|
rl_redisplay();
|
|
} else {
|
|
cli_notice("caught signal %d, exiting..",
|
|
(int)ssi->ssi_signo);
|
|
sd_event_exit(cli_event, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cli_destroy(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!cli_event)
|
|
return;
|
|
|
|
if (cli_rl) {
|
|
cli_rl = false;
|
|
|
|
rl_replace_line("", 0);
|
|
rl_crlf();
|
|
rl_on_new_line();
|
|
rl_redisplay();
|
|
|
|
rl_message("");
|
|
rl_callback_handler_remove();
|
|
}
|
|
|
|
sd_event_source_unref(cli_stdin);
|
|
cli_stdin = NULL;
|
|
|
|
for (i = 0; cli_sigs[i]; ++i) {
|
|
sd_event_source_unref(cli_sigs[i]);
|
|
cli_sigs[i] = NULL;
|
|
}
|
|
|
|
sd_event_unref(cli_event);
|
|
cli_event = NULL;
|
|
}
|
|
|
|
static int cli_init(void)
|
|
{
|
|
static const int sigs[] = {
|
|
SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGPIPE, SIGCHLD, 0
|
|
};
|
|
unsigned int i;
|
|
sigset_t mask;
|
|
int r;
|
|
|
|
if (cli_event)
|
|
return cli_EINVAL();
|
|
|
|
r = sd_event_default(&cli_event);
|
|
if (r < 0) {
|
|
cli_vERR(r);
|
|
goto error;
|
|
}
|
|
|
|
r = sd_bus_attach_event(bus, cli_event, 0);
|
|
if (r < 0) {
|
|
cli_vERR(r);
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; sigs[i]; ++i) {
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, sigs[i]);
|
|
sigprocmask(SIG_BLOCK, &mask, NULL);
|
|
|
|
r = sd_event_add_signal(cli_event,
|
|
sigs[i],
|
|
cli_signal_fn,
|
|
NULL,
|
|
&cli_sigs[i]);
|
|
if (r < 0) {
|
|
cli_vERR(r);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
r = sd_event_add_io(cli_event,
|
|
fileno(stdin),
|
|
EPOLLHUP | EPOLLERR | EPOLLIN,
|
|
cli_stdin_fn,
|
|
NULL,
|
|
&cli_stdin);
|
|
if (r < 0) {
|
|
cli_vERR(r);
|
|
goto error;
|
|
}
|
|
|
|
cli_rl = true;
|
|
|
|
rl_erase_empty_line = 1;
|
|
rl_callback_handler_install(NULL, cli_handler_fn);
|
|
|
|
rl_set_prompt(CLI_PROMPT);
|
|
printf("\r");
|
|
rl_on_new_line();
|
|
rl_redisplay();
|
|
|
|
return 0;
|
|
|
|
error:
|
|
cli_destroy();
|
|
return r;
|
|
}
|
|
|
|
static int cli_run(void)
|
|
{
|
|
if (!cli_event)
|
|
return cli_EINVAL();
|
|
|
|
return sd_event_loop(cli_event);
|
|
}
|
|
|
|
/*
|
|
* cmd list
|
|
*/
|
|
|
|
static int cmd_list_link(sd_bus_message *m, const char *link)
|
|
{
|
|
const char *obj, *name = "<unknown>";
|
|
int r;
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sa{sv}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"sa{sv}")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (strcmp(obj, "org.freedesktop.miracle.Link")) {
|
|
r = sd_bus_message_skip(m, "a{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
continue;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(obj, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s",
|
|
&name);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
} else {
|
|
sd_bus_message_skip(m, "v");
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("%16s %-24s\n", link, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_list_links(sd_bus_message *m)
|
|
{
|
|
_shl_cleanup_free_ char *link = NULL;
|
|
unsigned int link_cnt = 0;
|
|
const char *obj;
|
|
int r;
|
|
|
|
cli_printf("%16s %-24s\n", "LINK-ID", "NAME");
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"oa{sa{sv}}")) > 0) {
|
|
r = sd_bus_message_read(m, "o", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
obj = shl_startswith(obj, "/org/freedesktop/miracle/link/");
|
|
if (!obj) {
|
|
r = sd_bus_message_skip(m, "a{sa{sv}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
continue;
|
|
}
|
|
|
|
free(link);
|
|
link = sd_bus_label_unescape(obj);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
|
|
++link_cnt;
|
|
r = cmd_list_link(m, link);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("\n");
|
|
|
|
return link_cnt;
|
|
}
|
|
|
|
static int cmd_list_peer(sd_bus_message *m,
|
|
const char *link_filter,
|
|
const char *peer)
|
|
{
|
|
_shl_cleanup_free_ char *link = NULL;
|
|
const char *obj, *name = "<unknown>";
|
|
int r, connected = 0;
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sa{sv}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"sa{sv}")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (strcmp(obj, "org.freedesktop.miracle.Peer")) {
|
|
r = sd_bus_message_skip(m, "a{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
continue;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(obj, "Link")) {
|
|
r = bus_message_read_basic_variant(m, "o",
|
|
&obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
obj = shl_startswith(obj,
|
|
"/org/freedesktop/miracle/link/");
|
|
if (obj) {
|
|
free(link);
|
|
link = sd_bus_label_unescape(obj);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
}
|
|
} else if (!strcmp(obj, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s",
|
|
&name);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
} else if (!strcmp(obj, "Connected")) {
|
|
r = bus_message_read_basic_variant(m, "b",
|
|
&connected);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
} else {
|
|
sd_bus_message_skip(m, "v");
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!link_filter || !strcmp(link_filter, link))
|
|
cli_printf("%16s %-9s %-24s %-10s\n",
|
|
link ? : "<none>",
|
|
peer,
|
|
name,
|
|
connected ? "yes" : "no");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_list_peers(sd_bus_message *m, const char *link_filter)
|
|
{
|
|
_shl_cleanup_free_ char *peer = NULL;
|
|
unsigned int peer_cnt = 0;
|
|
const char *obj;
|
|
int r;
|
|
|
|
cli_printf("%16s %-9s %-24s %-10s\n",
|
|
"LINK", "PEER-ID", "NAME", "CONNECTED");
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"oa{sa{sv}}")) > 0) {
|
|
r = sd_bus_message_read(m, "o", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
obj = shl_startswith(obj, "/org/freedesktop/miracle/peer/");
|
|
if (!obj) {
|
|
r = sd_bus_message_skip(m, "a{sa{sv}}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
continue;
|
|
}
|
|
|
|
free(peer);
|
|
peer = sd_bus_label_unescape(obj);
|
|
if (!peer)
|
|
return cli_ENOMEM();
|
|
|
|
++peer_cnt;
|
|
r = cmd_list_peer(m, link_filter, peer);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("\n");
|
|
|
|
return peer_cnt;
|
|
}
|
|
|
|
static int cmd_list(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
unsigned int link_cnt, peer_cnt;
|
|
int r;
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
"/org/freedesktop/miracle",
|
|
"org.freedesktop.DBus.ObjectManager",
|
|
"GetManagedObjects",
|
|
&err,
|
|
&m,
|
|
"");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve objects: %s",
|
|
bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
/* print links */
|
|
|
|
r = cmd_list_links(m);
|
|
if (r < 0)
|
|
return r;
|
|
link_cnt = r;
|
|
|
|
sd_bus_message_rewind(m, true);
|
|
|
|
/* print peers */
|
|
|
|
r = cmd_list_peers(m, NULL);
|
|
if (r < 0)
|
|
return r;
|
|
peer_cnt = r;
|
|
|
|
/* print stats */
|
|
|
|
cli_printf(" %u peers and %u links listed.\n", peer_cnt, link_cnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: select
|
|
*/
|
|
|
|
static int cmd_select(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
int r;
|
|
|
|
if (!n) {
|
|
if (selected_link) {
|
|
cli_printf("link %s deselected\n", selected_link);
|
|
free(selected_link);
|
|
selected_link = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
name = sd_bus_label_escape(args[0]);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"Get",
|
|
&err,
|
|
NULL,
|
|
"ss", "org.freedesktop.miracle.Link", "Type");
|
|
if (r < 0) {
|
|
cli_error("unknown link %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
free(selected_link);
|
|
selected_link = strdup(args[0]);
|
|
if (!selected_link)
|
|
return cli_ENOMEM();
|
|
|
|
cli_printf("link %s selected\n", selected_link);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: show-link
|
|
*/
|
|
|
|
static int cmd_show_link(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
_shl_cleanup_free_ char *type = NULL, *iface = NULL, *fname = NULL;
|
|
const char *t, *arg_link;
|
|
int r;
|
|
|
|
if (n > 0)
|
|
arg_link = args[0];
|
|
else if (!(arg_link = selected_link))
|
|
return log_error("no link selected"), -EINVAL;
|
|
|
|
name = sd_bus_label_escape(arg_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"GetAll",
|
|
&err,
|
|
&m,
|
|
"s", "org.freedesktop.miracle.Link");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve link %s: %s",
|
|
arg_link, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(t, "Type")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(type);
|
|
type = strdup(t);
|
|
if (!type)
|
|
return cli_ENOMEM();
|
|
} else if (!strcmp(t, "Interface")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(iface);
|
|
iface = strdup(t);
|
|
if (!iface)
|
|
return cli_ENOMEM();
|
|
} else if (!strcmp(t, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(fname);
|
|
fname = strdup(t);
|
|
if (!fname)
|
|
return cli_ENOMEM();
|
|
} else {
|
|
r = sd_bus_message_skip(m, "v");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("Link=%s\n", arg_link);
|
|
if (type)
|
|
cli_printf("Type=%s\n", type);
|
|
if (iface)
|
|
cli_printf("Interface=%s\n", iface);
|
|
if (fname)
|
|
cli_printf("Name=%s\n", fname);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: show-peer
|
|
*/
|
|
|
|
static int cmd_show_peer(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
_shl_cleanup_free_ char *link = NULL, *fname = NULL, *iface = NULL;
|
|
_shl_cleanup_free_ char *laddr = NULL, *raddr = NULL;
|
|
const char *t;
|
|
int r, is_connected = false;
|
|
|
|
name = sd_bus_label_escape(args[0]);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"GetAll",
|
|
&err,
|
|
&m,
|
|
"s", "org.freedesktop.miracle.Peer");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve peer %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(t, "Link")) {
|
|
r = bus_message_read_basic_variant(m, "o", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
t = shl_startswith(t,
|
|
"/org/freedesktop/miracle/link/");
|
|
if (t) {
|
|
free(link);
|
|
link = sd_bus_label_unescape(t);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
}
|
|
} else if (!strcmp(t, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(fname);
|
|
fname = strdup(t);
|
|
if (!fname)
|
|
return cli_ENOMEM();
|
|
} else if (!strcmp(t, "Connected")) {
|
|
r = bus_message_read_basic_variant(m, "b",
|
|
&is_connected);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
} else if (!strcmp(t, "Interface")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(iface);
|
|
iface = strdup(t);
|
|
if (!iface)
|
|
return cli_ENOMEM();
|
|
} else if (!strcmp(t, "LocalAddress")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(laddr);
|
|
laddr = strdup(t);
|
|
if (!laddr)
|
|
return cli_ENOMEM();
|
|
} else if (!strcmp(t, "RemoteAddress")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(raddr);
|
|
raddr = strdup(t);
|
|
if (!raddr)
|
|
return cli_ENOMEM();
|
|
} else {
|
|
r = sd_bus_message_skip(m, "v");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("Peer=%s\n", args[0]);
|
|
if (link)
|
|
cli_printf("Link=%s\n", link);
|
|
if (fname)
|
|
cli_printf("Name=%s\n", fname);
|
|
cli_printf("Connected=%d\n", is_connected);
|
|
if (iface)
|
|
cli_printf("Interface=%s\n", iface);
|
|
if (laddr)
|
|
cli_printf("LocalAddress=%s\n", laddr);
|
|
if (raddr)
|
|
cli_printf("RemoteAddress=%s\n", raddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: add-link
|
|
*/
|
|
|
|
static int cmd_add_link(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *link = NULL, *type = NULL;
|
|
const char *name;
|
|
char *t, *iface;
|
|
int r;
|
|
|
|
type = strdup(args[0]);
|
|
if (!type)
|
|
return cli_ENOMEM();
|
|
|
|
t = strchr(type, ':');
|
|
if (!t)
|
|
return cli_EINVAL();
|
|
|
|
*t = 0;
|
|
iface = t + 1;
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
"/org/freedesktop/miracle",
|
|
"org.freedesktop.miracle.Manager",
|
|
"AddLink",
|
|
&err,
|
|
&m,
|
|
"ss", type, iface);
|
|
if (r < 0) {
|
|
cli_error("cannot add link %s:%s: %s",
|
|
type, iface, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_read(m, "s", &name);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
link = sd_bus_label_unescape(name);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
|
|
cli_printf("link %s added\n", link);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: remove-link
|
|
*/
|
|
|
|
static int cmd_remove_link(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
int r;
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
"/org/freedesktop/miracle",
|
|
"org.freedesktop.miracle.Manager",
|
|
"RemoveLink",
|
|
&err,
|
|
NULL,
|
|
"s", args[0]);
|
|
if (r < 0) {
|
|
cli_error("cannot remove link %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("link %s removed\n", args[0]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: set-link-name
|
|
*/
|
|
|
|
static int cmd_set_link_name(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
const char *arg_link, *arg_name;
|
|
int r;
|
|
|
|
arg_link = args[0];
|
|
arg_name = args[1];
|
|
if (n < 2) {
|
|
if (!(arg_link = selected_link))
|
|
return log_error("no link selected"), -EINVAL;
|
|
|
|
arg_name = args[0];
|
|
}
|
|
|
|
name = sd_bus_label_escape(arg_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_message_new_method_call(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"Set",
|
|
&m);
|
|
if (r < 0)
|
|
return log_bus_create(r);
|
|
|
|
r = sd_bus_message_append(m, "ss",
|
|
"org.freedesktop.miracle.Link", "Name");
|
|
if (r < 0)
|
|
return log_bus_create(r);
|
|
|
|
r = sd_bus_message_open_container(m, 'v', "s");
|
|
if (r < 0)
|
|
return log_bus_create(r);
|
|
|
|
r = sd_bus_message_append(m, "s", arg_name);
|
|
if (r < 0)
|
|
return log_bus_create(r);
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
if (r < 0)
|
|
return log_bus_create(r);
|
|
|
|
r = sd_bus_call(bus, m, 0, &err, NULL);
|
|
if (r < 0) {
|
|
cli_error("cannot set friendly-name to %s on link %s: %s",
|
|
arg_name, arg_link, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Friendly-name set to %s on link %s\n",
|
|
arg_name, arg_link);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: start-scan
|
|
*/
|
|
|
|
static int cmd_start_scan(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
const char *arg_link;
|
|
int r;
|
|
|
|
if (n > 0)
|
|
arg_link = args[0];
|
|
else if (!(arg_link = selected_link))
|
|
return log_error("no link selected"), -EINVAL;
|
|
|
|
name = sd_bus_label_escape(arg_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Link",
|
|
"StartScan",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r < 0) {
|
|
cli_error("cannot start scan on link %s: %s",
|
|
arg_link, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Scan started on link %s\n", arg_link);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: stop-scan
|
|
*/
|
|
|
|
static int cmd_stop_scan(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
const char *arg_link;
|
|
int r;
|
|
|
|
if (n > 0)
|
|
arg_link = args[0];
|
|
else if (!(arg_link = selected_link))
|
|
return log_error("no link selected"), -EINVAL;
|
|
|
|
name = sd_bus_label_escape(arg_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Link",
|
|
"StopScan",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r < 0) {
|
|
cli_error("cannot stop scan on link %s: %s",
|
|
arg_link, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Scan stopped on link %s\n", arg_link);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: scan
|
|
*/
|
|
|
|
static char *scan_link;
|
|
|
|
static int cmd_scan_stop(bool async)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
int r;
|
|
|
|
if (!scan_link)
|
|
return 0;
|
|
|
|
name = sd_bus_label_escape(scan_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Link",
|
|
"StopScan",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r >= 0)
|
|
cli_printf("Scan stopped on link %s\n", scan_link);
|
|
else if (async &&
|
|
sd_bus_error_has_name(&err, SD_BUS_ERROR_UNKNOWN_OBJECT))
|
|
/* ignore */ ;
|
|
else
|
|
cli_error("cannot stop scan on link %s: %s",
|
|
scan_link, bus_error_message(&err, r));
|
|
|
|
free(scan_link);
|
|
scan_link = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void cmd_scan_list(void)
|
|
{
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
int r;
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
"/org/freedesktop/miracle",
|
|
"org.freedesktop.DBus.ObjectManager",
|
|
"GetManagedObjects",
|
|
&err,
|
|
&m,
|
|
"");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve objects: %s",
|
|
bus_error_message(&err, r));
|
|
return;
|
|
}
|
|
|
|
cmd_list_peers(m, scan_link);
|
|
}
|
|
|
|
static int cmd_scan(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
const char *arg_link;
|
|
int r;
|
|
|
|
if (n > 0 && !strcmp(args[0], "stop"))
|
|
return cmd_scan_stop(false);
|
|
|
|
if (scan_link) {
|
|
log_error("another managed scan is already running on link %s",
|
|
scan_link);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (n > 0)
|
|
arg_link = args[0];
|
|
else if (!(arg_link = selected_link))
|
|
return log_error("no link selected"), -EINVAL;
|
|
|
|
name = sd_bus_label_escape(arg_link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
scan_link = strdup(arg_link);
|
|
if (!scan_link)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Link",
|
|
"StartScan",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r < 0) {
|
|
cli_warning("cannot start scan on link %s (already running?): %s",
|
|
arg_link, bus_error_message(&err, r));
|
|
return -EINVAL;
|
|
}
|
|
|
|
cli_printf("Scan started on link %s, listing peers..\n", arg_link);
|
|
cmd_scan_list();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: allow
|
|
*/
|
|
|
|
static int cmd_allow(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *name = NULL, *path = NULL;
|
|
const char *pin = "";
|
|
int r;
|
|
|
|
if (n > 1)
|
|
pin = args[1];
|
|
|
|
name = sd_bus_label_escape(args[0]);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Peer",
|
|
"Allow",
|
|
&err,
|
|
NULL,
|
|
"s", pin);
|
|
if (r < 0) {
|
|
cli_error("cannot allow provision-request for peer %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Provision request allowed for peer %s\n", args[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: reject
|
|
*/
|
|
|
|
static int cmd_reject(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *name = NULL, *path = NULL;
|
|
int r;
|
|
|
|
name = sd_bus_label_escape(args[0]);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Peer",
|
|
"Reject",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r < 0) {
|
|
cli_error("cannot reject provision-request for peer %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Provision request rejected for peer %s\n", args[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: connect
|
|
*/
|
|
|
|
static int cmd_connect(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *name = NULL, *path = NULL;
|
|
const char *peer, *prov, *pin;
|
|
int r;
|
|
|
|
peer = args[0];
|
|
prov = (n > 1) ? args[1] : "";
|
|
pin = (n > 2) ? args[2] : "";
|
|
|
|
name = sd_bus_label_escape(peer);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Peer",
|
|
"Connect",
|
|
&err,
|
|
NULL,
|
|
"ss", prov, pin);
|
|
if (r < 0) {
|
|
cli_error("cannot connect to peer %s: %s",
|
|
peer, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Connecting to peer %s\n", peer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: disconnect
|
|
*/
|
|
|
|
static int cmd_disconnect(char **args, unsigned int n)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_shl_cleanup_free_ char *name = NULL, *path = NULL;
|
|
int r;
|
|
|
|
name = sd_bus_label_escape(args[0]);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.miracle.Peer",
|
|
"Disconnect",
|
|
&err,
|
|
NULL,
|
|
NULL);
|
|
if (r < 0) {
|
|
cli_error("cannot disconnect from peer %s: %s",
|
|
args[0], bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
cli_printf("Disconnected from peer %s\n", args[0]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cmd: quit/exit
|
|
*/
|
|
|
|
static int cmd_quit(char **args, unsigned int n)
|
|
{
|
|
sd_event_exit(cli_event, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* filters
|
|
*/
|
|
|
|
static int filters_show_peer(const char *peer)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL;
|
|
_shl_cleanup_free_ char *link = NULL, *fname = NULL;
|
|
const char *t;
|
|
int r;
|
|
|
|
name = sd_bus_label_escape(peer);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/peer/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"GetAll",
|
|
&err,
|
|
&m,
|
|
"s", "org.freedesktop.miracle.Peer");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve peer %s: %s",
|
|
peer, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(t, "Link")) {
|
|
r = bus_message_read_basic_variant(m, "o", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
t = shl_startswith(t,
|
|
"/org/freedesktop/miracle/link/");
|
|
if (t) {
|
|
free(link);
|
|
link = sd_bus_label_unescape(t);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
}
|
|
} else if (!strcmp(t, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(fname);
|
|
fname = strdup(t);
|
|
if (!fname)
|
|
return cli_ENOMEM();
|
|
} else {
|
|
r = sd_bus_message_skip(m, "v");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Peer: %s@%s Name: %s\n",
|
|
peer, link ? : "<none>", fname ? : "<unknown>");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_show_link(const char *link)
|
|
{
|
|
_cleanup_sd_bus_error_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
|
_cleanup_sd_bus_message_ sd_bus_message *m = NULL;
|
|
_shl_cleanup_free_ char *path = NULL, *name = NULL, *fname = NULL;
|
|
const char *t;
|
|
int r;
|
|
|
|
name = sd_bus_label_escape(link);
|
|
if (!name)
|
|
return cli_ENOMEM();
|
|
|
|
path = shl_strcat("/org/freedesktop/miracle/link/", name);
|
|
if (!path)
|
|
return cli_ENOMEM();
|
|
|
|
r = sd_bus_call_method(bus,
|
|
"org.freedesktop.miracle",
|
|
path,
|
|
"org.freedesktop.DBus.Properties",
|
|
"GetAll",
|
|
&err,
|
|
&m,
|
|
"s", "org.freedesktop.miracle.Link");
|
|
if (r < 0) {
|
|
cli_error("cannot retrieve link %s: %s",
|
|
link, bus_error_message(&err, r));
|
|
return r;
|
|
}
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m, 'e', "sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(t, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
free(fname);
|
|
fname = strdup(t);
|
|
if (!fname)
|
|
return cli_ENOMEM();
|
|
} else {
|
|
r = sd_bus_message_skip(m, "v");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Link: %s Name: %s\n",
|
|
link, fname ? : "<unknown>");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_object_fn(sd_bus *bus,
|
|
sd_bus_message *m,
|
|
void *data,
|
|
sd_bus_error *err)
|
|
{
|
|
_shl_cleanup_free_ char *peer = NULL, *link = NULL;
|
|
const char *obj, *t;
|
|
bool added;
|
|
int r;
|
|
|
|
added = !strcmp(sd_bus_message_get_member(m), "InterfacesAdded");
|
|
|
|
r = sd_bus_message_read(m, "o", &obj);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
t = shl_startswith(obj, "/org/freedesktop/miracle/peer/");
|
|
if (t) {
|
|
peer = sd_bus_label_unescape(t);
|
|
if (!peer)
|
|
return cli_ENOMEM();
|
|
|
|
if (added) {
|
|
r = filters_show_peer(peer);
|
|
if (r < 0)
|
|
return r;
|
|
} else {
|
|
cli_printf("[" CLI_YELLOW "REMOVE" CLI_DEFAULT "] Peer: %s\n",
|
|
peer);
|
|
}
|
|
}
|
|
|
|
t = shl_startswith(obj, "/org/freedesktop/miracle/link/");
|
|
if (t) {
|
|
link = sd_bus_label_unescape(t);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
|
|
if (added) {
|
|
r = filters_show_link(link);
|
|
if (r < 0)
|
|
return r;
|
|
} else {
|
|
if (scan_link && !strcmp(link, scan_link))
|
|
cmd_scan_stop(true);
|
|
|
|
cli_printf("[" CLI_YELLOW "REMOVE" CLI_DEFAULT "] Link: %s\n",
|
|
link);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_props_peer(const char *peer, const char *prop,
|
|
sd_bus_message *m)
|
|
{
|
|
int r, connected;
|
|
|
|
if (!strcmp(prop, "Connected")) {
|
|
r = bus_message_read_basic_variant(m, "b", &connected);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (connected)
|
|
cli_printf("[" CLI_GREEN "CONNECT" CLI_DEFAULT "] Peer: %s\n",
|
|
peer);
|
|
else
|
|
cli_printf("[" CLI_YELLOW "DISCONNECT" CLI_DEFAULT "] Peer: %s\n",
|
|
peer);
|
|
} else {
|
|
sd_bus_message_skip(m, "v");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_props_link(const char *link, const char *prop,
|
|
sd_bus_message *m)
|
|
{
|
|
const char *name;
|
|
int r;
|
|
|
|
if (!strcmp(prop, "Name")) {
|
|
r = bus_message_read_basic_variant(m, "s",
|
|
&name);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
} else {
|
|
sd_bus_message_skip(m, "v");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_props_fn(sd_bus *bus,
|
|
sd_bus_message *m,
|
|
void *data,
|
|
sd_bus_error *err)
|
|
{
|
|
_shl_cleanup_free_ char *peer = NULL, *link = NULL;
|
|
const char *path, *t;
|
|
int r;
|
|
|
|
if (!sd_bus_message_is_signal(m,
|
|
"org.freedesktop.DBus.Properties",
|
|
"PropertiesChanged"))
|
|
return 0;
|
|
|
|
path = sd_bus_message_get_path(m);
|
|
t = shl_startswith(path, "/org/freedesktop/miracle/peer/");
|
|
if (t) {
|
|
peer = sd_bus_label_unescape(t);
|
|
if (!peer)
|
|
return cli_ENOMEM();
|
|
}
|
|
|
|
t = shl_startswith(path, "/org/freedesktop/miracle/link/");
|
|
if (t) {
|
|
link = sd_bus_label_unescape(t);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
}
|
|
|
|
/* skip iface name */
|
|
r = sd_bus_message_skip(m, "s");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
while ((r = sd_bus_message_enter_container(m,
|
|
'e',
|
|
"sv")) > 0) {
|
|
r = sd_bus_message_read(m, "s", &t);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = 0;
|
|
if (link)
|
|
r = filters_props_link(link, t, m);
|
|
else if (peer)
|
|
r = filters_props_peer(peer, t, m);
|
|
else
|
|
sd_bus_message_skip(m, "v");
|
|
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
}
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
r = sd_bus_message_exit_container(m);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_peer_fn(sd_bus *bus,
|
|
sd_bus_message *m,
|
|
void *data,
|
|
sd_bus_error *err)
|
|
{
|
|
_shl_cleanup_free_ char *peer = NULL;
|
|
const char *path, *type, *pin;
|
|
int r;
|
|
|
|
path = sd_bus_message_get_path(m);
|
|
path = shl_startswith(path, "/org/freedesktop/miracle/peer/");
|
|
if (!path)
|
|
return 0;
|
|
|
|
peer = sd_bus_label_unescape(path);
|
|
if (!peer)
|
|
return cli_ENOMEM();
|
|
|
|
if (sd_bus_message_is_signal(m,
|
|
"org.freedesktop.miracle.Peer",
|
|
"ProvisionRequest")) {
|
|
r = sd_bus_message_read(m, "ss", &type, &pin);
|
|
if (r < 0)
|
|
return cli_log_parser(r);
|
|
|
|
if (!strcmp(type, "pbc"))
|
|
cli_printf("[" CLI_YELLOW "PROVISION" CLI_DEFAULT "] Peer %s: incoming PBC provision request\n",
|
|
peer);
|
|
else if (!strcmp(type, "display"))
|
|
cli_printf("[" CLI_YELLOW "PROVISION" CLI_DEFAULT "] Peer %s: incoming provision request with PIN: %s\n",
|
|
peer, pin);
|
|
else if (!strcmp(type, "pin"))
|
|
cli_printf("[" CLI_YELLOW "PROVISION" CLI_DEFAULT "] Peer %s: incoming PIN provision request\n",
|
|
peer, pin);
|
|
else
|
|
cli_printf("[" CLI_YELLOW "PROVISION" CLI_DEFAULT "] Peer %s: incoming provision request\n",
|
|
peer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int filters_link_fn(sd_bus *bus,
|
|
sd_bus_message *m,
|
|
void *data,
|
|
sd_bus_error *err)
|
|
{
|
|
_shl_cleanup_free_ char *link = NULL;
|
|
const char *path;
|
|
|
|
path = sd_bus_message_get_path(m);
|
|
path = shl_startswith(path, "/org/freedesktop/miracle/link/");
|
|
if (!path)
|
|
return 0;
|
|
|
|
link = sd_bus_label_unescape(path);
|
|
if (!link)
|
|
return cli_ENOMEM();
|
|
|
|
if (sd_bus_message_is_signal(m,
|
|
"org.freedesktop.miracle.Link",
|
|
"ScanStopped")) {
|
|
if (scan_link && !strcmp(link, scan_link))
|
|
cmd_scan_stop(true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void filters_init()
|
|
{
|
|
int r;
|
|
|
|
r = sd_bus_add_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.DBus.ObjectManager'",
|
|
filters_object_fn,
|
|
NULL);
|
|
if (r < 0)
|
|
cli_error("cannot add dbus match: %d", r);
|
|
|
|
r = sd_bus_add_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.DBus.Properties'",
|
|
filters_props_fn,
|
|
NULL);
|
|
if (r < 0)
|
|
cli_error("cannot add dbus match: %d", r);
|
|
|
|
r = sd_bus_add_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.miracle.Peer'",
|
|
filters_peer_fn,
|
|
NULL);
|
|
if (r < 0)
|
|
cli_error("cannot add dbus match: %d", r);
|
|
|
|
r = sd_bus_add_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.miracle.Link'",
|
|
filters_link_fn,
|
|
NULL);
|
|
if (r < 0)
|
|
cli_error("cannot add dbus match: %d", r);
|
|
}
|
|
|
|
static void filters_destroy()
|
|
{
|
|
sd_bus_remove_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.miracle.Link'",
|
|
filters_link_fn,
|
|
NULL);
|
|
|
|
sd_bus_remove_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.miracle.Peer'",
|
|
filters_peer_fn,
|
|
NULL);
|
|
|
|
sd_bus_remove_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.DBus.Properties'",
|
|
filters_props_fn,
|
|
NULL);
|
|
|
|
sd_bus_remove_match(bus,
|
|
"type='signal',"
|
|
"sender='org.freedesktop.miracle',"
|
|
"interface='org.freedesktop.DBus.ObjectManager'",
|
|
filters_object_fn,
|
|
NULL);
|
|
}
|
|
|
|
/*
|
|
* main
|
|
*/
|
|
|
|
static const struct cli_cmd cli_cmds[] = {
|
|
{ "list", NULL, CLI_M, LESS, 0, cmd_list, "List links and peers" },
|
|
{ "select", "[link]", CLI_Y, LESS, 1, cmd_select, "Select default link" },
|
|
{ "show-link", "[link]", CLI_M, LESS, 1, cmd_show_link, "Show link information" },
|
|
{ "show-peer", "<peer>", CLI_M, EQUAL, 1, cmd_show_peer, "Show peer information" },
|
|
{ "add-link", "<link>", CLI_M, EQUAL, 1, cmd_add_link, "Add link" },
|
|
{ "remove-link", "<link>", CLI_M, EQUAL, 1, cmd_remove_link, "Remove link" },
|
|
{ "scan", "[link|stop]", CLI_Y, LESS, 1, cmd_scan, "Start/Stop managed scan" },
|
|
{ "start-scan", "[link]", CLI_N, LESS, 1, cmd_start_scan, "Start neighborhood scan" },
|
|
{ "stop-scan", "[link]", CLI_M, LESS, 1, cmd_stop_scan, "Stop neighborhood scan" },
|
|
{ "set-link-name", "[link] <name>", CLI_M, MORE, 1, cmd_set_link_name, "Set friendly name of link" },
|
|
{ "allow", "<peer>", CLI_Y, EQUAL, 1, cmd_allow, "Allow incoming provision request" },
|
|
{ "reject", "<peer>", CLI_Y, EQUAL, 1, cmd_reject, "Reject incoming provision request" },
|
|
{ "connect", "<peer> [provision] [pin]", CLI_M, MORE, 1, cmd_connect, "Connect to peer" },
|
|
{ "disconnect", "<peer>", CLI_M, EQUAL, 1, cmd_disconnect, "Disconnect from peer" },
|
|
{ "quit", NULL, CLI_Y, MORE, 0, cmd_quit, "Quit program" },
|
|
{ "exit", NULL, CLI_Y, MORE, 0, cmd_quit, NULL },
|
|
{ "help", NULL, CLI_M, MORE, 0, NULL, "Print help" },
|
|
{ },
|
|
};
|
|
|
|
static int miraclectl_run(void)
|
|
{
|
|
int r;
|
|
|
|
filters_init();
|
|
|
|
r = cli_run();
|
|
|
|
cmd_scan_stop(true);
|
|
filters_destroy();
|
|
|
|
return r;
|
|
}
|
|
|
|
static int miraclectl_main(int argc, char *argv[])
|
|
{
|
|
int r, left;
|
|
|
|
left = argc - optind;
|
|
if (left <= 0) {
|
|
r = cli_init();
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = miraclectl_run();
|
|
cli_destroy();
|
|
} else {
|
|
r = cli_do(argv + optind, left);
|
|
if (r == -EAGAIN)
|
|
cli_error("unknown operation %s",
|
|
argv[optind]);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int parse_argv(int argc, char *argv[])
|
|
{
|
|
enum {
|
|
ARG_VERSION = 0x100,
|
|
ARG_LOG_LEVEL,
|
|
};
|
|
static const struct option options[] = {
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
|
|
{}
|
|
};
|
|
int c;
|
|
|
|
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
|
switch (c) {
|
|
case 'h':
|
|
return cli_help();
|
|
case ARG_VERSION:
|
|
puts(PACKAGE_STRING);
|
|
return 0;
|
|
case ARG_LOG_LEVEL:
|
|
cli_max_sev = atoi(optarg);
|
|
break;
|
|
case '?':
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int r;
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
r = parse_argv(argc, argv);
|
|
if (r < 0)
|
|
return EXIT_FAILURE;
|
|
if (!r)
|
|
return EXIT_SUCCESS;
|
|
|
|
r = sd_bus_default_system(&bus);
|
|
if (r < 0) {
|
|
cli_error("cannot connect to system bus: %s", strerror(-r));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
r = miraclectl_main(argc, argv);
|
|
sd_bus_unref(bus);
|
|
|
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|