blob: 7e6424f5f2513c862f2d50111c984253613e811c [file] [log] [blame]
/*
*
* Connection Manager
*
* Copyright (C) 2012 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 <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <glib.h>
#include "services.h"
#include "dbus.h"
static void append_property_array(DBusMessageIter *iter, char *property,
char **data, int num_args)
{
DBusMessageIter value, array;
int i;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
dbus_array_open(iter, &value);
dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array);
for (i = 0; i < num_args; i++) {
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
&data[i]);
printf("Added: %s\n", data[i]);
}
dbus_message_iter_close_container(&value, &array);
dbus_message_iter_close_container(iter, &value);
}
static void append_property_dict(DBusMessageIter *iter, char *property,
char **keys, char **data, int num_args)
{
DBusMessageIter value, dict, entry, dict_key;
int i;
unsigned char prefix;
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &property);
/* Top most level is a{sv} */
dbus_dict_open_variant(iter, &value);
dbus_dict_open(&value, &dict);
for (i = 0; i < num_args; i++) {
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
NULL, &entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
&keys[i]);
if (strcmp(property, "IPv6.Configuration") == 0 &&
g_strcmp0(keys[i], "PrefixLength")) {
if (data[i] == NULL) {
fprintf(stderr, "No values entered!\n");
exit(EXIT_FAILURE);
}
prefix = atoi(data[i]);
dbus_message_iter_open_container(&entry,
DBUS_TYPE_VARIANT,
DBUS_TYPE_BYTE_AS_STRING,
&dict_key);
dbus_message_iter_append_basic(&dict_key,
DBUS_TYPE_BYTE, &prefix);
} else {
dbus_message_iter_open_container(&entry,
DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING,
&dict_key);
dbus_message_iter_append_basic(&dict_key,
DBUS_TYPE_STRING,
&data[i]);
}
dbus_message_iter_close_container(&entry, &dict_key);
dbus_message_iter_close_container(&dict, &entry);
}
/* Close {sv}, then close a{sv} */
dbus_dict_close(&value, &dict);
dbus_dict_close(iter, &value);
}
void iterate_array(DBusMessageIter *iter)
{
DBusMessageIter array_item;
dbus_bool_t key_bool;
char *key_str;
dbus_message_iter_recurse(iter, &array_item);
/* Make sure the entry is not NULL! */
printf("[ ");
while (dbus_message_iter_get_arg_type(&array_item) !=
DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&array_item) ==
DBUS_TYPE_STRING) {
dbus_message_iter_get_basic(&array_item,
&key_str);
printf("%s ", key_str);
} else if (dbus_message_iter_get_arg_type(&array_item) ==
DBUS_TYPE_BOOLEAN) {
dbus_message_iter_get_basic(&array_item, &key_bool);
printf("%s ", key_bool == TRUE ? "True"
: "False");
}
dbus_message_iter_next(&array_item);
}
if (dbus_message_iter_get_arg_type(&array_item) ==
DBUS_TYPE_INVALID)
printf("] ");
}
void iterate_dict(DBusMessageIter *dict, char *string, uint16_t key_int)
{
DBusMessageIter dict_entry, sub_dict_entry;
printf("{ ");
while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
dbus_message_iter_recurse(dict, &dict_entry);
dbus_message_iter_get_basic(&dict_entry, &string);
printf("%s=", string);
dbus_message_iter_next(&dict_entry);
while (dbus_message_iter_get_arg_type(&dict_entry)
!= DBUS_TYPE_INVALID) {
dbus_message_iter_recurse(&dict_entry, &sub_dict_entry);
if (dbus_message_iter_get_arg_type(&sub_dict_entry)
== DBUS_TYPE_UINT16) {
dbus_message_iter_get_basic(&sub_dict_entry,
&key_int);
printf("%d ", key_int);
} else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
== DBUS_TYPE_STRING) {
dbus_message_iter_get_basic(&sub_dict_entry,
&string);
printf("%s ", string);
} else if (dbus_message_iter_get_arg_type(&sub_dict_entry)
== DBUS_TYPE_ARRAY) {
iterate_array(&sub_dict_entry);
}
dbus_message_iter_next(&dict_entry);
}
dbus_message_iter_next(dict);
}
printf("}");
}
/* Get dictionary info about the current service and store it */
static void extract_service_properties(DBusMessageIter *dict,
struct service_data *service)
{
DBusMessageIter entry, value, array_item;
char *key;
char *key_str;
uint16_t key_uint16;
dbus_bool_t key_bool;
while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
dbus_message_iter_recurse(dict, &entry);
dbus_message_iter_get_basic(&entry, &key);
printf("\n %s = ", key);
if (strcmp(key, "Name") == 0 && strlen(key) < 5)
service->name = key;
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &value);
/* Check if entry is a dictionary itself */
if (strcmp(key, "Ethernet") == 0 ||
/* if just strcmp, the .Configuration names don't match
* and they are iterated with iterate_array instead*/
strncmp(key, "IPv4", 4) == 0 ||
strncmp(key, "IPv6", 4) == 0 ||
strncmp(key, "Proxy", 5) == 0 ||
strcmp(key, "Provider") == 0) {
dbus_message_iter_recurse(&value, &array_item);
iterate_dict(&array_item, key_str, key_uint16);
} else
switch (dbus_message_iter_get_arg_type(&value)) {
case DBUS_TYPE_ARRAY:
iterate_array(&value);
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(&value, &key_bool);
printf("%s", key_bool == TRUE ? "True" : "False");
break;
case DBUS_TYPE_BYTE:
dbus_message_iter_get_basic(&value, &key_uint16);
printf("%d", key_uint16);
break;
case DBUS_TYPE_STRING:
dbus_message_iter_get_basic(&value, &key_str);
printf("%s", key_str);
break;
}
dbus_message_iter_next(dict);
}
printf("\n\n");
}
static void match_service_name(DBusMessage *message, char *service_name,
struct service_data *service)
{
DBusMessageIter iter, array;
dbus_message_iter_init(message, &iter);
dbus_message_iter_recurse(&iter, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
DBusMessageIter entry, dict;
char *path;
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &path);
service->path = strip_service_path(path);
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &dict);
extract_service_name(&dict, service);
if (g_strcmp0(service_name, service->name) == 0) {
printf(" Matched %s with %s\n\n", service->name,
service->path);
break;
}
dbus_message_iter_next(&array);
}
}
void extract_service_name(DBusMessageIter *dict, struct service_data *service)
{
DBusMessageIter dict_entry, value;
const char *key;
const char *state;
while (dbus_message_iter_get_arg_type(dict) == DBUS_TYPE_DICT_ENTRY) {
dbus_message_iter_recurse(dict, &dict_entry);
dbus_message_iter_get_basic(&dict_entry, &key);
if (strcmp(key, "Name") == 0) {
dbus_message_iter_next(&dict_entry);
dbus_message_iter_recurse(&dict_entry, &value);
dbus_message_iter_get_basic(&value, &service->name);
}
if (strcmp(key, "AutoConnect") == 0) {
dbus_message_iter_next(&dict_entry);
dbus_message_iter_recurse(&dict_entry, &value);
dbus_message_iter_get_basic(&value, &service->autoconn);
}
if (strcmp(key, "State") == 0) {
dbus_message_iter_next(&dict_entry);
dbus_message_iter_recurse(&dict_entry, &value);
dbus_message_iter_get_basic(&value, &state);
if (strcmp(state, "ready") == 0) {
service->connected = TRUE;
service->online = FALSE;
} else if (strcmp(state, "online") == 0) {
service->connected = FALSE;
service->online = TRUE;
} else {
service->connected = FALSE;
service->online = FALSE;
}
}
if (strcmp(key, "Favorite") == 0) {
dbus_message_iter_next(&dict_entry);
dbus_message_iter_recurse(&dict_entry, &value);
dbus_message_iter_get_basic(&value, &service->favorite);
}
dbus_message_iter_next(dict);
}
}
/* Show detailed information about a service */
void extract_services(DBusMessage *message, char *service_name)
{
DBusMessageIter iter, array;
dbus_message_iter_init(message, &iter);
dbus_message_iter_recurse(&iter, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
DBusMessageIter entry, dict;
struct service_data service;
char *path;
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &path);
service.path = strip_service_path(path);
if (g_strcmp0(service.path, service_name) == 0) {
printf("[ %s ]\n", service.path);
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &dict);
extract_service_properties(&dict, &service);
}
dbus_message_iter_next(&array);
}
}
/* Support both string names and path names for connecting to services */
char *strip_service_path(char *service)
{
char *service_name;
service_name = strrchr(service, '/');
if (service_name == NULL)
return service;
else
return service_name + 1;
}
/* Show a simple list of service names only */
void get_services(DBusMessage *message)
{
DBusMessageIter iter, array;
dbus_message_iter_init(message, &iter);
dbus_message_iter_recurse(&iter, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
DBusMessageIter entry, dict;
struct service_data service;
char *path;
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &path);
service.path = strip_service_path(path);
dbus_message_iter_next(&entry);
dbus_message_iter_recurse(&entry, &dict);
extract_service_name(&dict, &service);
printf("%-1s%-1s%-1s %-20s { %s }\n",
service.favorite ? "*" : "",
service.autoconn ? "A" : "",
service.online ? "O" : (service.connected ? "R" : ""),
service.name, service.path);
dbus_message_iter_next(&array);
}
}
const char *find_service(DBusConnection *connection, DBusMessage *message,
char *service_name, struct service_data *service)
{
DBusMessageIter iter, array, entry;
char *path;
service_name = strip_service_path(service_name);
match_service_name(message, service_name, service);
/* Service name did not match, so check if entry is a path */
if (g_strcmp0(service_name, service->name)) {
dbus_message_iter_init(message, &iter);
dbus_message_iter_recurse(&iter, &array);
while (dbus_message_iter_get_arg_type(&array) ==
DBUS_TYPE_STRUCT) {
dbus_message_iter_recurse(&array, &entry);
dbus_message_iter_get_basic(&entry, &path);
service->path = strip_service_path(path);
if (g_strcmp0(service->path, service_name) == 0)
return service->path;
dbus_message_iter_next(&array);
}
fprintf(stderr, "'%s' is not a valid service name or path.\n",
service_name);
fprintf(stderr, "Use the 'services' command to find available "
"services.\n");
return NULL;
} else
return service->path;
}
int set_proxy_manual(DBusConnection *connection, DBusMessage *message,
char *name, char **servers, char **excludes,
int num_servers, int num_excludes)
{
DBusMessage *message_send;
DBusMessageIter iter, value, dict, entry, data;
struct service_data service;
char *path;
const char *path_name;
char *property = "Proxy.Configuration";
char *method = "Method";
char *manual = "manual";
path_name = find_service(connection, message, name, &service);
if (path_name == NULL)
return -ENXIO;
path = g_strdup_printf("/net/connman/service/%s", path_name);
message_send = dbus_message_new_method_call("net.connman", path,
"net.connman.Service",
"SetProperty");
if (message_send == NULL) {
g_free(path);
return -ENOMEM;
}
dbus_message_iter_init_append(message_send, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
dbus_dict_open_variant(&iter, &value);
dbus_dict_open(&value, &dict);
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
&entry);
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &method);
dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING,
&data);
dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &manual);
dbus_message_iter_close_container(&entry, &data);
dbus_message_iter_close_container(&dict, &entry);
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
&entry);
append_property_array(&entry, "Servers", servers, num_servers);
dbus_message_iter_close_container(&dict, &entry);
if (num_excludes != 0) {
dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
NULL, &entry);
append_property_array(&entry, "Excludes", excludes,
num_excludes);
dbus_message_iter_close_container(&dict, &entry);
}
dbus_message_iter_close_container(&value, &dict);
dbus_message_iter_close_container(&iter, &value);
dbus_connection_send(connection, message_send, NULL);
dbus_connection_flush(connection);
dbus_message_unref(message_send);
g_free(path);
return 0;
}
int set_service_property(DBusConnection *connection, DBusMessage *message,
char *name, char *property, char **keys,
void *data, int num_args)
{
DBusMessage *message_send;
DBusMessageIter iter;
struct service_data service;
char *path;
const char *path_name;
path_name = find_service(connection, message, name, &service);
if (path_name == NULL)
return -ENXIO;
path = g_strdup_printf("/net/connman/service/%s", path_name);
message_send = dbus_message_new_method_call("net.connman", path,
"net.connman.Service",
"SetProperty");
if (message_send == NULL) {
g_free(path);
return -ENOMEM;
}
dbus_message_iter_init_append(message_send, &iter);
if (strcmp(property, "AutoConnect") == 0)
dbus_property_append_basic(&iter, (const char *) property,
DBUS_TYPE_BOOLEAN, data);
else if ((strcmp(property, "Domains.Configuration") == 0)
|| (strcmp(property, "Timeservers.Configuration") == 0)
|| (strcmp(property, "Nameservers.Configuration") == 0))
append_property_array(&iter, property, data, num_args);
else if ((strcmp(property, "IPv4.Configuration") == 0)
|| (strcmp(property, "IPv6.Configuration") == 0)
|| (strcmp(property, "Proxy.Configuration") == 0))
append_property_dict(&iter, property, keys, data, num_args);
dbus_connection_send(connection, message_send, NULL);
dbus_connection_flush(connection);
dbus_message_unref(message_send);
g_free(path);
return 0;
}
int remove_service(DBusConnection *connection, DBusMessage *message,
char *name)
{
struct service_data service;
DBusMessage *message_send;
const char *path_name;
char *path;
path_name = find_service(connection, message, name, &service);
if (path_name == NULL)
return -ENXIO;
if (service.favorite == FALSE)
return 0;
path = g_strdup_printf("/net/connman/service/%s", path_name);
message_send = dbus_message_new_method_call(CONNMAN_SERVICE, path,
CONNMAN_SERVICE_INTERFACE,
"Remove");
if (message_send == NULL) {
g_free(path);
return -ENOMEM;
}
dbus_connection_send(connection, message_send, NULL);
dbus_message_unref(message_send);
g_free(path);
return 0;
}