mirror of
https://github.com/albfan/miraclecast.git
synced 2025-02-12 19:21:53 +00:00
Add new miracle-wifictl program
The miracle-wifictl program can be used to manage miracle-wifid. It provides low-level P2P helpers, so the main miraclectl can skip thos. Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
This commit is contained in:
parent
8ecc84d634
commit
6f9ab46448
6 changed files with 2366 additions and 0 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -22,6 +22,7 @@ configure
|
|||
libtool
|
||||
m4/
|
||||
miracle-dhcp
|
||||
miracle-wifictl
|
||||
miracle-wifid
|
||||
miraclectl
|
||||
miracled
|
||||
|
|
20
Makefile.am
20
Makefile.am
|
@ -107,6 +107,26 @@ miracle_wifid_LDADD = \
|
|||
$(DEPS_LIBS)
|
||||
miracle_wifid_LDFLAGS = $(AM_LDFLAGS)
|
||||
|
||||
#
|
||||
# miracle-wifictl
|
||||
#
|
||||
|
||||
bin_PROGRAMS += miracle-wifictl
|
||||
|
||||
miracle_wifictl_SOURCES = \
|
||||
src/ctl/ctl.h \
|
||||
src/ctl/ctl-cli.c \
|
||||
src/ctl/ctl-wifi.c \
|
||||
src/ctl/wifictl.c
|
||||
miracle_wifictl_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(DEPS_CFLAGS)
|
||||
miracle_wifictl_LDADD = \
|
||||
libmiracle-shared.la \
|
||||
-lreadline \
|
||||
$(DEPS_LIBS)
|
||||
miracle_wifictl_LDFLAGS = $(AM_LDFLAGS)
|
||||
|
||||
#
|
||||
# miraclectl
|
||||
#
|
||||
|
|
369
src/ctl/ctl-cli.c
Normal file
369
src/ctl/ctl-cli.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 "ctl.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>
|
||||
|
||||
/*
|
||||
* Helpers for interactive commands
|
||||
*/
|
||||
|
||||
static sd_event *cli_event;
|
||||
static sd_bus *cli_bus;
|
||||
static sd_event_source *cli_sigs[_NSIG];
|
||||
static sd_event_source *cli_stdin;
|
||||
static bool cli_rl;
|
||||
static const struct cli_cmd *cli_cmds;
|
||||
int cli_max_sev = LOG_NOTICE;
|
||||
|
||||
static bool is_cli(void)
|
||||
{
|
||||
return cli_rl;
|
||||
}
|
||||
|
||||
void cli_printv(const char *fmt, va_list args)
|
||||
{
|
||||
SHL_PROTECT_ERRNO;
|
||||
_shl_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();
|
||||
}
|
||||
}
|
||||
|
||||
void cli_printf(const char *fmt, ...)
|
||||
{
|
||||
SHL_PROTECT_ERRNO;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
cli_printv(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
int cli_help(const struct cli_cmd *cmds)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (is_cli()) {
|
||||
cli_printf("Available commands:\n");
|
||||
} else {
|
||||
cli_fn_help();
|
||||
}
|
||||
|
||||
for (i = 0; cmds[i].cmd; ++i) {
|
||||
if (!cmds[i].desc)
|
||||
continue;
|
||||
if (is_cli() && cmds[i].cli_cmp == CLI_N)
|
||||
continue;
|
||||
if (!is_cli() && cmds[i].cli_cmp == CLI_Y)
|
||||
continue;
|
||||
|
||||
cli_printf(" %s %-*s %s\n",
|
||||
cmds[i].cmd,
|
||||
(int)(40 - strlen(cmds[i].cmd)),
|
||||
cmds[i].args ? : "",
|
||||
cmds[i].desc ? : "");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
const char *cmd;
|
||||
int r;
|
||||
|
||||
if (!n)
|
||||
return -EAGAIN;
|
||||
|
||||
cmd = *args++;
|
||||
--n;
|
||||
|
||||
for (i = 0; cmds[i].cmd; ++i) {
|
||||
if (strcmp(cmd, cmds[i].cmd))
|
||||
continue;
|
||||
if (is_cli() && cmds[i].cli_cmp == CLI_N)
|
||||
continue;
|
||||
if (!is_cli() && cmds[i].cli_cmp == CLI_Y)
|
||||
continue;
|
||||
|
||||
switch (cmds[i].argc_cmp) {
|
||||
case CLI_EQUAL:
|
||||
if (n != cmds[i].argc) {
|
||||
cli_printf("Invalid number of arguments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
case CLI_MORE:
|
||||
if (n < cmds[i].argc) {
|
||||
cli_printf("too few arguments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
case CLI_LESS:
|
||||
if (n > cmds[i].argc) {
|
||||
cli_printf("too many arguments\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmds[i].fn) {
|
||||
r = cmds[i].fn(args, n);
|
||||
return (r == -EAGAIN) ? -EINVAL : r;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "help"))
|
||||
return cli_help(cmds);
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static void cli_handler_fn(char *input)
|
||||
{
|
||||
_shl_free_ char *original = input;
|
||||
_shl_strv_free_ 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(cli_cmds, 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cli_cmds = NULL;
|
||||
sd_bus_detach_event(cli_bus);
|
||||
cli_bus = NULL;
|
||||
sd_event_unref(cli_event);
|
||||
cli_event = NULL;
|
||||
}
|
||||
|
||||
int cli_init(sd_bus *bus, const struct cli_cmd *cmds)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
cli_cmds = cmds;
|
||||
cli_bus = bus;
|
||||
|
||||
r = sd_bus_attach_event(cli_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,
|
||||
&cli_sigs[i],
|
||||
sigs[i],
|
||||
cli_signal_fn,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
cli_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_event_add_io(cli_event,
|
||||
&cli_stdin,
|
||||
fileno(stdin),
|
||||
EPOLLHUP | EPOLLERR | EPOLLIN,
|
||||
cli_stdin_fn,
|
||||
NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
int cli_run(void)
|
||||
{
|
||||
if (!cli_event)
|
||||
return cli_EINVAL();
|
||||
|
||||
return sd_event_loop(cli_event);
|
||||
}
|
||||
|
||||
void cli_exit(void)
|
||||
{
|
||||
if (!cli_event)
|
||||
return cli_vEINVAL();
|
||||
|
||||
sd_event_exit(cli_event, 0);
|
||||
}
|
||||
|
||||
bool cli_running(void)
|
||||
{
|
||||
return is_cli();
|
||||
}
|
1204
src/ctl/ctl-wifi.c
Normal file
1204
src/ctl/ctl-wifi.c
Normal file
File diff suppressed because it is too large
Load diff
239
src/ctl/ctl.h
Normal file
239
src/ctl/ctl.h
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "shl_dlist.h"
|
||||
|
||||
#ifndef CTL_CTL_H
|
||||
#define CTL_CTL_H
|
||||
|
||||
struct ctl_wifi;
|
||||
struct ctl_link;
|
||||
struct ctl_peer;
|
||||
|
||||
/* wifi handling */
|
||||
|
||||
struct ctl_peer {
|
||||
struct shl_dlist list;
|
||||
char *label;
|
||||
struct ctl_link *l;
|
||||
|
||||
/* properties */
|
||||
char *p2p_mac;
|
||||
char *friendly_name;
|
||||
bool connected;
|
||||
char *interface;
|
||||
char *local_address;
|
||||
char *remote_address;
|
||||
};
|
||||
|
||||
#define peer_from_dlist(_p) shl_dlist_entry((_p), struct ctl_peer, list);
|
||||
|
||||
int ctl_peer_connect(struct ctl_peer *p, const char *prov, const char *pin);
|
||||
int ctl_peer_disconnect(struct ctl_peer *p);
|
||||
|
||||
struct ctl_link {
|
||||
struct shl_dlist list;
|
||||
char *label;
|
||||
struct ctl_wifi *w;
|
||||
|
||||
struct shl_dlist peers;
|
||||
|
||||
bool have_p2p_scan;
|
||||
|
||||
/* properties */
|
||||
unsigned int ifindex;
|
||||
char *ifname;
|
||||
char *friendly_name;
|
||||
bool p2p_scanning;
|
||||
};
|
||||
|
||||
#define link_from_dlist(_l) shl_dlist_entry((_l), struct ctl_link, list);
|
||||
|
||||
int ctl_link_set_friendly_name(struct ctl_link *l, const char *name);
|
||||
int ctl_link_set_p2p_scanning(struct ctl_link *l, bool val);
|
||||
|
||||
struct ctl_wifi {
|
||||
sd_bus *bus;
|
||||
|
||||
struct shl_dlist links;
|
||||
};
|
||||
|
||||
int ctl_wifi_new(struct ctl_wifi **out, sd_bus *bus);
|
||||
void ctl_wifi_free(struct ctl_wifi *w);
|
||||
int ctl_wifi_fetch(struct ctl_wifi *w);
|
||||
|
||||
struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,
|
||||
const char *label);
|
||||
struct ctl_link *ctl_wifi_search_link(struct ctl_wifi *w,
|
||||
const char *label);
|
||||
struct ctl_link *ctl_wifi_find_link_by_peer(struct ctl_wifi *w,
|
||||
const char *label);
|
||||
struct ctl_link *ctl_wifi_search_link_by_peer(struct ctl_wifi *w,
|
||||
const char *label);
|
||||
struct ctl_peer *ctl_wifi_find_peer(struct ctl_wifi *w,
|
||||
const char *label);
|
||||
struct ctl_peer *ctl_wifi_search_peer(struct ctl_wifi *w,
|
||||
const char *real_label);
|
||||
|
||||
/* CLI handling */
|
||||
|
||||
extern int cli_max_sev;
|
||||
void cli_printv(const char *fmt, va_list args);
|
||||
void cli_printf(const char *fmt, ...);
|
||||
|
||||
enum {
|
||||
#ifndef LOG_FATAL
|
||||
LOG_FATAL = 0,
|
||||
#endif
|
||||
#ifndef LOG_ALERT
|
||||
LOG_ALERT = 1,
|
||||
#endif
|
||||
#ifndef LOG_CRITICAL
|
||||
LOG_CRITICAL = 2,
|
||||
#endif
|
||||
#ifndef LOG_ERROR
|
||||
LOG_ERROR = 3,
|
||||
#endif
|
||||
#ifndef LOG_WARNING
|
||||
LOG_WARNING = 4,
|
||||
#endif
|
||||
#ifndef LOG_NOTICE
|
||||
LOG_NOTICE = 5,
|
||||
#endif
|
||||
#ifndef LOG_INFO
|
||||
LOG_INFO = 6,
|
||||
#endif
|
||||
#ifndef LOG_DEBUG
|
||||
LOG_DEBUG = 7,
|
||||
#endif
|
||||
LOG_SEV_NUM,
|
||||
};
|
||||
|
||||
#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))
|
||||
|
||||
#define cli_log_create(_r) \
|
||||
(cli_error("cannot create dbus message: %s", \
|
||||
strerror((_r) < 0 ? -(_r) : (_r))), (_r))
|
||||
|
||||
#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
|
||||
|
||||
struct cli_cmd {
|
||||
const char *cmd;
|
||||
const char *args;
|
||||
enum {
|
||||
CLI_N, /* no */
|
||||
CLI_M, /* maybe */
|
||||
CLI_Y, /* yes */
|
||||
} cli_cmp;
|
||||
enum {
|
||||
CLI_MORE,
|
||||
CLI_LESS,
|
||||
CLI_EQUAL,
|
||||
} argc_cmp;
|
||||
int argc;
|
||||
int (*fn) (char **args, unsigned int n);
|
||||
const char *desc;
|
||||
};
|
||||
|
||||
int cli_init(sd_bus *bus, const struct cli_cmd *cmds);
|
||||
void cli_destroy(void);
|
||||
int cli_run(void);
|
||||
void cli_exit(void);
|
||||
bool cli_running(void);
|
||||
|
||||
int cli_help(const struct cli_cmd *cmds);
|
||||
int cli_do(const struct cli_cmd *cmds, char **args, unsigned int n);
|
||||
|
||||
/* callback functions */
|
||||
|
||||
void ctl_fn_peer_new(struct ctl_peer *p);
|
||||
void ctl_fn_peer_free(struct ctl_peer *p);
|
||||
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin);
|
||||
void ctl_fn_peer_connected(struct ctl_peer *p);
|
||||
void ctl_fn_peer_disconnected(struct ctl_peer *p);
|
||||
void ctl_fn_link_new(struct ctl_link *l);
|
||||
void ctl_fn_link_free(struct ctl_link *l);
|
||||
|
||||
void cli_fn_help(void);
|
||||
|
||||
#endif /* CTL_CTL_H */
|
533
src/ctl/wifictl.c
Normal file
533
src/ctl/wifictl.c
Normal file
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 "ctl.h"
|
||||
#include "shl_macro.h"
|
||||
#include "shl_util.h"
|
||||
|
||||
static sd_bus *bus;
|
||||
static struct ctl_wifi *wifi;
|
||||
|
||||
static struct ctl_link *selected_link;
|
||||
|
||||
/*
|
||||
* cmd list
|
||||
*/
|
||||
|
||||
static int cmd_list(char **args, unsigned int n)
|
||||
{
|
||||
size_t link_cnt = 0, peer_cnt = 0;
|
||||
struct shl_dlist *i, *j;
|
||||
struct ctl_link *l;
|
||||
struct ctl_peer *p;
|
||||
|
||||
/* list links */
|
||||
|
||||
cli_printf("%6s %-24s %-30s\n",
|
||||
"LINK", "INTERFACE", "FRIENDLY-NAME");
|
||||
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
++link_cnt;
|
||||
|
||||
cli_printf("%6s %-24s %-30s\n",
|
||||
l->label,
|
||||
shl_isempty(l->ifname) ?
|
||||
"<unknown>" : l->ifname,
|
||||
shl_isempty(l->friendly_name) ?
|
||||
"<unknown>" : l->friendly_name);
|
||||
}
|
||||
|
||||
cli_printf("\n");
|
||||
|
||||
/* list peers */
|
||||
|
||||
cli_printf("%6s %-24s %-30s %-10s\n",
|
||||
"LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED");
|
||||
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
|
||||
shl_dlist_for_each(j, &l->peers) {
|
||||
p = peer_from_dlist(j);
|
||||
++peer_cnt;
|
||||
|
||||
cli_printf("%6s %-24s %-30s %-10s\n",
|
||||
p->l->label,
|
||||
p->label,
|
||||
shl_isempty(p->friendly_name) ?
|
||||
"<unknown>" : p->friendly_name,
|
||||
p->connected ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
cli_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: select
|
||||
*/
|
||||
|
||||
static int cmd_select(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l;
|
||||
|
||||
if (!n) {
|
||||
if (selected_link) {
|
||||
cli_printf("link %s deselected\n",
|
||||
selected_link->label);
|
||||
selected_link = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = ctl_wifi_search_link(wifi, args[0]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
selected_link = l;
|
||||
cli_printf("link %s selected\n", selected_link->label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: show
|
||||
*/
|
||||
|
||||
static int cmd_show(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
struct ctl_peer *p = NULL;
|
||||
|
||||
if (n > 0) {
|
||||
if (!(l = ctl_wifi_find_link(wifi, args[0])) &&
|
||||
!(p = ctl_wifi_find_peer(wifi, args[0])) &&
|
||||
!(l = ctl_wifi_search_link(wifi, args[0])) &&
|
||||
!(p = ctl_wifi_search_peer(wifi, args[0]))) {
|
||||
cli_error("unknown link or peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
l = selected_link;
|
||||
}
|
||||
|
||||
if (l) {
|
||||
cli_printf("Link=%s\n", l->label);
|
||||
if (l->ifindex > 0)
|
||||
cli_printf("InterfaceIndex=%u\n", l->ifindex);
|
||||
if (l->ifname && *l->ifname)
|
||||
cli_printf("InterfaceName=%s\n", l->ifname);
|
||||
if (l->friendly_name && *l->friendly_name)
|
||||
cli_printf("FriendlyName=%s\n", l->friendly_name);
|
||||
cli_printf("P2PScanning=%d\n", l->p2p_scanning);
|
||||
} else if (p) {
|
||||
cli_printf("Peer=%s\n", p->label);
|
||||
if (p->p2p_mac && *p->p2p_mac)
|
||||
cli_printf("P2PMac=%s\n", p->p2p_mac);
|
||||
if (p->friendly_name && *p->friendly_name)
|
||||
cli_printf("FriendlyName=%s\n", p->friendly_name);
|
||||
cli_printf("Connected=%d\n", p->connected);
|
||||
if (p->interface && *p->interface)
|
||||
cli_printf("Interface=%s\n", p->interface);
|
||||
if (p->local_address && *p->local_address)
|
||||
cli_printf("LocalAddress=%s\n", p->local_address);
|
||||
if (p->remote_address && *p->remote_address)
|
||||
cli_printf("RemoteAddress=%s\n", p->remote_address);
|
||||
} else {
|
||||
cli_printf("Show what?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: set-friendly-name
|
||||
*/
|
||||
|
||||
static int cmd_set_friendly_name(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
const char *name;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("To what?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
l = ctl_wifi_search_link(wifi, args[0]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = args[1];
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
|
||||
l = l ? : selected_link;
|
||||
if (!l) {
|
||||
cli_error("no link selected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctl_link_set_friendly_name(l, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: p2p-scan
|
||||
*/
|
||||
|
||||
static int cmd_p2p_scan(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
unsigned int i;
|
||||
bool stop = false;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (!strcmp(args[i], "stop")) {
|
||||
stop = true;
|
||||
} else {
|
||||
l = ctl_wifi_search_link(wifi, args[i]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l = l ? : selected_link;
|
||||
if (!l) {
|
||||
cli_error("no link selected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctl_link_set_p2p_scanning(l, !stop);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: connect
|
||||
*/
|
||||
|
||||
static bool is_valid_prov(const char *prov)
|
||||
{
|
||||
return prov && (!strcmp(prov, "auto") ||
|
||||
!strcmp(prov, "pbc") ||
|
||||
!strcmp(prov, "display") ||
|
||||
!strcmp(prov, "pin"));
|
||||
}
|
||||
|
||||
static int cmd_connect(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_peer *p;
|
||||
const char *prov, *pin;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("To whom?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = ctl_wifi_search_peer(wifi, args[0]);
|
||||
if (!p) {
|
||||
cli_error("unknown peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > 2) {
|
||||
prov = args[1];
|
||||
pin = args[2];
|
||||
} else if (n > 1) {
|
||||
if (is_valid_prov(args[1])) {
|
||||
prov = args[1];
|
||||
pin = "";
|
||||
} else {
|
||||
prov = "auto";
|
||||
pin = args[1];
|
||||
}
|
||||
} else {
|
||||
prov = "auto";
|
||||
pin = "";
|
||||
}
|
||||
|
||||
return ctl_peer_connect(p, prov, pin);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: disconnect
|
||||
*/
|
||||
|
||||
static int cmd_disconnect(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_peer *p;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("From whom?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = ctl_wifi_search_peer(wifi, args[0]);
|
||||
if (!p) {
|
||||
cli_error("unknown peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctl_peer_disconnect(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: quit/exit
|
||||
*/
|
||||
|
||||
static int cmd_quit(char **args, unsigned int n)
|
||||
{
|
||||
cli_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* main
|
||||
*/
|
||||
|
||||
static const struct cli_cmd cli_cmds[] = {
|
||||
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects" },
|
||||
{ "select", "[link]", CLI_Y, CLI_LESS, 1, cmd_select, "Select default link" },
|
||||
{ "show", "[link|peer]", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information" },
|
||||
{ "set-friendly-name", "[link] <name>", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object" },
|
||||
{ "p2p-scan", "[link] [stop]", CLI_Y, CLI_LESS, 2, cmd_p2p_scan, "Control neighborhood P2P scanning" },
|
||||
{ "connect", "<peer> [provision] [pin]", CLI_M, CLI_LESS, 3, cmd_connect, "Connect to peer" },
|
||||
{ "disconnect", "<peer>", CLI_M, CLI_EQUAL, 1, cmd_disconnect, "Disconnect from peer" },
|
||||
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program" },
|
||||
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL },
|
||||
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" },
|
||||
{ },
|
||||
};
|
||||
|
||||
void ctl_fn_peer_new(struct ctl_peer *p)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_free(struct ctl_peer *p)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "PROV" CLI_DEFAULT "] Peer: %s Type: %s PIN: %s\n",
|
||||
p->label, prov, pin);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_connected(struct ctl_peer *p)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "CONNECT" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_disconnected(struct ctl_peer *p)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "DISCONNECT" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_link_new(struct ctl_link *l)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Link: %s\n",
|
||||
l->label);
|
||||
}
|
||||
|
||||
void ctl_fn_link_free(struct ctl_link *l)
|
||||
{
|
||||
if (l == selected_link) {
|
||||
cli_printf("link %s deselected\n",
|
||||
selected_link->label);
|
||||
selected_link = NULL;
|
||||
}
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Link: %s\n",
|
||||
l->label);
|
||||
}
|
||||
|
||||
void cli_fn_help()
|
||||
{
|
||||
/*
|
||||
* 80-char barrier:
|
||||
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
*/
|
||||
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||
"Send control command to or query the MiracleCast Wifi-Manager. If no arguments\n"
|
||||
"are 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
|
||||
*/
|
||||
}
|
||||
|
||||
static int ctl_interactive(void)
|
||||
{
|
||||
struct shl_dlist *i;
|
||||
struct ctl_link *l;
|
||||
int r;
|
||||
|
||||
r = cli_init(bus, cli_cmds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ctl_wifi_fetch(wifi);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = cli_run();
|
||||
|
||||
/* stop managed scans, but only in interactive mode */
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
if (l->have_p2p_scan)
|
||||
ctl_link_set_p2p_scanning(l, false);
|
||||
}
|
||||
|
||||
error:
|
||||
cli_destroy();
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ctl_single(char **argv, int argc)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = ctl_wifi_fetch(wifi);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = cli_do(cli_cmds, argv, argc);
|
||||
if (r == -EAGAIN)
|
||||
cli_error("unknown operation %s", argv[0]);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ctl_main(int argc, char *argv[])
|
||||
{
|
||||
int r, left;
|
||||
|
||||
r = ctl_wifi_new(&wifi, bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
left = argc - optind;
|
||||
if (left <= 0)
|
||||
r = ctl_interactive();
|
||||
else
|
||||
r = ctl_single(argv + optind, left);
|
||||
|
||||
ctl_wifi_free(wifi);
|
||||
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(cli_cmds);
|
||||
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 = ctl_main(argc, argv);
|
||||
sd_bus_unref(bus);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in a new issue