Imported Upstream version 1.24
[platform/upstream/connman.git] / client / commands.c
index 557a8ff..9c01fd5 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
+ *  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
@@ -29,6 +29,8 @@
 #include <string.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.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;
@@ -74,7 +83,7 @@ static bool check_dbus_name(const char *name)
         */
        unsigned int i;
 
-       if (name == NULL || name[0] == '\0')
+       if (!name || name[0] == '\0')
                return false;
 
        for (i = 0; name[i] != '\0'; i++)
@@ -89,7 +98,7 @@ static bool check_dbus_name(const char *name)
 
 static int parse_boolean(char *arg)
 {
-       if (arg == NULL)
+       if (!arg)
                return -1;
 
        if (strcasecmp(arg, "no") == 0 ||
@@ -117,10 +126,10 @@ static int parse_args(char *arg, struct connman_option *options)
 {
        int i;
 
-       if (arg == NULL)
+       if (!arg)
                return -1;
 
-       for (i = 0; options[i].name != NULL; i++) {
+       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))
@@ -137,14 +146,14 @@ static int enable_return(DBusMessageIter *iter, const char *error,
        char *str;
 
        str = strrchr(tech, '/');
-       if (str != NULL)
+       if (str)
                str++;
        else
                str = tech;
 
-       if (error == NULL) {
+       if (!error)
                fprintf(stdout, "Enabled %s\n", str);
-       else
+       else
                fprintf(stderr, "Error %s: %s\n", str, error);
 
        g_free(user_data);
@@ -166,7 +175,7 @@ static int cmd_enable(char *args[], int num, struct connman_option *options)
        if (check_dbus_name(args[1]) == false)
                return -EINVAL;
 
-       if (strcmp(args[1], "offlinemode") == 0) {
+       if (strcmp(args[1], "offline") == 0) {
                tech = g_strdup(args[1]);
                return __connmanctl_dbus_set_property(connection, "/",
                                "net.connman.Manager", enable_return, tech,
@@ -186,14 +195,14 @@ static int disable_return(DBusMessageIter *iter, const char *error,
        char *str;
 
        str = strrchr(tech, '/');
-       if (str != NULL)
+       if (str)
                str++;
        else
                str = tech;
 
-       if (error == NULL) {
+       if (!error)
                fprintf(stdout, "Disabled %s\n", str);
-       else
+       else
                fprintf(stderr, "Error %s: %s\n", str, error);
 
        g_free(user_data);
@@ -215,7 +224,7 @@ static int cmd_disable(char *args[], int num, struct connman_option *options)
        if (check_dbus_name(args[1]) == false)
                return -EINVAL;
 
-       if (strcmp(args[1], "offlinemode") == 0) {
+       if (strcmp(args[1], "offline") == 0) {
                tech = g_strdup(args[1]);
                return __connmanctl_dbus_set_property(connection, "/",
                                "net.connman.Manager", disable_return, tech,
@@ -233,7 +242,7 @@ static int state_print(DBusMessageIter *iter, const char *error,
 {
        DBusMessageIter entry;
 
-       if (error != NULL) {
+       if (error) {
                fprintf(stderr, "Error: %s", error);
                return 0;
        }
@@ -252,13 +261,13 @@ static int cmd_state(char *args[], int num, struct connman_option *options)
 
        return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                        CONNMAN_PATH, "net.connman.Manager", "GetProperties",
-                       state_print, NULL, DBUS_TYPE_INVALID);
+                       state_print, NULL, NULL, NULL);
 }
 
 static int services_list(DBusMessageIter *iter, const char *error,
                void *user_data)
 {
-       if (error == NULL) {
+       if (!error) {
                __connmanctl_services_list(iter);
                fprintf(stdout, "\n");
        } else {
@@ -268,14 +277,26 @@ static int services_list(DBusMessageIter *iter, const char *error,
        return 0;
 }
 
-static int services_properties(DBusMessageIter *iter, const char *error,
-               void *user_data)
+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 == NULL) {
+       if (!error) {
                fprintf(stdout, "%s\n", path);
 
                dbus_message_iter_recurse(iter, &dict);
@@ -285,7 +306,7 @@ static int services_properties(DBusMessageIter *iter, const char *error,
 
        } else {
                str = strrchr(path, '/');
-               if (str != NULL)
+               if (str)
                        str++;
                else
                        str = path;
@@ -323,11 +344,11 @@ static int cmd_services(char *args[], int num, struct connman_option *options)
                break;
        }
 
-       if (service_name == NULL) {
+       if (!service_name) {
                return __connmanctl_dbus_method_call(connection,
                                CONNMAN_SERVICE, CONNMAN_PATH,
                                "net.connman.Manager", "GetServices",
-                               services_list, NULL, DBUS_TYPE_INVALID);
+                               services_list, NULL, NULL, NULL);
        }
 
        if (check_dbus_name(service_name) == false)
@@ -336,7 +357,34 @@ static int cmd_services(char *args[], int num, struct connman_option *options)
        path = g_strdup_printf("/net/connman/service/%s", service_name);
        return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
                        "net.connman.Service", "GetProperties",
-                       services_properties, path, DBUS_TYPE_INVALID);
+                       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,
@@ -344,7 +392,7 @@ static int technology_print(DBusMessageIter *iter, const char *error,
 {
        DBusMessageIter array;
 
-       if (error != NULL) {
+       if (error) {
                fprintf(stderr, "Error: %s\n", error);
                return 0;
        }
@@ -378,7 +426,7 @@ static int cmd_technologies(char *args[], int num,
 
        return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
                        CONNMAN_PATH, "net.connman.Manager", "GetTechnologies",
-                       technology_print, NULL, DBUS_TYPE_INVALID);
+                       technology_print, NULL, NULL, NULL);
 }
 
 struct tether_enable {
@@ -393,19 +441,19 @@ static int tether_set_return(DBusMessageIter *iter, const char *error,
        char *str;
 
        str = strrchr(tether->path, '/');
-       if (str != NULL)
+       if (str)
                str++;
        else
                str = tether->path;
 
-       if (error == NULL) {
+       if (!error) {
                fprintf(stdout, "%s tethering for %s\n",
-                               tether->enable == TRUE ? "Enabled": "Disabled",
+                               tether->enable ? "Enabled" : "Disabled",
                                str);
        } else
                fprintf(stderr, "Error %s %s tethering: %s\n",
-                               tether->enable == TRUE ?
-                               "enabling": "disabling", str, error);
+                               tether->enable ?
+                               "enabling" : "disabling", str, error);
 
        g_free(tether->path);
        g_free(user_data);
@@ -446,9 +494,6 @@ struct tether_properties {
 
 static int tether_update(struct tether_properties *tether)
 {
-       printf("%d %d %d\n", tether->ssid_result, tether->passphrase_result,
-               tether->set_tethering);
-
        if (tether->ssid_result == 0 && tether->passphrase_result == 0)
                return tether_set("wifi", tether->set_tethering);
 
@@ -466,7 +511,7 @@ static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
 {
        struct tether_properties *tether = user_data;
 
-       if (error == NULL) {
+       if (!error) {
                fprintf(stdout, "Wifi SSID set\n");
                tether->ssid_result = 0;
        } else {
@@ -482,7 +527,7 @@ static int tether_set_passphrase_return(DBusMessageIter *iter,
 {
        struct tether_properties *tether = user_data;
 
-       if (error == NULL) {
+       if (!error) {
                fprintf(stdout, "Wifi passphrase set\n");
                tether->passphrase_result = 0;
        } else {
@@ -565,7 +610,7 @@ static int scan_return(DBusMessageIter *iter, const char *error,
 {
        char *path = user_data;
 
-       if (error == NULL) {
+       if (!error) {
                char *str = strrchr(path, '/');
                str++;
                fprintf(stdout, "Scan completed for %s\n", str);
@@ -593,7 +638,7 @@ static int cmd_scan(char *args[], int num, struct connman_option *options)
        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, DBUS_TYPE_INVALID);
+                       scan_return, path, NULL, NULL);
 }
 
 static int connect_return(DBusMessageIter *iter, const char *error,
@@ -601,7 +646,7 @@ static int connect_return(DBusMessageIter *iter, const char *error,
 {
        char *path = user_data;
 
-       if (error == NULL) {
+       if (!error) {
                char *str = strrchr(path, '/');
                str++;
                fprintf(stdout, "Connected %s\n", str);
@@ -629,7 +674,7 @@ static int cmd_connect(char *args[], int num, struct connman_option *options)
        path = g_strdup_printf("/net/connman/service/%s", args[1]);
        return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
                        "net.connman.Service", "Connect",
-                       connect_return, path, DBUS_TYPE_INVALID);
+                       connect_return, path, NULL, NULL);
 }
 
 static int disconnect_return(DBusMessageIter *iter, const char *error,
@@ -637,7 +682,7 @@ static int disconnect_return(DBusMessageIter *iter, const char *error,
 {
        char *path = user_data;
 
-       if (error == NULL) {
+       if (!error) {
                char *str = strrchr(path, '/');
                str++;
                fprintf(stdout, "Disconnected %s\n", str);
@@ -665,7 +710,7 @@ static int cmd_disconnect(char *args[], int num, struct connman_option *options)
        path = g_strdup_printf("/net/connman/service/%s", args[1]);
        return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE, path,
                        "net.connman.Service", "Disconnect",
-                       disconnect_return, path, DBUS_TYPE_INVALID);
+                       disconnect_return, path, NULL, NULL);
 }
 
 static int config_return(DBusMessageIter *iter, const char *error,
@@ -673,7 +718,7 @@ static int config_return(DBusMessageIter *iter, const char *error,
 {
        char *service_name = user_data;
 
-       if (error != NULL)
+       if (error)
                fprintf(stderr, "Error %s: %s\n", service_name, error);
 
        g_free(user_data);
@@ -693,10 +738,10 @@ static void config_append_ipv4(DBusMessageIter *iter,
        char **opts = append->opts;
        int i = 0;
 
-       if (opts == NULL)
+       if (!opts)
                return;
 
-       while (opts[i] != NULL && ipv4[i] != NULL) {
+       while (opts[i] && ipv4[i]) {
                __connmanctl_dbus_append_dict_entry(iter, ipv4[i],
                                DBUS_TYPE_STRING, &opts[i]);
                i++;
@@ -710,7 +755,7 @@ static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
        struct config_append *append = user_data;
        char **opts = append->opts;
 
-       if (opts == NULL)
+       if (!opts)
                return;
 
        append->values = 1;
@@ -736,7 +781,7 @@ static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
                        break;
 
                default:
-                       if (opts[1] != NULL) {
+                       if (opts[1]) {
                                append->values = 2;
 
                                if (g_strcmp0(opts[1], "prefered") != 0 &&
@@ -758,7 +803,7 @@ static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
        } else if (g_strcmp0(opts[0], "manual") == 0) {
                int i = 1;
 
-               while (opts[i] != NULL && ipv6[i] != NULL) {
+               while (opts[i] && ipv6[i]) {
                        if (i == 2) {
                                int value = atoi(opts[i]);
                                __connmanctl_dbus_append_dict_entry(iter,
@@ -775,7 +820,7 @@ static void config_append_ipv6(DBusMessageIter *iter, void *user_data)
                append->values = i;
 
        } else if (g_strcmp0(opts[0], "off") != 0) {
-               fprintf(stderr, "Error %s: %s\n", opts[0], strerror(-EINVAL));
+               fprintf(stderr, "Error %s: %s\n", opts[0], strerror(EINVAL));
 
                return;
        }
@@ -790,10 +835,10 @@ static void config_append_str(DBusMessageIter *iter, void *user_data)
        char **opts = append->opts;
        int i = 0;
 
-       if (opts == NULL)
+       if (!opts)
                return;
 
-       while (opts[i] != NULL) {
+       while (opts[i]) {
                dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
                                &opts[i]);
                i++;
@@ -808,10 +853,10 @@ static void append_servers(DBusMessageIter *iter, void *user_data)
        char **opts = append->opts;
        int i = 1;
 
-       if (opts == NULL)
+       if (!opts)
                return;
 
-       while (opts[i] != NULL && g_strcmp0(opts[i], "--excludes") != 0) {
+       while (opts[i] && g_strcmp0(opts[i], "--excludes") != 0) {
                dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
                                &opts[i]);
                i++;
@@ -826,12 +871,12 @@ static void append_excludes(DBusMessageIter *iter, void *user_data)
        char **opts = append->opts;
        int i = append->values;
 
-       if (opts == NULL || opts[i] == NULL ||
+       if (!opts || !opts[i] ||
                        g_strcmp0(opts[i], "--excludes") != 0)
                return;
 
        i++;
-       while (opts[i] != NULL) {
+       while (opts[i]) {
                dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
                                &opts[i]);
                i++;
@@ -845,7 +890,7 @@ static void config_append_proxy(DBusMessageIter *iter, void *user_data)
        struct config_append *append = user_data;
        char **opts = append->opts;
 
-       if (opts == NULL)
+       if (!opts)
                return;
 
        if (g_strcmp0(opts[0], "manual") == 0) {
@@ -856,7 +901,7 @@ static void config_append_proxy(DBusMessageIter *iter, void *user_data)
                                append_excludes, append);
 
        } else if (g_strcmp0(opts[0], "auto") == 0) {
-               if (opts[1] != NULL) {
+               if (opts[1]) {
                        __connmanctl_dbus_append_dict_entry(iter, "URL",
                                        DBUS_TYPE_STRING, &opts[1]);
                        append->values++;
@@ -881,13 +926,13 @@ static int cmd_config(char *args[], int num, struct connman_option *options)
        struct config_append append;
 
        service_name = args[1];
-       if (service_name == NULL)
+       if (!service_name)
                return -EINVAL;
 
        if (check_dbus_name(service_name) == false)
                return -EINVAL;
 
-       while (index < num && args[index] != NULL) {
+       while (index < num && args[index]) {
                c = parse_args(args[index], options);
                opt_start = &args[index + 1];
                append.opts = opt_start;
@@ -985,7 +1030,7 @@ static int cmd_config(char *args[], int num, struct connman_option *options)
                                        CONNMAN_SERVICE, path,
                                        "net.connman.Service", "Remove",
                                        config_return, g_strdup(service_name),
-                                       DBUS_TYPE_INVALID);
+                                       NULL, NULL);
                        break;
                default:
                        res = -EINVAL;
@@ -1016,25 +1061,30 @@ static DBusHandlerResult monitor_changed(DBusConnection *connection,
        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 (strncmp(interface, "net.connman.Agent", 17) == 0 ||
-                       strncmp(interface, "net.connman.vpn.Agent", 21) == 0)
+       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 != NULL && *interface != '\0')
+       if (interface && *interface != '\0')
                interface++;
 
        path = strrchr(dbus_message_get_path(message), '/');
-       if (path != NULL && *path != '\0')
+       if (path && *path != '\0')
                path++;
 
        __connmanctl_save_rl();
 
        if (dbus_message_is_signal(message, "net.connman.Manager",
-                                       "ServicesChanged") == TRUE) {
+                                       "ServicesChanged")) {
 
                fprintf(stdout, "%-12s %-20s = {\n", interface,
                                "ServicesChanged");
@@ -1044,22 +1094,30 @@ static DBusHandlerResult monitor_changed(DBusConnection *connection,
 
                __connmanctl_redraw_rl();
 
-               return DBUS_HANDLER_RESULT_HANDLED;
-       }
+               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();
 
-       if (dbus_message_is_signal(message, "net.connman.vpn.Manager",
-                                       "ConnectionAdded") == TRUE ||
+               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") == TRUE) {
+                                       "ConnectionRemoved")) {
                interface = "vpn.Manager";
                path = dbus_message_get_member(message);
 
        } else if (dbus_message_is_signal(message, "net.connman.Manager",
-                                       "TechnologyAdded") == TRUE ||
+                                       "TechnologyAdded") ||
                        dbus_message_is_signal(message, "net.connman.Manager",
-                                       "TechnologyRemoved") == TRUE)
+                                       "TechnologyRemoved"))
                path = dbus_message_get_member(message);
 
        fprintf(stdout, "%-12s %-20s ", interface, path);
@@ -1070,7 +1128,7 @@ static DBusHandlerResult monitor_changed(DBusConnection *connection,
 
        __connmanctl_redraw_rl();
 
-       return DBUS_HANDLER_RESULT_HANDLED;
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 static struct {
@@ -1092,7 +1150,7 @@ static void monitor_add(char *interface)
        char *rule;
        DBusError err;
 
-       for (i = 0; monitor[i].interface != NULL; i++) {
+       for (i = 0; monitor[i].interface; i++) {
                if (monitor[i].enabled == true)
                        add_filter = false;
 
@@ -1129,7 +1187,7 @@ static void monitor_del(char *interface)
        char *rule;
 
 
-       for (i = 0; monitor[i].interface != NULL; i++) {
+       for (i = 0; monitor[i].interface; i++) {
                if (g_strcmp0(interface, monitor[i].interface) == 0) {
                        if (monitor[i].enabled == false)
                                return;
@@ -1250,6 +1308,12 @@ static int cmd_monitor(char *args[], int num, struct connman_option *options)
 
 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;
 
@@ -1282,7 +1346,7 @@ static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
        char *str;
        DBusMessageIter dict;
 
-       if (error == NULL) {
+       if (!error) {
                fprintf(stdout, "%s\n", path);
 
                dbus_message_iter_recurse(iter, &dict);
@@ -1292,7 +1356,7 @@ static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
 
        } else {
                str = strrchr(path, '/');
-               if (str != NULL)
+               if (str)
                        str++;
                else
                        str = path;
@@ -1308,7 +1372,7 @@ static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
 static int vpnconnections_list(DBusMessageIter *iter, const char *error,
                void *user_data)
 {
-       if (error == NULL)
+       if (!error)
                __connmanctl_vpnconnections_list(iter);
         else
                fprintf(stderr, "Error: %s\n", error);
@@ -1326,12 +1390,12 @@ static int cmd_vpnconnections(char *args[], int num,
 
        vpnconnection_name = args[1];
 
-       if (vpnconnection_name == NULL)
+       if (!vpnconnection_name)
                return __connmanctl_dbus_method_call(connection,
                                VPN_SERVICE, VPN_PATH,
                                "net.connman.vpn.Manager", "GetConnections",
                                vpnconnections_list, NULL,
-                               DBUS_TYPE_INVALID);
+                               NULL, NULL);
 
        if (check_dbus_name(vpnconnection_name) == false)
                return -EINVAL;
@@ -1340,12 +1404,18 @@ static int cmd_vpnconnections(char *args[], int num,
                        vpnconnection_name);
        return __connmanctl_dbus_method_call(connection, VPN_SERVICE, path,
                        "net.connman.vpn.Connection", "GetProperties",
-                       vpnconnections_properties, path, DBUS_TYPE_INVALID);
+                       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;
 
@@ -1372,165 +1442,1112 @@ static int cmd_vpnagent(char *args[], int num, struct connman_option *options)
        return 0;
 }
 
-static int cmd_exit(char *args[], int num, struct connman_option *options)
+static DBusMessage *session_release(DBusConnection *connection,
+               DBusMessage *message, void *user_data)
 {
-       return 1;
-}
+       __connmanctl_save_rl();
 
-static struct connman_option service_options[] = {
-       {"properties", 'p', "[<service>]      (obsolete)"},
-       { NULL, }
-};
+       fprintf(stdout, "Session %s released\n", session_path);
 
-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|prefered]|\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"},
-       { NULL, }
-};
+       __connmanctl_redraw_rl();
 
-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, }
-};
+       g_free(session_path);
+       session_path = NULL;
+       session_connected = false;
 
-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;
-} cmd_table[] = {
-       { "state",        NULL,           NULL,            cmd_state,
-         "Shows if the system is online or offline" },
-       { "technologies", NULL,           NULL,            cmd_technologies,
-         "Display technologies" },
-       { "enable",       "<technology>|offline", NULL,    cmd_enable,
-         "Enables given technology or offline mode" },
-       { "disable",      "<technology>|offline", NULL,    cmd_disable,
-         "Disables given technology or offline mode"},
-       { "tether", "<technology> on|off\n"
-                   "            wifi [on|off] <ssid> <passphrase> ",
-                                         NULL,            cmd_tether,
-         "Enable, disable tethering, set SSID and passphrase for wifi" },
-       { "services",     "[<service>]",  service_options, cmd_services,
-         "Display services" },
-       { "scan",         "<technology>", NULL,            cmd_scan,
-         "Scans for new services for given technology" },
-       { "connect",      "<service>",    NULL,            cmd_connect,
-         "Connect a given service" },
-       { "disconnect",   "<service>",    NULL,            cmd_disconnect,
-         "Disconnect a given service" },
-       { "config",       "<service>",    config_options,  cmd_config,
-         "Set service configuration options" },
-       { "monitor",      "[off]",        monitor_options, cmd_monitor,
-         "Monitor signals from interfaces" },
-       { "agent", "on|off",              NULL,            cmd_agent,
-         "Agent mode" },
-       {"vpnconnections", "[<connection>]", NULL,         cmd_vpnconnections,
-        "Display VPN connections" },
-       { "vpnagent",     "on|off",     NULL,            cmd_vpnagent,
-         "VPN Agent mode" },
-       { "help",         NULL,           NULL,            cmd_help,
-         "Show help" },
-       { "exit",         NULL,           NULL,            cmd_exit,
-         "Exit" },
-       { "quit",         NULL,           NULL,            cmd_exit,
-         "Quit" },
-       {  NULL, },
-};
+       return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
+}
 
-static int cmd_help(char *args[], int num, struct connman_option *options)
+static DBusMessage *session_update(DBusConnection *connection,
+               DBusMessage *message, void *user_data)
 {
-       bool interactive = __connmanctl_is_interactive();
-       int i, j;
+       DBusMessageIter iter, dict;
 
-       if (interactive == false)
-               fprintf(stdout, "Usage: connmanctl [[command] [args]]\n");
+       __connmanctl_save_rl();
 
-       for (i = 0; cmd_table[i].cmd != NULL; i++) {
-               const char *cmd = cmd_table[i].cmd;
-               const char *argument = cmd_table[i].argument;
-               const char *desc = cmd_table[i].desc;
+       fprintf(stdout, "Session      Update               = {\n");
 
-               printf("%-16s%-22s%s\n", cmd != NULL? cmd: "",
-                               argument != NULL? argument: "",
-                               desc != NULL? desc: "");
+       dbus_message_iter_init(message, &iter);
+       dbus_message_iter_recurse(&iter, &dict);
 
-               if (cmd_table[i].options != NULL) {
-                       for (j = 0; cmd_table[i].options[j].name != NULL;
-                            j++) {
-                               const char *options_desc =
-                                       cmd_table[i].options[j].desc != NULL ?
-                                       cmd_table[i].options[j].desc: "";
+       __connmanctl_dbus_print(&dict, "", " = ", "\n");
+       fprintf(stdout, "\n}\n");
 
-                               printf("   --%-16s%s\n",
-                                               cmd_table[i].options[j].name,
-                                               options_desc);
+       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);
        }
 
-       if (interactive == false)
-               fprintf(stdout, "\nNote: arguments and output are considered "
-                               "EXPERIMENTAL for now.\n");
+       __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;
 }
 
-int __connmanctl_commands(DBusConnection *dbus_conn, char *argv[], int argc)
+static void session_notify_remove(void)
 {
-       int i, result;
+       if (!session_notify_path)
+               return;
 
-       connection = dbus_conn;
+       g_dbus_unregister_interface(connection, session_notify_path,
+                       "net.connman.Notification");
 
-       for (i = 0; cmd_table[i].cmd != NULL; i++) {
-               if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 &&
-                               cmd_table[i].func != NULL) {
-                       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;
-               }
+       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;
        }
 
-       fprintf(stderr, "Error '%s': Unknown command\n", argv[0]);
-       return -EINVAL;
+       return -EINPROGRESS;
 }
 
-char *__connmanctl_lookup_command(const char *text, int state)
+
+static int session_connect(void)
 {
-       static int i = 0;
-       static int len = 0;
+       return __connmanctl_dbus_method_call(connection, "net.connman",
+                       session_path, "net.connman.Session", "Connect",
+                       session_connect_cb, NULL, NULL, NULL);
+}
 
-       if (state == 0) {
-               i = 0;
-               len = strlen(text);
+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;
        }
 
-       while (cmd_table[i].cmd != NULL) {
-               const char *command = cmd_table[i].cmd;
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH) {
+               fprintf(stderr, "Error creating session: No session path\n");
+               return -EINVAL;
+       }
 
-               i++;
+       g_free(session_path);
 
-               if (strncmp(text, command, len) == 0)
-                       return strdup(command);
+       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;
        }
 
-       return NULL;
+       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"},
+       { 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 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 },
+       { "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>",    NULL,            cmd_connect,
+         "Connect a given service", lookup_service_arg },
+       { "disconnect",   "<service>",    NULL,            cmd_disconnect,
+         "Disconnect a given service", 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 },
+       { "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)
+{
+       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)
+{
+       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)
+{
+       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);
 }