blob: 3a3c072c01698e25855c075240b28e398a0e5702 [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2012-2014 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>
#include <glib.h>
#include <gdbus.h>
#include "dbus_helpers.h"
#include "input.h"
#include "services.h"
#include "peers.h"
#include "commands.h"
#include "agent.h"
#include "vpnconnections.h"
static DBusConnection *connection;
static GHashTable *service_hash;
static GHashTable *peer_hash;
static GHashTable *technology_hash;
static char *session_notify_path;
static char *session_path;
static bool session_connected;
struct connman_option {
const char *name;
const char val;
const char *desc;
};
static char *ipv4[] = {
"Method",
"Address",
"Netmask",
"Gateway",
NULL
};
static char *ipv6[] = {
"Method",
"Address",
"PrefixLength",
"Gateway",
NULL
};
static int cmd_help(char *args[], int num, struct connman_option *options);
static bool check_dbus_name(const char *name)
{
/*
* Valid dbus chars should be [A-Z][a-z][0-9]_
* and should not start with number.
*/
unsigned int i;
if (!name || name[0] == '\0')
return false;
for (i = 0; name[i] != '\0'; i++)
if (!((name[i] >= 'A' && name[i] <= 'Z') ||
(name[i] >= 'a' && name[i] <= 'z') ||
(name[i] >= '0' && name[i] <= '9') ||
name[i] == '_'))
return false;
return true;
}
static int parse_boolean(char *arg)
{
if (!arg)
return -1;
if (strcasecmp(arg, "no") == 0 ||
strcasecmp(arg, "false") == 0 ||
strcasecmp(arg, "off" ) == 0 ||
strcasecmp(arg, "disable" ) == 0 ||
strcasecmp(arg, "n") == 0 ||
strcasecmp(arg, "f") == 0 ||
strcasecmp(arg, "0") == 0)
return 0;
if (strcasecmp(arg, "yes") == 0 ||
strcasecmp(arg, "true") == 0 ||
strcasecmp(arg, "on") == 0 ||
strcasecmp(arg, "enable" ) == 0 ||
strcasecmp(arg, "y") == 0 ||
strcasecmp(arg, "t") == 0 ||
strcasecmp(arg, "1") == 0)
return 1;
return -1;
}
static int parse_args(char *arg, struct connman_option *options)
{
int i;
if (!arg)
return -1;
for (i = 0; options[i].name; i++) {
if (strcmp(options[i].name, arg) == 0 ||
(strncmp(arg, "--", 2) == 0 &&
strcmp(&arg[2], options[i].name) == 0))
return options[i].val;
}
return '?';
}
static int enable_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *tech = user_data;
char *str;
str = strrchr(tech, '/');
if (str)
str++;
else
str = tech;
if (!error)
fprintf(stdout, "Enabled %s\n", str);
else
fprintf(stderr, "Error %s: %s\n", str, error);
g_free(user_data);
return 0;
}
static int cmd_enable(char *args[], int num, struct connman_option *options)
{
char *tech;
dbus_bool_t b = TRUE;
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
if (strcmp(args[1], "offline") == 0) {
tech = g_strdup(args[1]);
return __connmanctl_dbus_set_property(connection, "/",
"net.connman.Manager", enable_return, tech,
"OfflineMode", DBUS_TYPE_BOOLEAN, &b);
}
tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
return __connmanctl_dbus_set_property(connection, tech,
"net.connman.Technology", enable_return, tech,
"Powered", DBUS_TYPE_BOOLEAN, &b);
}
static int disable_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *tech = user_data;
char *str;
str = strrchr(tech, '/');
if (str)
str++;
else
str = tech;
if (!error)
fprintf(stdout, "Disabled %s\n", str);
else
fprintf(stderr, "Error %s: %s\n", str, error);
g_free(user_data);
return 0;
}
static int cmd_disable(char *args[], int num, struct connman_option *options)
{
char *tech;
dbus_bool_t b = FALSE;
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
if (strcmp(args[1], "offline") == 0) {
tech = g_strdup(args[1]);
return __connmanctl_dbus_set_property(connection, "/",
"net.connman.Manager", disable_return, tech,
"OfflineMode", DBUS_TYPE_BOOLEAN, &b);
}
tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
return __connmanctl_dbus_set_property(connection, tech,
"net.connman.Technology", disable_return, tech,
"Powered", DBUS_TYPE_BOOLEAN, &b);
}
static int prefer_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
fprintf(stderr, "Error: %s\n", error);
}
return 0;
}
static int cmd_prefer(char *args[], int num, struct connman_option *options)
{
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
fprintf(stderr, "cmd_prefer %s\n", args[1]);
return __connmanctl_dbus_set_property(connection, "/",
"net.connman.Manager", prefer_cb, NULL,
"Preferred", DBUS_TYPE_STRING, &args[1]);
}
static int state_print(DBusMessageIter *iter, const char *error,
void *user_data)
{
DBusMessageIter entry;
if (error) {
fprintf(stderr, "Error: %s", error);
return 0;
}
dbus_message_iter_recurse(iter, &entry);
__connmanctl_dbus_print(&entry, " ", " = ", "\n");
fprintf(stdout, "\n");
return 0;
}
static int cmd_state(char *args[], int num, struct connman_option *options)
{
if (num > 1)
return -E2BIG;
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
CONNMAN_PATH, "net.connman.Manager", "GetProperties",
state_print, NULL, NULL, NULL);
}
static int services_list(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (!error) {
__connmanctl_services_list(iter);
fprintf(stdout, "\n");
} else {
fprintf(stderr, "Error: %s\n", error);
}
return 0;
}
static int peers_list(DBusMessageIter *iter,
const char *error, void *user_data)
{
if (!error) {
__connmanctl_peers_list(iter);
fprintf(stdout, "\n");
} else
fprintf(stderr, "Error: %s\n", error);
return 0;
}
static int object_properties(DBusMessageIter *iter,
const char *error, void *user_data)
{
char *path = user_data;
char *str;
DBusMessageIter dict;
if (!error) {
fprintf(stdout, "%s\n", path);
dbus_message_iter_recurse(iter, &dict);
__connmanctl_dbus_print(&dict, " ", " = ", "\n");
fprintf(stdout, "\n");
} else {
str = strrchr(path, '/');
if (str)
str++;
else
str = path;
fprintf(stderr, "Error %s: %s\n", str, error);
}
g_free(user_data);
return 0;
}
static int cmd_services(char *args[], int num, struct connman_option *options)
{
char *service_name = NULL;
char *path;
int c;
if (num > 3)
return -E2BIG;
c = parse_args(args[1], options);
switch (c) {
case -1:
break;
case 'p':
if (num < 3)
return -EINVAL;
service_name = args[2];
break;
default:
if (num > 2)
return -E2BIG;
service_name = args[1];
break;
}
if (!service_name) {
return __connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, CONNMAN_PATH,
"net.connman.Manager", "GetServices",
services_list, NULL, NULL, NULL);
}
if (check_dbus_name(service_name) == false)
return -EINVAL;
path = g_strdup_printf("/net/connman/service/%s", service_name);
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
"net.connman.Service", "GetProperties",
object_properties, path, NULL, NULL);
}
static int cmd_peers(char *args[], int num, struct connman_option *options)
{
char *peer_name = NULL;
char *path;
if (num > 2)
return -E2BIG;
if (num == 2)
peer_name = args[1];
if (!peer_name) {
return __connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, CONNMAN_PATH,
"net.connman.Manager", "GetPeers",
peers_list, NULL, NULL, NULL);
}
if (check_dbus_name(peer_name) == false)
return -EINVAL;
path = g_strdup_printf("/net/connman/peer/%s", peer_name);
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
path, "net.connman.Peer", "GetProperties",
object_properties, path, NULL, NULL);
}
static int technology_print(DBusMessageIter *iter, const char *error,
void *user_data)
{
DBusMessageIter array;
if (error) {
fprintf(stderr, "Error: %s\n", error);
return 0;
}
dbus_message_iter_recurse(iter, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
DBusMessageIter entry, dict;
const char *path;
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &path);
fprintf(stdout, "%s\n", path);
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &dict);
__connmanctl_dbus_print(&dict, " ", " = ", "\n");
fprintf(stdout, "\n");
dbus_message_iter_next(&array);
}
return 0;
}
static int cmd_technologies(char *args[], int num,
struct connman_option *options)
{
if (num > 1)
return -E2BIG;
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
CONNMAN_PATH, "net.connman.Manager", "GetTechnologies",
technology_print, NULL, NULL, NULL);
}
struct tether_enable {
char *path;
dbus_bool_t enable;
};
static int tether_set_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
struct tether_enable *tether = user_data;
char *str;
str = strrchr(tether->path, '/');
if (str)
str++;
else
str = tether->path;
if (!error) {
fprintf(stdout, "%s tethering for %s\n",
tether->enable ? "Enabled" : "Disabled",
str);
} else
fprintf(stderr, "Error %s %s tethering: %s\n",
tether->enable ?
"enabling" : "disabling", str, error);
g_free(tether->path);
g_free(user_data);
return 0;
}
static int tether_set(char *technology, int set_tethering)
{
struct tether_enable *tether = g_new(struct tether_enable, 1);
switch(set_tethering) {
case 1:
tether->enable = TRUE;
break;
case 0:
tether->enable = FALSE;
break;
default:
g_free(tether);
return 0;
}
tether->path = g_strdup_printf("/net/connman/technology/%s",
technology);
return __connmanctl_dbus_set_property(connection, tether->path,
"net.connman.Technology", tether_set_return,
tether, "Tethering", DBUS_TYPE_BOOLEAN,
&tether->enable);
}
struct tether_properties {
int ssid_result;
int passphrase_result;
int set_tethering;
};
static int tether_update(struct tether_properties *tether)
{
if (tether->ssid_result == 0 && tether->passphrase_result == 0)
return tether_set("wifi", tether->set_tethering);
if (tether->ssid_result != -EINPROGRESS &&
tether->passphrase_result != -EINPROGRESS) {
g_free(tether);
return 0;
}
return -EINPROGRESS;
}
static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
struct tether_properties *tether = user_data;
if (!error) {
fprintf(stdout, "Wifi SSID set\n");
tether->ssid_result = 0;
} else {
fprintf(stderr, "Error setting wifi SSID: %s\n", error);
tether->ssid_result = -EINVAL;
}
return tether_update(tether);
}
static int tether_set_passphrase_return(DBusMessageIter *iter,
const char *error, void *user_data)
{
struct tether_properties *tether = user_data;
if (!error) {
fprintf(stdout, "Wifi passphrase set\n");
tether->passphrase_result = 0;
} else {
fprintf(stderr, "Error setting wifi passphrase: %s\n", error);
tether->passphrase_result = -EINVAL;
}
return tether_update(tether);
}
static int tether_set_ssid(char *ssid, char *passphrase, int set_tethering)
{
struct tether_properties *tether = g_new(struct tether_properties, 1);
tether->set_tethering = set_tethering;
tether->ssid_result = __connmanctl_dbus_set_property(connection,
"/net/connman/technology/wifi",
"net.connman.Technology",
tether_set_ssid_return, tether,
"TetheringIdentifier", DBUS_TYPE_STRING, &ssid);
tether->passphrase_result =__connmanctl_dbus_set_property(connection,
"/net/connman/technology/wifi",
"net.connman.Technology",
tether_set_passphrase_return, tether,
"TetheringPassphrase", DBUS_TYPE_STRING, &passphrase);
if (tether->ssid_result != -EINPROGRESS &&
tether->passphrase_result != -EINPROGRESS) {
g_free(tether);
return -ENXIO;
}
return -EINPROGRESS;
}
static int cmd_tether(char *args[], int num, struct connman_option *options)
{
char *ssid, *passphrase;
int set_tethering;
if (num < 3)
return -EINVAL;
passphrase = args[num - 1];
ssid = args[num - 2];
set_tethering = parse_boolean(args[2]);
if (strcmp(args[1], "wifi") == 0) {
if (num > 5)
return -E2BIG;
if (num == 5 && set_tethering == -1)
return -EINVAL;
if (num == 4)
set_tethering = -1;
if (num > 3)
return tether_set_ssid(ssid, passphrase, set_tethering);
}
if (num > 3)
return -E2BIG;
if (set_tethering == -1)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
return tether_set(args[1], set_tethering);
}
static int scan_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *path = user_data;
if (!error) {
char *str = strrchr(path, '/');
str++;
fprintf(stdout, "Scan completed for %s\n", str);
} else
fprintf(stderr, "Error %s: %s\n", path, error);
g_free(user_data);
return 0;
}
static int cmd_scan(char *args[], int num, struct connman_option *options)
{
char *path;
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
path = g_strdup_printf("/net/connman/technology/%s", args[1]);
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
"net.connman.Technology", "Scan",
scan_return, path, NULL, NULL);
}
static int connect_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *path = user_data;
if (!error) {
char *str = strrchr(path, '/');
str++;
fprintf(stdout, "Connected %s\n", str);
} else
fprintf(stderr, "Error %s: %s\n", path, error);
g_free(user_data);
return 0;
}
static int cmd_connect(char *args[], int num, struct connman_option *options)
{
const char *iface = "net.connman.Service";
char *path;
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
iface = "net.connman.Peer";
path = g_strdup_printf("/net/connman/peer/%s", args[1]);
} else
path = g_strdup_printf("/net/connman/service/%s", args[1]);
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
iface, "Connect", connect_return, path, NULL, NULL);
}
static int check_count=0;
static int check_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *path = user_data;
char *str = strrchr(path, '/');
str++;
fprintf(stdout, "%s %s\n", str, error ? "offline" : "online");
g_free(user_data);
check_count--;
return check_count ? -EINPROGRESS : 0;
}
static void check_append(DBusMessageIter *iter, void *user_data)
{
const char *check_server = user_data;
fprintf(stderr, "check_append %s\n", check_server);
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &check_server);
}
static int cmd_check(char *args[], int num, struct connman_option *options)
{
const char *iface = "net.connman.Service";
char *path;
int i, ret;
if (check_count) {
fprintf(stdout, "previous checking not finished yet, check_count=%d\n", check_count);
return -EINVAL;
}
if (num < 3)
return -EINVAL;
if (strcmp(args[1], "google") && strcmp(args[1], "connman")) {
fprintf(stdout, "online check server %s is not either googe or connman\n", args[1]);
return -EINVAL;
}
for (i = 2; i < num; i++) {
if (check_dbus_name(args[i]) == false)
return -EINVAL;
}
for (i = 2; i < num; i++) {
if (g_strstr_len(args[i], 5, "peer_") == args[i]) {
iface = "net.connman.Peer";
path = g_strdup_printf("/net/connman/peer/%s", args[i]);
} else
path = g_strdup_printf("/net/connman/service/%s", args[i]);
ret = __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
iface, "OnlineCheck", check_return, path, check_append, args[1]);
if (ret == -EINPROGRESS)
check_count++;
}
return ret;
}
static int disconnect_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *path = user_data;
if (!error) {
char *str = strrchr(path, '/');
str++;
fprintf(stdout, "Disconnected %s\n", str);
} else
fprintf(stderr, "Error %s: %s\n", path, error);
g_free(user_data);
return 0;
}
static int cmd_disconnect(char *args[], int num, struct connman_option *options)
{
const char *iface = "net.connman.Service";
char *path;
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
if (check_dbus_name(args[1]) == false)
return -EINVAL;
if (g_strstr_len(args[1], 5, "peer_") == args[1]) {
iface = "net.connman.Peer";
path = g_strdup_printf("/net/connman/peer/%s", args[1]);
} else
path = g_strdup_printf("/net/connman/service/%s", args[1]);
return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
path, iface, "Disconnect",
disconnect_return, path, NULL, NULL);
}
static int config_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *service_name = user_data;
if (error)
fprintf(stderr, "Error %s: %s\n", service_name, error);
g_free(user_data);
return 0;
}
struct config_append {
char **opts;
int values;
};
static void config_append_ipv4(DBusMessageIter *iter,
void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
int i = 0;
if (!opts)
return;
while (opts[i] && ipv4[i]) {
__connmanctl_dbus_append_dict_entry(iter, ipv4[i],
DBUS_TYPE_STRING, &opts[i]);
i++;
}
append->values = i;
}
static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
if (!opts)
return;
append->values = 1;
if (g_strcmp0(opts[0], "auto") == 0) {
char *str;
switch (parse_boolean(opts[1])) {
case 0:
append->values = 2;
str = "disabled";
__connmanctl_dbus_append_dict_entry(iter, "Privacy",
DBUS_TYPE_STRING, &str);
break;
case 1:
append->values = 2;
str = "enabled";
__connmanctl_dbus_append_dict_entry(iter, "Privacy",
DBUS_TYPE_STRING, &str);
break;
default:
if (opts[1]) {
append->values = 2;
if (g_strcmp0(opts[1], "prefered") != 0 &&
g_strcmp0(opts[1],
"preferred") != 0) {
fprintf(stderr, "Error %s: %s\n",
opts[1],
strerror(EINVAL));
return;
}
str = "prefered";
__connmanctl_dbus_append_dict_entry(iter,
"Privacy", DBUS_TYPE_STRING,
&str);
}
break;
}
} else if (g_strcmp0(opts[0], "manual") == 0) {
int i = 1;
while (opts[i] && ipv6[i]) {
if (i == 2) {
int value = atoi(opts[i]);
__connmanctl_dbus_append_dict_entry(iter,
ipv6[i], DBUS_TYPE_BYTE,
&value);
} else {
__connmanctl_dbus_append_dict_entry(iter,
ipv6[i], DBUS_TYPE_STRING,
&opts[i]);
}
i++;
}
append->values = i;
} else if (g_strcmp0(opts[0], "off") != 0) {
fprintf(stderr, "Error %s: %s\n", opts[0], strerror(EINVAL));
return;
}
__connmanctl_dbus_append_dict_entry(iter, "Method", DBUS_TYPE_STRING,
&opts[0]);
}
static void config_append_str(DBusMessageIter *iter, void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
int i = 0;
if (!opts)
return;
while (opts[i]) {
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
&opts[i]);
i++;
}
append->values = i;
}
static void append_servers(DBusMessageIter *iter, void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
int i = 1;
if (!opts)
return;
while (opts[i] && g_strcmp0(opts[i], "--excludes") != 0) {
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
&opts[i]);
i++;
}
append->values = i;
}
static void append_excludes(DBusMessageIter *iter, void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
int i = append->values;
if (!opts || !opts[i] ||
g_strcmp0(opts[i], "--excludes") != 0)
return;
i++;
while (opts[i]) {
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
&opts[i]);
i++;
}
append->values = i;
}
static void config_append_proxy(DBusMessageIter *iter, void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
if (!opts)
return;
if (g_strcmp0(opts[0], "manual") == 0) {
__connmanctl_dbus_append_dict_string_array(iter, "Servers",
append_servers, append);
__connmanctl_dbus_append_dict_string_array(iter, "Excludes",
append_excludes, append);
} else if (g_strcmp0(opts[0], "auto") == 0) {
if (opts[1]) {
__connmanctl_dbus_append_dict_entry(iter, "URL",
DBUS_TYPE_STRING, &opts[1]);
append->values++;
}
} else if (g_strcmp0(opts[0], "direct") != 0)
return;
__connmanctl_dbus_append_dict_entry(iter, "Method",DBUS_TYPE_STRING,
&opts[0]);
append->values++;
}
static int cmd_config(char *args[], int num, struct connman_option *options)
{
int result = 0, res = 0, index = 2, oldindex = 0;
int c;
char *service_name, *path;
char **opt_start;
dbus_bool_t val;
dbus_int32_t order;
struct config_append append;
service_name = args[1];
if (!service_name)
return -EINVAL;
if (check_dbus_name(service_name) == false)
return -EINVAL;
while (index < num && args[index]) {
c = parse_args(args[index], options);
opt_start = &args[index + 1];
append.opts = opt_start;
append.values = 0;
res = 0;
oldindex = index;
path = g_strdup_printf("/net/connman/service/%s", service_name);
switch (c) {
case 'a':
switch (parse_boolean(*opt_start)) {
case 1:
val = TRUE;
break;
case 0:
val = FALSE;
break;
default:
res = -EINVAL;
break;
}
index++;
if (res == 0) {
res = __connmanctl_dbus_set_property(connection,
path, "net.connman.Service",
config_return,
g_strdup(service_name),
"AutoConnect",
DBUS_TYPE_BOOLEAN, &val);
}
break;
case 'i':
res = __connmanctl_dbus_set_property_dict(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"IPv4.Configuration", DBUS_TYPE_STRING,
config_append_ipv4, &append);
index += append.values;
break;
case 'v':
res = __connmanctl_dbus_set_property_dict(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"IPv6.Configuration", DBUS_TYPE_STRING,
config_append_ipv6, &append);
index += append.values;
break;
case 'n':
res = __connmanctl_dbus_set_property_array(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"Nameservers.Configuration",
DBUS_TYPE_STRING, config_append_str,
&append);
index += append.values;
break;
case 't':
res = __connmanctl_dbus_set_property_array(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"Timeservers.Configuration",
DBUS_TYPE_STRING, config_append_str,
&append);
index += append.values;
break;
case 'd':
res = __connmanctl_dbus_set_property_array(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"Domains.Configuration",
DBUS_TYPE_STRING, config_append_str,
&append);
index += append.values;
break;
case 'x':
res = __connmanctl_dbus_set_property_dict(connection,
path, "net.connman.Service",
config_return, g_strdup(service_name),
"Proxy.Configuration",
DBUS_TYPE_STRING, config_append_proxy,
&append);
index += append.values;
break;
case 'r':
res = __connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, path,
"net.connman.Service", "Remove",
config_return, g_strdup(service_name),
NULL, NULL);
break;
case 'o':
order = atoi(*opt_start);
res = __connmanctl_dbus_set_property(connection,
path, "net.connman.Service",
config_return,
g_strdup(service_name),
"Order",
DBUS_TYPE_INT32, &order);
index++;
break;
default:
res = -EINVAL;
break;
}
g_free(path);
if (res < 0) {
if (res == -EINPROGRESS)
result = -EINPROGRESS;
else
printf("Error '%s': %s\n", args[oldindex],
strerror(-res));
} else
index += res;
index++;
}
return result;
}
static DBusHandlerResult monitor_changed(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
DBusMessageIter iter;
const char *interface, *path;
interface = dbus_message_get_interface(message);
if (!interface)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (strncmp(interface, "net.connman.", 12) != 0)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
if (!strcmp(interface, "net.connman.Agent") ||
!strcmp(interface, "net.connman.vpn.Agent") ||
!strcmp(interface, "net.connman.Session") ||
!strcmp(interface, "net.connman.Notification"))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
interface = strrchr(interface, '.');
if (interface && *interface != '\0')
interface++;
path = strrchr(dbus_message_get_path(message), '/');
if (path && *path != '\0')
path++;
__connmanctl_save_rl();
if (dbus_message_is_signal(message, "net.connman.Manager",
"ServicesChanged")) {
fprintf(stdout, "%-12s %-20s = {\n", interface,
"ServicesChanged");
dbus_message_iter_init(message, &iter);
__connmanctl_services_list(&iter);
fprintf(stdout, "\n}\n");
__connmanctl_redraw_rl();
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} else if (dbus_message_is_signal(message, "net.connman.Manager",
"PeersChanged")) {
fprintf(stdout, "%-12s %-20s = {\n", interface,
"PeersChanged");
dbus_message_iter_init(message, &iter);
__connmanctl_peers_list(&iter);
fprintf(stdout, "\n}\n");
__connmanctl_redraw_rl();
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} else if (dbus_message_is_signal(message, "net.connman.vpn.Manager",
"ConnectionAdded") ||
dbus_message_is_signal(message,
"net.connman.vpn.Manager",
"ConnectionRemoved")) {
interface = "vpn.Manager";
path = dbus_message_get_member(message);
} else if (dbus_message_is_signal(message, "net.connman.Manager",
"TechnologyAdded") ||
dbus_message_is_signal(message, "net.connman.Manager",
"TechnologyRemoved"))
path = dbus_message_get_member(message);
fprintf(stdout, "%-12s %-20s ", interface, path);
dbus_message_iter_init(message, &iter);
__connmanctl_dbus_print(&iter, "", " = ", " = ");
fprintf(stdout, "\n");
__connmanctl_redraw_rl();
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static struct {
char *interface;
bool enabled;
} monitor[] = {
{ "Service", false },
{ "Technology", false },
{ "Manager", false },
{ "vpn.Manager", false },
{ "vpn.Connection", false },
{ NULL, },
};
static void monitor_add(char *interface)
{
bool add_filter = true, found = false;
int i;
char *rule;
DBusError err;
for (i = 0; monitor[i].interface; i++) {
if (monitor[i].enabled == true)
add_filter = false;
if (g_strcmp0(interface, monitor[i].interface) == 0) {
if (monitor[i].enabled == true)
return;
monitor[i].enabled = true;
found = true;
}
}
if (found == false)
return;
if (add_filter == true)
dbus_connection_add_filter(connection, monitor_changed,
NULL, NULL);
dbus_error_init(&err);
rule = g_strdup_printf("type='signal',interface='net.connman.%s'",
interface);
dbus_bus_add_match(connection, rule, &err);
g_free(rule);
if (dbus_error_is_set(&err))
fprintf(stderr, "Error: %s\n", err.message);
}
static void monitor_del(char *interface)
{
bool del_filter = true, found = false;
int i;
char *rule;
for (i = 0; monitor[i].interface; i++) {
if (g_strcmp0(interface, monitor[i].interface) == 0) {
if (monitor[i].enabled == false)
return;
monitor[i].enabled = false;
found = true;
}
if (monitor[i].enabled == true)
del_filter = false;
}
if (found == false)
return;
rule = g_strdup_printf("type='signal',interface='net.connman.%s'",
interface);
dbus_bus_remove_match(connection, rule, NULL);
g_free(rule);
if (del_filter == true)
dbus_connection_remove_filter(connection, monitor_changed,
NULL);
}
static int cmd_monitor(char *args[], int num, struct connman_option *options)
{
bool add = true;
int c;
if (num > 3)
return -E2BIG;
if (num == 3) {
switch (parse_boolean(args[2])) {
case 0:
add = false;
break;
default:
break;
}
}
c = parse_args(args[1], options);
switch (c) {
case -1:
monitor_add("Service");
monitor_add("Technology");
monitor_add("Manager");
monitor_add("vpn.Manager");
monitor_add("vpn.Connection");
break;
case 's':
if (add == true)
monitor_add("Service");
else
monitor_del("Service");
break;
case 'c':
if (add == true)
monitor_add("Technology");
else
monitor_del("Technology");
break;
case 'm':
if (add == true)
monitor_add("Manager");
else
monitor_del("Manager");
break;
case 'M':
if (add == true)
monitor_add("vpn.Manager");
else
monitor_del("vpn.Manager");
break;
case 'C':
if (add == true)
monitor_add("vpn.Connection");
else
monitor_del("vpn.Connection");
break;
default:
switch(parse_boolean(args[1])) {
case 0:
monitor_del("Service");
monitor_del("Technology");
monitor_del("Manager");
monitor_del("vpn.Manager");
monitor_del("vpn.Connection");
break;
case 1:
monitor_add("Service");
monitor_add("Technology");
monitor_add("Manager");
monitor_add("vpn.Manager");
monitor_add("vpn.Connection");
break;
default:
return -EINVAL;
}
}
if (add == true)
return -EINPROGRESS;
return 0;
}
static int cmd_agent(char *args[], int num, struct connman_option *options)
{
if (!__connmanctl_is_interactive()) {
fprintf(stderr, "Error: Not supported in non-interactive "
"mode\n");
return 0;
}
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
switch(parse_boolean(args[1])) {
case 0:
__connmanctl_agent_unregister(connection);
break;
case 1:
if (__connmanctl_agent_register(connection) == -EINPROGRESS)
return -EINPROGRESS;
break;
default:
return -EINVAL;
break;
}
return 0;
}
static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *path = user_data;
char *str;
DBusMessageIter dict;
if (!error) {
fprintf(stdout, "%s\n", path);
dbus_message_iter_recurse(iter, &dict);
__connmanctl_dbus_print(&dict, " ", " = ", "\n");
fprintf(stdout, "\n");
} else {
str = strrchr(path, '/');
if (str)
str++;
else
str = path;
fprintf(stderr, "Error %s: %s\n", str, error);
}
g_free(user_data);
return 0;
}
static int vpnconnections_list(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (!error)
__connmanctl_vpnconnections_list(iter);
else
fprintf(stderr, "Error: %s\n", error);
return 0;
}
static int cmd_vpnconnections(char *args[], int num,
struct connman_option *options)
{
char *vpnconnection_name, *path;
if (num > 2)
return -E2BIG;
vpnconnection_name = args[1];
if (!vpnconnection_name)
return __connmanctl_dbus_method_call(connection,
VPN_SERVICE, VPN_PATH,
"net.connman.vpn.Manager", "GetConnections",
vpnconnections_list, NULL,
NULL, NULL);
if (check_dbus_name(vpnconnection_name) == false)
return -EINVAL;
path = g_strdup_printf("/net/connman/vpn/connection/%s",
vpnconnection_name);
return __connmanctl_dbus_method_call(connection, VPN_SERVICE, path,
"net.connman.vpn.Connection", "GetProperties",
vpnconnections_properties, path, NULL, NULL);
}
static int cmd_vpnagent(char *args[], int num, struct connman_option *options)
{
if (!__connmanctl_is_interactive()) {
fprintf(stderr, "Error: Not supported in non-interactive "
"mode\n");
return 0;
}
if (num > 2)
return -E2BIG;
if (num < 2)
return -EINVAL;
switch(parse_boolean(args[1])) {
case 0:
__connmanctl_vpn_agent_unregister(connection);
break;
case 1:
if (__connmanctl_vpn_agent_register(connection) ==
-EINPROGRESS)
return -EINPROGRESS;
break;
default:
return -EINVAL;
break;
}
return 0;
}
static DBusMessage *session_release(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
__connmanctl_save_rl();
fprintf(stdout, "Session %s released\n", session_path);
__connmanctl_redraw_rl();
g_free(session_path);
session_path = NULL;
session_connected = false;
return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
}
static DBusMessage *session_update(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
DBusMessageIter iter, dict;
__connmanctl_save_rl();
fprintf(stdout, "Session Update = {\n");
dbus_message_iter_init(message, &iter);
dbus_message_iter_recurse(&iter, &dict);
__connmanctl_dbus_print(&dict, "", " = ", "\n");
fprintf(stdout, "\n}\n");
dbus_message_iter_recurse(&iter, &dict);
while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter entry, variant;
char *field, *state;
dbus_message_iter_recurse(&dict, &entry);
dbus_message_iter_get_basic(&entry, &field);
if (dbus_message_iter_get_arg_type(&entry)
== DBUS_TYPE_STRING
&& !strcmp(field, "State")) {
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &variant);
if (dbus_message_iter_get_arg_type(&variant)
!= DBUS_TYPE_STRING)
break;
dbus_message_iter_get_basic(&variant, &state);
if (!session_connected && (!strcmp(state, "connected")
|| !strcmp(state, "online"))) {
fprintf(stdout, "Session %s connected\n",
session_path);
session_connected = true;
break;
}
if (!strcmp(state, "disconnected") &&
session_connected) {
fprintf(stdout, "Session %s disconnected\n",
session_path);
session_connected = false;
}
break;
}
dbus_message_iter_next(&dict);
}
__connmanctl_redraw_rl();
return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
}
static const GDBusMethodTable notification_methods[] = {
{ GDBUS_METHOD("Release", NULL, NULL, session_release) },
{ GDBUS_METHOD("Update", GDBUS_ARGS({"settings", "a{sv}"}),
NULL, session_update) },
{ },
};
static int session_notify_add(const char *path)
{
if (session_notify_path)
return 0;
if (!g_dbus_register_interface(connection, path,
"net.connman.Notification",
notification_methods, NULL, NULL,
NULL, NULL)) {
fprintf(stderr, "Error: Failed to register VPN Agent "
"callbacks\n");
return -EIO;
}
session_notify_path = g_strdup(path);
return 0;
}
static void session_notify_remove(void)
{
if (!session_notify_path)
return;
g_dbus_unregister_interface(connection, session_notify_path,
"net.connman.Notification");
g_free(session_notify_path);
session_notify_path = NULL;
}
static int session_connect_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
fprintf(stderr, "Error: %s", error);
return 0;
}
return -EINPROGRESS;
}
static int session_connect(void)
{
return __connmanctl_dbus_method_call(connection, "net.connman",
session_path, "net.connman.Session", "Connect",
session_connect_cb, NULL, NULL, NULL);
}
static int session_disconnect_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error)
fprintf(stderr, "Error: %s", error);
return 0;
}
static int session_disconnect(void)
{
return __connmanctl_dbus_method_call(connection, "net.connman",
session_path, "net.connman.Session", "Disconnect",
session_disconnect_cb, NULL, NULL, NULL);
}
static int session_create_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
gboolean connect = GPOINTER_TO_INT(user_data);
char *str;
if (error) {
fprintf(stderr, "Error creating session: %s", error);
session_notify_remove();
return 0;
}
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) {
fprintf(stderr, "Error creating session: No session path\n");
return -EINVAL;
}
g_free(session_path);
dbus_message_iter_get_basic(iter, &str);
session_path = g_strdup(str);
fprintf(stdout, "Session %s created\n", session_path);
if (connect)
return session_connect();
return -EINPROGRESS;
}
static void session_create_append(DBusMessageIter *iter, void *user_data)
{
const char *notify_path = user_data;
__connmanctl_dbus_append_dict(iter, NULL, NULL);
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
&notify_path);
}
static int session_create(gboolean connect)
{
int res;
char *notify_path;
notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
session_notify_add(notify_path);
res = __connmanctl_dbus_method_call(connection, "net.connman", "/",
"net.connman.Manager", "CreateSession",
session_create_cb, GINT_TO_POINTER(connect),
session_create_append, notify_path);
g_free(notify_path);
if (res < 0 && res != -EINPROGRESS)
session_notify_remove();
return res;
}
static int session_destroy_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
fprintf(stderr, "Error destroying session: %s", error);
return 0;
}
fprintf(stdout, "Session %s ended\n", session_path);
g_free(session_path);
session_path = NULL;
session_connected = false;
return 0;
}
static void session_destroy_append(DBusMessageIter *iter, void *user_data)
{
const char *path = user_data;
dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
}
static int session_destroy(void)
{
return __connmanctl_dbus_method_call(connection, "net.connman", "/",
"net.connman.Manager", "DestroySession",
session_destroy_cb, NULL,
session_destroy_append, session_path);
}
static int session_config_return(DBusMessageIter *iter, const char *error,
void *user_data)
{
char *property_name = user_data;
if (error)
fprintf(stderr, "Error setting session %s: %s\n",
property_name, error);
return 0;
}
static void session_config_append_array(DBusMessageIter *iter,
void *user_data)
{
struct config_append *append = user_data;
char **opts = append->opts;
int i = 1;
if (!opts)
return;
while (opts[i] && strncmp(opts[i], "--", 2) != 0) {
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
&opts[i]);
i++;
}
append->values = i;
}
static int session_config(char *args[], int num,
struct connman_option *options)
{
int index = 0, res = 0;
struct config_append append;
char c;
while (index < num && args[index]) {
append.opts = &args[index];
append.values = 0;
c = parse_args(args[index], options);
switch (c) {
case 'b':
res = __connmanctl_dbus_session_change_array(connection,
session_path, session_config_return,
"AllowedBearers", "AllowedBearers",
session_config_append_array, &append);
break;
case 't':
if (!args[index + 1]) {
res = -EINVAL;
break;
}
res = __connmanctl_dbus_session_change(connection,
session_path, session_config_return,
"ConnectionType", "ConnectionType",
DBUS_TYPE_STRING, &args[index + 1]);
append.values = 2;
break;
default:
res = -EINVAL;
}
if (res < 0 && res != -EINPROGRESS) {
printf("Error '%s': %s\n", args[index],
strerror(-res));
return 0;
}
index += append.values;
}
return 0;
}
static int cmd_session(char *args[], int num, struct connman_option *options)
{
char *command;
if (num < 2)
return -EINVAL;
command = args[1];
switch(parse_boolean(command)) {
case 0:
if (!session_path)
return -EALREADY;
return session_destroy();
case 1:
if (session_path)
return -EALREADY;
return session_create(FALSE);
default:
if (!strcmp(command, "connect")) {
if (!session_path)
return session_create(TRUE);
return session_connect();
} else if (!strcmp(command, "disconnect")) {
if (!session_path) {
fprintf(stdout, "Session does not exist\n");
return 0;
}
return session_disconnect();
} else if (!strcmp(command, "config")) {
if (!session_path) {
fprintf(stdout, "Session does not exist\n");
return 0;
}
if (num == 2)
return -EINVAL;
return session_config(&args[2], num - 2, options);
}
}
return -EINVAL;
}
static int cmd_exit(char *args[], int num, struct connman_option *options)
{
return 1;
}
static char *lookup_service(const char *text, int state)
{
static int len = 0;
static GHashTableIter iter;
gpointer key, value;
if (state == 0) {
g_hash_table_iter_init(&iter, service_hash);
len = strlen(text);
}
while (g_hash_table_iter_next(&iter, &key, &value)) {
const char *service = key;
if (strncmp(text, service, len) == 0)
return strdup(service);
}
return NULL;
}
static char *lookup_service_arg(const char *text, int state)
{
if (__connmanctl_input_calc_level() > 1) {
__connmanctl_input_lookup_end();
return NULL;
}
return lookup_service(text, state);
}
static char *lookup_peer(const char *text, int state)
{
static GHashTableIter iter;
gpointer key, value;
static int len = 0;
if (state == 0) {
g_hash_table_iter_init(&iter, peer_hash);
len = strlen(text);
}
while (g_hash_table_iter_next(&iter, &key, &value)) {
const char *peer = key;
if (strncmp(text, peer, len) == 0)
return strdup(peer);
}
return NULL;
}
static char *lookup_peer_arg(const char *text, int state)
{
if (__connmanctl_input_calc_level() > 1) {
__connmanctl_input_lookup_end();
return NULL;
}
return lookup_peer(text, state);
}
static char *lookup_technology(const char *text, int state)
{
static int len = 0;
static GHashTableIter iter;
gpointer key, value;
if (state == 0) {
g_hash_table_iter_init(&iter, technology_hash);
len = strlen(text);
}
while (g_hash_table_iter_next(&iter, &key, &value)) {
const char *technology = key;
if (strncmp(text, technology, len) == 0)
return strdup(technology);
}
return NULL;
}
static char *lookup_technology_arg(const char *text, int state)
{
if (__connmanctl_input_calc_level() > 1) {
__connmanctl_input_lookup_end();
return NULL;
}
return lookup_technology(text, state);
}
static char *lookup_technology_offline(const char *text, int state)
{
static int len = 0;
static bool end = false;
char *str;
if (__connmanctl_input_calc_level() > 1) {
__connmanctl_input_lookup_end();
return NULL;
}
if (state == 0) {
len = strlen(text);
end = false;
}
if (end)
return NULL;
str = lookup_technology(text, state);
if (str)
return str;
end = true;
if (strncmp(text, "offline", len) == 0)
return strdup("offline");
return NULL;
}
static char *lookup_on_off(const char *text, int state)
{
char *onoff[] = { "on", "off", NULL };
static int idx = 0;
static int len = 0;
char *str;
if (!state) {
idx = 0;
len = strlen(text);
}
while (onoff[idx]) {
str = onoff[idx];
idx++;
if (!strncmp(text, str, len))
return strdup(str);
}
return NULL;
}
static char *lookup_tether(const char *text, int state)
{
int level;
level = __connmanctl_input_calc_level();
if (level < 2)
return lookup_technology(text, state);
if (level == 2)
return lookup_on_off(text, state);
__connmanctl_input_lookup_end();
return NULL;
}
static char *lookup_agent(const char *text, int state)
{
if (__connmanctl_input_calc_level() > 1) {
__connmanctl_input_lookup_end();
return NULL;
}
return lookup_on_off(text, state);
}
static struct connman_option service_options[] = {
{"properties", 'p', "[<service>] (obsolete)"},
{ NULL, }
};
static struct connman_option config_options[] = {
{"nameservers", 'n', "<dns1> [<dns2>] [<dns3>]"},
{"timeservers", 't', "<ntp1> [<ntp2>] [...]"},
{"domains", 'd', "<domain1> [<domain2>] [...]"},
{"ipv6", 'v', "off|auto [enable|disable|preferred]|\n"
"\t\t\tmanual <address> <prefixlength> <gateway>"},
{"proxy", 'x', "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
"\t\t\t[exclude <exclude1> [<exclude2>] [...]]"},
{"autoconnect", 'a', "yes|no"},
{"ipv4", 'i', "off|dhcp|manual <address> <netmask> <gateway>"},
{"remove", 'r', " Remove service"},
{"order", 'o', " <order> set service order manually"},
{ NULL, }
};
static struct connman_option monitor_options[] = {
{"services", 's', "[off] Monitor only services"},
{"tech", 'c', "[off] Monitor only technologies"},
{"manager", 'm', "[off] Monitor only manager interface"},
{"vpnmanager", 'M', "[off] Monitor only VPN manager "
"interface"},
{"vpnconnection", 'C', "[off] Monitor only VPN "
"connections" },
{ NULL, }
};
static struct connman_option session_options[] = {
{"bearers", 'b', "<technology1> [<technology2> [...]]"},
{"type", 't', "local|internet|any"},
{ NULL, }
};
static char *lookup_options(struct connman_option *options, const char *text,
int state)
{
static int idx = 0;
static int len = 0;
const char *str;
if (state == 0) {
idx = 0;
len = strlen(text);
}
while (options[idx].name) {
str = options[idx].name;
idx++;
if (str && strncmp(text, str, len) == 0)
return strdup(str);
}
return NULL;
}
static char *lookup_monitor(const char *text, int state)
{
int level;
level = __connmanctl_input_calc_level();
if (level < 2)
return lookup_options(monitor_options, text, state);
if (level == 2)
return lookup_on_off(text, state);
__connmanctl_input_lookup_end();
return NULL;
}
static char *lookup_config(const char *text, int state)
{
if (__connmanctl_input_calc_level() < 2)
return lookup_service(text, state);
return lookup_options(config_options, text, state);
}
static char *lookup_session(const char *text, int state)
{
return lookup_options(session_options, text, state);
}
static int peer_service_cb(DBusMessageIter *iter, const char *error,
void *user_data)
{
bool registration = GPOINTER_TO_INT(user_data);
if (error)
fprintf(stderr, "Error %s peer service: %s\n",
registration ? "registering" : "unregistering", error);
else
fprintf(stdout, "Peer service %s\n",
registration ? "registered" : "unregistered");
return 0;
}
struct _peer_service {
unsigned char *bjr_query;
int bjr_query_len;
unsigned char *bjr_response;
int bjr_response_len;
unsigned char *wfd_ies;
int wfd_ies_len;
char *upnp_service;
int version;
int master;
};
static void append_dict_entry_fixed_array(DBusMessageIter *iter,
const char *property, void *value, int length)
{
DBusMessageIter dict_entry, variant, array;
dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY,
NULL, &dict_entry);
dbus_message_iter_append_basic(&dict_entry, DBUS_TYPE_STRING,
&property);
dbus_message_iter_open_container(&dict_entry, DBUS_TYPE_VARIANT,
DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
&variant);
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
DBUS_TYPE_BYTE_AS_STRING, &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
value, length);
dbus_message_iter_close_container(&variant, &array);
dbus_message_iter_close_container(&dict_entry, &variant);
dbus_message_iter_close_container(iter, &dict_entry);
}
static void append_peer_service_dict(DBusMessageIter *iter, void *user_data)
{
struct _peer_service *service = user_data;
if (service->bjr_query && service->bjr_response) {
append_dict_entry_fixed_array(iter, "BonjourQuery",
&service->bjr_query, service->bjr_query_len);
append_dict_entry_fixed_array(iter, "BonjourResponse",
&service->bjr_response, service->bjr_response_len);
} else if (service->upnp_service && service->version) {
__connmanctl_dbus_append_dict_entry(iter, "UpnpVersion",
DBUS_TYPE_INT32, &service->version);
__connmanctl_dbus_append_dict_entry(iter, "UpnpService",
DBUS_TYPE_STRING, &service->upnp_service);
} else if (service->wfd_ies) {
append_dict_entry_fixed_array(iter, "WiFiDisplayIEs",
&service->wfd_ies, service->wfd_ies_len);
}
}
static void peer_service_append(DBusMessageIter *iter, void *user_data)
{
struct _peer_service *service = user_data;
dbus_bool_t master;
__connmanctl_dbus_append_dict(iter, append_peer_service_dict, service);
if (service->master < 0)
return;
master = service->master == 1 ? TRUE : FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &master);
}
static struct _peer_service *fill_in_peer_service(unsigned char *bjr_query,
int bjr_query_len, unsigned char *bjr_response,
int bjr_response_len, char *upnp_service,
int version, unsigned char *wfd_ies,
int wfd_ies_len)
{
struct _peer_service *service;
service = dbus_malloc0(sizeof(*service));
if (bjr_query_len && bjr_response_len) {
service->bjr_query = dbus_malloc0(bjr_query_len);
memcpy(service->bjr_query, bjr_query, bjr_query_len);
service->bjr_query_len = bjr_query_len;
service->bjr_response = dbus_malloc0(bjr_response_len);
memcpy(service->bjr_response, bjr_response, bjr_response_len);
service->bjr_response_len = bjr_response_len;
} else if (upnp_service && version) {
service->upnp_service = strdup(upnp_service);
service->version = version;
} else if (wfd_ies && wfd_ies_len) {
service->wfd_ies = dbus_malloc0(wfd_ies_len);
memcpy(service->wfd_ies, wfd_ies, wfd_ies_len);
service->wfd_ies_len = wfd_ies_len;
} else {
dbus_free(service);
service = NULL;
}
return service;
}
static void free_peer_service(struct _peer_service *service)
{
dbus_free(service->bjr_query);
dbus_free(service->bjr_response);
dbus_free(service->wfd_ies);
free(service->upnp_service);
dbus_free(service);
}
static int peer_service_register(unsigned char *bjr_query, int bjr_query_len,
unsigned char *bjr_response, int bjr_response_len,
char *upnp_service, int version,
unsigned char *wfd_ies, int wfd_ies_len, int master)
{
struct _peer_service *service;
bool registration = true;
int ret;
service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
bjr_response_len, upnp_service, version,
wfd_ies, wfd_ies_len);
if (!service)
return -EINVAL;
service->master = master;
ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
"net.connman.Manager", "RegisterPeerService",
peer_service_cb, GINT_TO_POINTER(registration),
peer_service_append, service);
free_peer_service(service);
return ret;
}
static int peer_service_unregister(unsigned char *bjr_query, int bjr_query_len,
unsigned char *bjr_response, int bjr_response_len,
char *upnp_service, int version,
unsigned char *wfd_ies, int wfd_ies_len)
{
struct _peer_service *service;
bool registration = false;
int ret;
service = fill_in_peer_service(bjr_query, bjr_query_len, bjr_response,
bjr_response_len, upnp_service, version,
wfd_ies, wfd_ies_len);
if (!service)
return -EINVAL;
service->master = -1;
ret = __connmanctl_dbus_method_call(connection, "net.connman", "/",
"net.connman.Manager", "UnregisterPeerService",
peer_service_cb, GINT_TO_POINTER(registration),
peer_service_append, service);
free_peer_service(service);
return ret;
}
static int parse_spec_array(char *command, unsigned char spec[1024])
{
int length, pos, end;
char b[3] = {};
char *e;
end = strlen(command);
for (e = NULL, length = pos = 0; command[pos] != '\0'; length++) {
if (pos+2 > end)
return -EINVAL;
b[0] = command[pos];
b[1] = command[pos+1];
spec[length] = strtol(b, &e, 16);
if (e && *e != '\0')
return -EINVAL;
pos += 2;
}
return length;
}
static int cmd_peer_service(char *args[], int num,
struct connman_option *options)
{
unsigned char bjr_query[1024] = {};
unsigned char bjr_response[1024] = {};
unsigned char wfd_ies[1024] = {};
char *upnp_service = NULL;
int bjr_query_len = 0, bjr_response_len = 0;
int version = 0, master = 0, wfd_ies_len = 0;
int limit;
if (num < 4)
return -EINVAL;
if (!strcmp(args[2], "wfd_ies")) {
wfd_ies_len = parse_spec_array(args[3], wfd_ies);
if (wfd_ies_len == -EINVAL)
return -EINVAL;
limit = 5;
goto master;
}
if (num < 6)
return -EINVAL;
limit = 7;
if (!strcmp(args[2], "bjr_query")) {
if (strcmp(args[4], "bjr_response"))
return -EINVAL;
bjr_query_len = parse_spec_array(args[3], bjr_query);
bjr_response_len = parse_spec_array(args[5], bjr_response);
if (bjr_query_len == -EINVAL || bjr_response_len == -EINVAL)
return -EINVAL;
} else if (!strcmp(args[2], "upnp_service")) {
char *e = NULL;
if (strcmp(args[4], "upnp_version"))
return -EINVAL;
upnp_service = args[3];
version = strtol(args[5], &e, 10);
if (*e != '\0')
return -EINVAL;
}
master:
if (num == limit) {
master = parse_boolean(args[6]);
if (master < 0)
return -EINVAL;
}
if (!strcmp(args[1], "register")) {
return peer_service_register(bjr_query, bjr_query_len,
bjr_response, bjr_response_len, upnp_service,
version, wfd_ies, wfd_ies_len, master);
} else if (!strcmp(args[1], "unregister")) {
return peer_service_unregister(bjr_query, bjr_query_len,
bjr_response, bjr_response_len, upnp_service,
version, wfd_ies, wfd_ies_len);
}
return -EINVAL;
}
static const struct {
const char *cmd;
const char *argument;
struct connman_option *options;
int (*func) (char *args[], int num, struct connman_option *options);
const char *desc;
__connmanctl_lookup_cb cb;
} cmd_table[] = {
{ "state", NULL, NULL, cmd_state,
"Shows if the system is online or offline", NULL },
{ "technologies", NULL, NULL, cmd_technologies,
"Display technologies", NULL },
{ "enable", "<technology>|offline", NULL, cmd_enable,
"Enables given technology or offline mode",
lookup_technology_offline },
{ "disable", "<technology>|offline", NULL, cmd_disable,
"Disables given technology or offline mode",
lookup_technology_offline },
{ "tether", "<technology> on|off\n"
" wifi [on|off] <ssid> <passphrase> ",
NULL, cmd_tether,
"Enable, disable tethering, set SSID and passphrase for wifi",
lookup_tether },
{ "prefer", "<technology>", NULL, cmd_prefer,
"Prefer given technology", NULL },
{ "services", "[<service>]", service_options, cmd_services,
"Display services", lookup_service_arg },
{ "peers", "[peer]", NULL, cmd_peers,
"Display peers", lookup_peer_arg },
{ "scan", "<technology>", NULL, cmd_scan,
"Scans for new services for given technology",
lookup_technology_arg },
{ "connect", "<service/peer>", NULL, cmd_connect,
"Connect a given service or peer", lookup_service_arg },
{ "disconnect", "<service/peer>", NULL, cmd_disconnect,
"Disconnect a given service or peer", lookup_service_arg },
{ "config", "<service>", config_options, cmd_config,
"Set service configuration options", lookup_config },
{ "monitor", "[off]", monitor_options, cmd_monitor,
"Monitor signals from interfaces", lookup_monitor },
{ "agent", "on|off", NULL, cmd_agent,
"Agent mode", lookup_agent },
{"vpnconnections", "[<connection>]", NULL, cmd_vpnconnections,
"Display VPN connections", NULL },
{ "vpnagent", "on|off", NULL, cmd_vpnagent,
"VPN Agent mode", lookup_agent },
{ "session", "on|off|connect|disconnect|config", session_options,
cmd_session, "Enable or disable a session", lookup_session },
{ "peer_service", "register|unregister <specs> <master>\n"
"Where specs are:\n"
"\tbjr_query <query> bjr_response <response>\n"
"\tupnp_service <service> upnp_version <version>\n"
"\twfd_ies <ies>\n", NULL,
cmd_peer_service, "(Un)Register a Peer Service", NULL },
{ "check", "google|connman <service> <service> ...", NULL, cmd_check,
"Online check a given service or peer list", lookup_service_arg },
{ "help", NULL, NULL, cmd_help,
"Show help", NULL },
{ "exit", NULL, NULL, cmd_exit,
"Exit", NULL },
{ "quit", NULL, NULL, cmd_exit,
"Quit", NULL },
{ NULL, },
};
static int cmd_help(char *args[], int num, struct connman_option *options)
{
bool interactive = __connmanctl_is_interactive();
int i, j;
if (interactive == false)
fprintf(stdout, "Usage: connmanctl [[command] [args]]\n");
for (i = 0; cmd_table[i].cmd; i++) {
const char *cmd = cmd_table[i].cmd;
const char *argument = cmd_table[i].argument;
const char *desc = cmd_table[i].desc;
printf("%-16s%-22s%s\n", cmd? cmd: "",
argument? argument: "",
desc? desc: "");
if (cmd_table[i].options) {
for (j = 0; cmd_table[i].options[j].name;
j++) {
const char *options_desc =
cmd_table[i].options[j].desc ?
cmd_table[i].options[j].desc: "";
printf(" --%-16s%s\n",
cmd_table[i].options[j].name,
options_desc);
}
}
}
if (interactive == false)
fprintf(stdout, "\nNote: arguments and output are considered "
"EXPERIMENTAL for now.\n");
return 0;
}
__connmanctl_lookup_cb __connmanctl_get_lookup_func(const char *text)
{
int i, cmdlen, textlen;
if (!text)
return NULL;
textlen = strlen(text);
for (i = 0; cmd_table[i].cmd; i++) {
cmdlen = strlen(cmd_table[i].cmd);
if (textlen > cmdlen && text[cmdlen] != ' ')
continue;
if (strncmp(cmd_table[i].cmd, text, cmdlen) == 0)
return cmd_table[i].cb;
}
return NULL;
}
int __connmanctl_commands(DBusConnection *dbus_conn, char *argv[], int argc)
{
int i, result;
connection = dbus_conn;
for (i = 0; cmd_table[i].cmd; i++) {
if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 &&
cmd_table[i].func) {
result = cmd_table[i].func(argv, argc,
cmd_table[i].options);
if (result < 0 && result != -EINPROGRESS)
fprintf(stderr, "Error '%s': %s\n", argv[0],
strerror(-result));
return result;
}
}
fprintf(stderr, "Error '%s': Unknown command\n", argv[0]);
return -EINVAL;
}
char *__connmanctl_lookup_command(const char *text, int state)
{
static int i = 0;
static int len = 0;
if (state == 0) {
i = 0;
len = strlen(text);
}
while (cmd_table[i].cmd) {
const char *command = cmd_table[i].cmd;
i++;
if (strncmp(text, command, len) == 0)
return strdup(command);
}
return NULL;
}
static char *get_path(char *full_path)
{
char *path;
path = strrchr(full_path, '/');
if (path && *path != '\0')
path++;
else
path = full_path;
return path;
}
static void add_service_id(const char *path)
{
g_hash_table_replace(service_hash, g_strdup(path),
GINT_TO_POINTER(TRUE));
}
static void remove_service_id(const char *path)
{
g_hash_table_remove(service_hash, path);
}
static void services_added(DBusMessageIter *iter)
{
DBusMessageIter array;
char *path = NULL;
while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) {
dbus_message_iter_recurse(iter, &array);
if (dbus_message_iter_get_arg_type(&array) !=
DBUS_TYPE_OBJECT_PATH)
return;
dbus_message_iter_get_basic(&array, &path);
add_service_id(get_path(path));
dbus_message_iter_next(iter);
}
}
static void update_services(DBusMessageIter *iter)
{
DBusMessageIter array;
char *path;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
dbus_message_iter_recurse(iter, &array);
services_added(&array);
dbus_message_iter_next(iter);
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
dbus_message_iter_recurse(iter, &array);
while (dbus_message_iter_get_arg_type(&array) ==
DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&array, &path);
remove_service_id(get_path(path));
dbus_message_iter_next(&array);
}
}
static int populate_service_hash(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
fprintf(stderr, "Error getting services: %s", error);
return 0;
}
update_services(iter);
return 0;
}
static void add_peer_id(const char *path)
{
g_hash_table_replace(peer_hash, g_strdup(path), GINT_TO_POINTER(TRUE));
}
static void remove_peer_id(const char *path)
{
g_hash_table_remove(peer_hash, path);
}
static void peers_added(DBusMessageIter *iter)
{
DBusMessageIter array;
char *path = NULL;
while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRUCT) {
dbus_message_iter_recurse(iter, &array);
if (dbus_message_iter_get_arg_type(&array) !=
DBUS_TYPE_OBJECT_PATH)
return;
dbus_message_iter_get_basic(&array, &path);
add_peer_id(get_path(path));
dbus_message_iter_next(iter);
}
}
static void update_peers(DBusMessageIter *iter)
{
DBusMessageIter array;
char *path;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
dbus_message_iter_recurse(iter, &array);
peers_added(&array);
dbus_message_iter_next(iter);
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
dbus_message_iter_recurse(iter, &array);
while (dbus_message_iter_get_arg_type(&array) ==
DBUS_TYPE_OBJECT_PATH) {
dbus_message_iter_get_basic(&array, &path);
remove_peer_id(get_path(path));
dbus_message_iter_next(&array);
}
}
static int populate_peer_hash(DBusMessageIter *iter,
const char *error, void *user_data)
{
if (error) {
fprintf(stderr, "Error getting peers: %s", error);
return 0;
}
update_peers(iter);
return 0;
}
static void add_technology_id(const char *path)
{
g_hash_table_replace(technology_hash, g_strdup(path),
GINT_TO_POINTER(TRUE));
}
static void remove_technology_id(const char *path)
{
g_hash_table_remove(technology_hash, path);
}
static void remove_technology(DBusMessageIter *iter)
{
char *path = NULL;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
return;
dbus_message_iter_get_basic(iter, &path);
remove_technology_id(get_path(path));
}
static void add_technology(DBusMessageIter *iter)
{
char *path = NULL;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
return;
dbus_message_iter_get_basic(iter, &path);
add_technology_id(get_path(path));
}
static void update_technologies(DBusMessageIter *iter)
{
DBusMessageIter array;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
dbus_message_iter_recurse(iter, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
DBusMessageIter object_path;
dbus_message_iter_recurse(&array, &object_path);
add_technology(&object_path);
dbus_message_iter_next(&array);
}
}
static int populate_technology_hash(DBusMessageIter *iter, const char *error,
void *user_data)
{
if (error) {
fprintf(stderr, "Error getting technologies: %s", error);
return 0;
}
update_technologies(iter);
return 0;
}
static DBusHandlerResult monitor_completions_changed(
DBusConnection *connection,
DBusMessage *message, void *user_data)
{
bool *enabled = user_data;
DBusMessageIter iter;
DBusHandlerResult handled;
if (*enabled)
handled = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
else
handled = DBUS_HANDLER_RESULT_HANDLED;
if (dbus_message_is_signal(message, "net.connman.Manager",
"ServicesChanged")) {
dbus_message_iter_init(message, &iter);
update_services(&iter);
return handled;
}
if (dbus_message_is_signal(message, "net.connman.Manager",
"PeersChanged")) {
dbus_message_iter_init(message, &iter);
update_peers(&iter);
return handled;
}
if (dbus_message_is_signal(message, "net.connman.Manager",
"TechnologyAdded")) {
dbus_message_iter_init(message, &iter);
add_technology(&iter);
return handled;
}
if (dbus_message_is_signal(message, "net.connman.Manager",
"TechnologyRemoved")) {
dbus_message_iter_init(message, &iter);
remove_technology(&iter);
return handled;
}
if (!g_strcmp0(dbus_message_get_interface(message),
"net.connman.Manager"))
return handled;
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
{
bool *manager_enabled = NULL;
DBusError err;
int i;
for (i = 0; monitor[i].interface; i++) {
if (!strcmp(monitor[i].interface, "Manager")) {
manager_enabled = &monitor[i].enabled;
break;
}
}
if (!dbus_conn) {
g_hash_table_destroy(service_hash);
g_hash_table_destroy(technology_hash);
dbus_bus_remove_match(connection,
"type='signal',interface='net.connman.Manager'", NULL);
dbus_connection_remove_filter(connection,
monitor_completions_changed,
manager_enabled);
return;
}
connection = dbus_conn;
service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
peer_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
technology_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, NULL);
__connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, CONNMAN_PATH,
"net.connman.Manager", "GetServices",
populate_service_hash, NULL, NULL, NULL);
__connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, CONNMAN_PATH,
"net.connman.Manager", "GetPeers",
populate_peer_hash, NULL, NULL, NULL);
__connmanctl_dbus_method_call(connection,
CONNMAN_SERVICE, CONNMAN_PATH,
"net.connman.Manager", "GetTechnologies",
populate_technology_hash, NULL, NULL, NULL);
dbus_connection_add_filter(connection,
monitor_completions_changed, manager_enabled,
NULL);
dbus_error_init(&err);
dbus_bus_add_match(connection,
"type='signal',interface='net.connman.Manager'", &err);
if (dbus_error_is_set(&err))
fprintf(stderr, "Error: %s\n", err.message);
}