Imported Upstream version 1.38
[platform/upstream/connman.git] / client / commands.c
index 9c01fd5..94c375d 100644 (file)
@@ -31,6 +31,7 @@
 #include <stdbool.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include <glib.h>
 #include <gdbus.h>
@@ -38,6 +39,7 @@
 #include "dbus_helpers.h"
 #include "input.h"
 #include "services.h"
+#include "tethering.h"
 #include "peers.h"
 #include "commands.h"
 #include "agent.h"
@@ -45,6 +47,7 @@
 
 static DBusConnection *connection;
 static GHashTable *service_hash;
+static GHashTable *vpnconnection_hash;
 static GHashTable *peer_hash;
 static GHashTable *technology_hash;
 static char *session_notify_path;
@@ -139,7 +142,7 @@ static int parse_args(char *arg, struct connman_option *options)
        return '?';
 }
 
-static int enable_return(DBusMessageIter *iter, const char *error,
+static int enable_return(DBusMessageIter *iter, int errnum, const char *error,
                void *user_data)
 {
        char *tech = user_data;
@@ -151,10 +154,19 @@ static int enable_return(DBusMessageIter *iter, const char *error,
        else
                str = tech;
 
-       if (!error)
+       switch (errnum) {
+       case 0:
                fprintf(stdout, "Enabled %s\n", str);
-       else
+               break;
+       case -ENODEV:
+               fprintf(stderr, "%s is not available\n", str);
+               break;
+       case -EALREADY:
+               fprintf(stderr, "%s is already enabled\n", str);
+               break;
+       default:
                fprintf(stderr, "Error %s: %s\n", str, error);
+       }
 
        g_free(user_data);
 
@@ -188,7 +200,7 @@ static int cmd_enable(char *args[], int num, struct connman_option *options)
                                "Powered", DBUS_TYPE_BOOLEAN, &b);
 }
 
-static int disable_return(DBusMessageIter *iter, const char *error,
+static int disable_return(DBusMessageIter *iter, int errnum, const char *error,
                void *user_data)
 {
        char *tech = user_data;
@@ -200,10 +212,19 @@ static int disable_return(DBusMessageIter *iter, const char *error,
        else
                str = tech;
 
-       if (!error)
-               fprintf(stdout, "Disabled %s\n", str);
-       else
+       switch (errnum) {
+       case 0:
+               fprintf(stdout, "Disable %s\n", str);
+               break;
+       case -ENODEV:
+               fprintf(stderr, "%s is not available\n", str);
+               break;
+       case -EALREADY:
+               fprintf(stderr, "%s is already disabled\n", str);
+               break;
+       default:
                fprintf(stderr, "Error %s: %s\n", str, error);
+       }
 
        g_free(user_data);
 
@@ -237,13 +258,13 @@ static int cmd_disable(char *args[], int num, struct connman_option *options)
                                "Powered", DBUS_TYPE_BOOLEAN, &b);
 }
 
-static int state_print(DBusMessageIter *iter, const char *error,
+static int state_print(DBusMessageIter *iter, int errnum, const char *error,
                void *user_data)
 {
        DBusMessageIter entry;
 
        if (error) {
-               fprintf(stderr, "Error: %s", error);
+               fprintf(stderr, "Error: %s\n", error);
                return 0;
        }
 
@@ -264,7 +285,34 @@ static int cmd_state(char *args[], int num, struct connman_option *options)
                        state_print, NULL, NULL, NULL);
 }
 
-static int services_list(DBusMessageIter *iter, const char *error,
+static int clock_print(DBusMessageIter *iter, int errnum, const char *error,
+               void *user_data)
+{
+       DBusMessageIter entry;
+
+       if (error) {
+               fprintf(stderr, "Error: %s\n", error);
+               return 0;
+       }
+
+       dbus_message_iter_recurse(iter, &entry);
+       __connmanctl_dbus_print(&entry, "  ", " = ", "\n");
+       fprintf(stdout, "\n");
+
+       return 0;
+}
+
+static int cmd_clock(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.Clock", "GetProperties",
+                       clock_print, NULL, NULL, NULL);
+}
+
+static int services_list(DBusMessageIter *iter, int errnum, const char *error,
                void *user_data)
 {
        if (!error) {
@@ -277,7 +325,7 @@ static int services_list(DBusMessageIter *iter, const char *error,
        return 0;
 }
 
-static int peers_list(DBusMessageIter *iter,
+static int peers_list(DBusMessageIter *iter, int errnum,
                                        const char *error, void *user_data)
 {
        if (!error) {
@@ -289,7 +337,19 @@ static int peers_list(DBusMessageIter *iter,
        return 0;
 }
 
-static int object_properties(DBusMessageIter *iter,
+static int tethering_clients_list(DBusMessageIter *iter, int errnum,
+                                       const char *error, void *user_data)
+{
+       if (!error) {
+               __connmanctl_tethering_clients_list(iter);
+               fprintf(stdout, "\n");
+       } else
+               fprintf(stderr, "Error: %s\n", error);
+
+       return 0;
+}
+
+static int object_properties(DBusMessageIter *iter, int errnum,
                                        const char *error, void *user_data)
 {
        char *path = user_data;
@@ -387,7 +447,7 @@ static int cmd_peers(char *args[], int num, struct connman_option *options)
                                object_properties, path, NULL, NULL);
 }
 
-static int technology_print(DBusMessageIter *iter, const char *error,
+static int technology_print(DBusMessageIter *iter, int errnum, const char *error,
                void *user_data)
 {
        DBusMessageIter array;
@@ -434,8 +494,8 @@ struct tether_enable {
        dbus_bool_t enable;
 };
 
-static int tether_set_return(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int tether_set_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        struct tether_enable *tether = user_data;
        char *str;
@@ -494,8 +554,13 @@ struct tether_properties {
 
 static int tether_update(struct tether_properties *tether)
 {
-       if (tether->ssid_result == 0 && tether->passphrase_result == 0)
-               return tether_set("wifi", tether->set_tethering);
+       int ret;
+
+       if (tether->ssid_result == 0 && tether->passphrase_result == 0) {
+               ret = tether_set("wifi", tether->set_tethering);
+               g_free(tether);
+               return ret;
+       }
 
        if (tether->ssid_result != -EINPROGRESS &&
                        tether->passphrase_result != -EINPROGRESS) {
@@ -506,8 +571,8 @@ static int tether_update(struct tether_properties *tether)
        return -EINPROGRESS;
 }
 
-static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int tether_set_ssid_return(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
 {
        struct tether_properties *tether = user_data;
 
@@ -522,8 +587,8 @@ static int tether_set_ssid_return(DBusMessageIter *iter, const char *error,
        return tether_update(tether);
 }
 
-static int tether_set_passphrase_return(DBusMessageIter *iter,
-               const char *error, void *user_data)
+static int tether_set_passphrase_return(DBusMessageIter *iter, int errnum,
+                                       const char *error, void *user_data)
 {
        struct tether_properties *tether = user_data;
 
@@ -605,7 +670,18 @@ static int cmd_tether(char *args[], int num, struct connman_option *options)
        return tether_set(args[1], set_tethering);
 }
 
-static int scan_return(DBusMessageIter *iter, const char *error,
+static int cmd_tethering_clients(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", "GetTetheringClients",
+                               tethering_clients_list, NULL, NULL, NULL);
+}
+
+static int scan_return(DBusMessageIter *iter, int ernnum, const char *error,
                void *user_data)
 {
        char *path = user_data;
@@ -641,8 +717,8 @@ static int cmd_scan(char *args[], int num, struct connman_option *options)
                        scan_return, path, NULL, NULL);
 }
 
-static int connect_return(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int connect_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        char *path = user_data;
 
@@ -660,6 +736,7 @@ static int connect_return(DBusMessageIter *iter, const char *error,
 
 static int cmd_connect(char *args[], int num, struct connman_option *options)
 {
+       const char *iface = "net.connman.Service";
        char *path;
 
        if (num > 2)
@@ -671,14 +748,18 @@ static int cmd_connect(char *args[], int num, struct connman_option *options)
        if (check_dbus_name(args[1]) == false)
                return -EINVAL;
 
-       path = g_strdup_printf("/net/connman/service/%s", args[1]);
+       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,
-                       "net.connman.Service", "Connect",
-                       connect_return, path, NULL, NULL);
+                       iface, "Connect", connect_return, path, NULL, NULL);
 }
 
-static int disconnect_return(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int disconnect_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        char *path = user_data;
 
@@ -696,6 +777,7 @@ static int disconnect_return(DBusMessageIter *iter, const char *error,
 
 static int cmd_disconnect(char *args[], int num, struct connman_option *options)
 {
+       const char *iface = "net.connman.Service";
        char *path;
 
        if (num > 2)
@@ -707,14 +789,140 @@ static int cmd_disconnect(char *args[], int num, struct connman_option *options)
        if (check_dbus_name(args[1]) == false)
                return -EINVAL;
 
-       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, NULL, NULL);
+       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)
+struct move_service {
+       char *service;
+       char *target;
+};
+
+static int move_before_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
+{
+       struct move_service *services = user_data;
+       char *service;
+       char *target;
+
+       if (!error) {
+               service = strrchr(services->service, '/');
+               service++;
+               target = strrchr(services->target, '/');
+               target++;
+               fprintf(stdout, "Moved %s before %s\n", service, target);
+       } else
+               fprintf(stderr, "Error %s: %s\n", services->service, error);
+
+       g_free(services->service);
+       g_free(services->target);
+       g_free(user_data);
+
+       return 0;
+}
+
+static void move_before_append_args(DBusMessageIter *iter, void *user_data)
+{
+       char *path = user_data;
+
+       dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static int cmd_service_move_before(char *args[], int num,
+               struct connman_option *options)
+{
+       const char *iface = "net.connman.Service";
+       struct move_service *services;
+
+       if (num > 3)
+               return -E2BIG;
+
+       if (num < 3)
+               return -EINVAL;
+
+       if (check_dbus_name(args[1]) == false)
+               return -EINVAL;
+
+       services = g_new(struct move_service, 1);
+
+       services->service = g_strdup_printf("/net/connman/service/%s", args[1]);
+       services->target = g_strdup_printf("/net/connman/service/%s", args[2]);
+
+       return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                       services->service, iface, "MoveBefore",
+                                       move_before_return, services,
+                                       move_before_append_args,
+                                       services->target);
+}
+
+static int move_after_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
+{
+       struct move_service *services = user_data;
+       char *service;
+       char *target;
+
+       if (!error) {
+               service = strrchr(services->service, '/');
+               service++;
+               target = strrchr(services->target, '/');
+               target++;
+               fprintf(stdout, "Moved %s after %s\n", service, target);
+       } else
+               fprintf(stderr, "Error %s: %s\n", services->service, error);
+
+       g_free(services->service);
+       g_free(services->target);
+       g_free(user_data);
+
+       return 0;
+}
+
+static void move_after_append_args(DBusMessageIter *iter, void *user_data)
+{
+       char *path = user_data;
+
+       dbus_message_iter_append_basic(iter,
+                               DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static int cmd_service_move_after(char *args[], int num,
+               struct connman_option *options)
+{
+       const char *iface = "net.connman.Service";
+       struct move_service *services;
+
+       if (num > 3)
+               return -E2BIG;
+
+       if (num < 3)
+               return -EINVAL;
+
+       if (check_dbus_name(args[1]) == false)
+               return -EINVAL;
+
+       services = g_new(struct move_service, 1);
+
+       services->service = g_strdup_printf("/net/connman/service/%s", args[1]);
+       services->target = g_strdup_printf("/net/connman/service/%s", args[2]);
+
+       return __connmanctl_dbus_method_call(connection, CONNMAN_SERVICE,
+                                       services->service, iface, "MoveAfter",
+                                       move_after_return, services,
+                                       move_after_append_args,
+                                       services->target);
+}
+
+static int config_return(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        char *service_name = user_data;
 
@@ -731,6 +939,13 @@ struct config_append {
        int values;
 };
 
+struct session_options {
+       char **args;
+       int num;
+       char *notify_path;
+       struct connman_option *options;
+};
+
 static void config_append_ipv4(DBusMessageIter *iter,
                void *user_data)
 {
@@ -1032,6 +1247,30 @@ static int cmd_config(char *args[], int num, struct connman_option *options)
                                        config_return, g_strdup(service_name),
                                        NULL, NULL);
                        break;
+
+               case 'm':
+                       switch (parse_boolean(*opt_start)) {
+                       case 1:
+                               val = TRUE;
+                               break;
+                       case 0:
+                               val = FALSE;
+                               break;
+                       default:
+                               res = -EINVAL;
+                               break;
+                       }
+                       if (res == 0) {
+                               res = __connmanctl_dbus_set_property(connection,
+                                               path, "net.connman.Service",
+                                               config_return,
+                                               g_strdup(service_name),
+                                               "mDNS.Configuration",
+                                               DBUS_TYPE_BOOLEAN, &val);
+                       }
+                       index++;
+                       break;
+
                default:
                        res = -EINVAL;
                        break;
@@ -1339,8 +1578,8 @@ static int cmd_agent(char *args[], int num, struct connman_option *options)
        return 0;
 }
 
-static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int vpnconnections_properties(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
 {
        char *path = user_data;
        char *str;
@@ -1369,8 +1608,8 @@ static int vpnconnections_properties(DBusMessageIter *iter, const char *error,
        return 0;
 }
 
-static int vpnconnections_list(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int vpnconnections_list(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        if (!error)
                __connmanctl_vpnconnections_list(iter);
@@ -1561,11 +1800,11 @@ static void session_notify_remove(void)
        session_notify_path = NULL;
 }
 
-static int session_connect_cb(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int session_connect_cb(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        if (error) {
-               fprintf(stderr, "Error: %s", error);
+               fprintf(stderr, "Error: %s\n", error);
                return 0;
        }
 
@@ -1580,11 +1819,11 @@ static int session_connect(void)
                        session_connect_cb, NULL, NULL, NULL);
 }
 
-static int session_disconnect_cb(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int session_disconnect_cb(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
 {
        if (error)
-               fprintf(stderr, "Error: %s", error);
+               fprintf(stderr, "Error: %s\n", error);
 
        return 0;
 }
@@ -1596,8 +1835,8 @@ static int session_disconnect(void)
                        session_disconnect_cb, NULL, NULL, NULL);
 }
 
-static int session_create_cb(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int session_create_cb(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        gboolean connect = GPOINTER_TO_INT(user_data);
        char *str;
@@ -1626,28 +1865,140 @@ static int session_create_cb(DBusMessageIter *iter, const char *error,
        return -EINPROGRESS;
 }
 
+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 void session_create_append_dict(DBusMessageIter *iter, void *user_data)
+{
+       struct session_options *args_struct = user_data;
+       int index = 0, res = 0;
+       struct config_append append;
+       char c;
+       char *ifname;
+       dbus_bool_t source_ip_rule;
+
+       while (index < args_struct->num && args_struct->args[index]) {
+               append.opts = &args_struct->args[index];
+               append.values = 0;
+
+               c = parse_args(args_struct->args[index], args_struct->options);
+
+               switch (c) {
+               case 'b':
+                       __connmanctl_dbus_append_dict_string_array(iter, "AllowedBearers",
+                                                                  session_config_append_array,
+                                                                  &append);
+                       break;
+               case 't':
+                       if (! args_struct->args[index + 1]) {
+                               res = -EINVAL;
+                               break;
+                       }
+                       __connmanctl_dbus_append_dict_entry(iter, "ConnectionType",
+                                                           DBUS_TYPE_STRING,
+                                                           &args_struct->args[index + 1]);
+                       append.values = 2;
+                       break;
+               case 'i':
+                       if (index + 1 <  args_struct->num)
+                               ifname =  args_struct->args[index + 1];
+                       else
+                               ifname = "";
+                        __connmanctl_dbus_append_dict_entry(iter, "AllowedInterface",
+                                                            DBUS_TYPE_STRING,
+                                                            &ifname);
+                       append.values = 2;
+                       break;
+               case 's':
+                       if (! args_struct->args[index + 1]) {
+                               res = -EINVAL;
+                               break;
+                       }
+                       switch (parse_boolean( args_struct->args[index + 1])) {
+                       case 1:
+                               source_ip_rule = TRUE;
+                               break;
+                       case 0:
+                               source_ip_rule = FALSE;
+                               break;
+                       default:
+                               res = -EINVAL;
+                               break;
+                       }
+                       __connmanctl_dbus_append_dict_entry(iter, "SourceIPRule",
+                                                           DBUS_TYPE_BOOLEAN,
+                                                           &source_ip_rule);
+                       append.values = 2;
+                       break;
+               case 'c':
+                       if (!args_struct->args[index + 1]) {
+                               res = -EINVAL;
+                               break;
+                       }
+                       __connmanctl_dbus_append_dict_entry(iter, "ContextIdentifier",
+                                                           DBUS_TYPE_STRING,
+                                                           &args_struct->args[index + 1]);
+                       append.values = 2;
+                       break;
+               default:
+                       res = -EINVAL;
+               }
+
+               if (res < 0 && res != -EINPROGRESS) {
+                       printf("Error '%s': %s\n",  args_struct->args[index],
+                                       strerror(-res));
+                       return;
+               }
+
+               index += append.values;
+       }
+}
+
 static void session_create_append(DBusMessageIter *iter, void *user_data)
 {
-       const char *notify_path = user_data;
+       struct session_options *args_struct = user_data;
 
-       __connmanctl_dbus_append_dict(iter, NULL, NULL);
+       __connmanctl_dbus_append_dict(iter, session_create_append_dict,
+                                     args_struct);
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
-                       &notify_path);
+                                      &args_struct->notify_path);
 }
 
-static int session_create(gboolean connect)
+static int session_create(gboolean connect, char *args[], int num,
+                         struct connman_option *options)
 {
        int res;
        char *notify_path;
+       struct session_options args_struct;
+       args_struct.args = args;
+       args_struct.num = num;
+       args_struct.options = options;
 
        notify_path = g_strdup_printf("/net/connman/connmanctl%d", getpid());
        session_notify_add(notify_path);
+       args_struct.notify_path = 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);
+                       session_create_append, &args_struct);
 
        g_free(notify_path);
 
@@ -1657,8 +2008,8 @@ static int session_create(gboolean connect)
        return res;
 }
 
-static int session_destroy_cb(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int session_destroy_cb(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
        if (error) {
                fprintf(stderr, "Error destroying session: %s", error);
@@ -1689,8 +2040,8 @@ static int session_destroy(void)
                        session_destroy_append, session_path);
 }
 
-static int session_config_return(DBusMessageIter *iter, const char *error,
-               void *user_data)
+static int session_config_return(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
 {
        char *property_name = user_data;
 
@@ -1701,31 +2052,14 @@ static int session_config_return(DBusMessageIter *iter, const char *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;
+       char *ifname;
+       dbus_bool_t source_ip_rule;
 
        while (index < num && args[index]) {
                append.opts = &args[index];
@@ -1752,6 +2086,53 @@ static int session_config(char *args[], int num,
                                        DBUS_TYPE_STRING, &args[index + 1]);
                        append.values = 2;
                        break;
+               case 'i':
+                       if (index + 1 < num)
+                               ifname = args[index + 1];
+                       else
+                               ifname = "";
+
+                       res = __connmanctl_dbus_session_change(connection,
+                                       session_path, session_config_return,
+                                       "AllowedInterface", "AllowedInterface",
+                                       DBUS_TYPE_STRING, &ifname);
+                       append.values = 2;
+                       break;
+               case 's':
+                       if (!args[index + 1]) {
+                               res = -EINVAL;
+                               break;
+                       }
+                       switch (parse_boolean(args[index + 1])) {
+                       case 1:
+                               source_ip_rule = TRUE;
+                               break;
+                       case 0:
+                               source_ip_rule = FALSE;
+                               break;
+                       default:
+                               res = -EINVAL;
+                               break;
+                       }
+
+                       res = __connmanctl_dbus_session_change(connection,
+                                       session_path, session_config_return,
+                                       "SourceIPRule", "SourceIPRule",
+                                       DBUS_TYPE_BOOLEAN, &source_ip_rule);
+                       append.values = 2;
+                       break;
+               case 'c':
+                               if (!args[index + 1]) {
+                                       res = -EINVAL;
+                                       break;
+                               }
+
+                               res = __connmanctl_dbus_session_change(connection,
+                                               session_path, session_config_return,
+                                               "ctxid", "ctxid", DBUS_TYPE_STRING,
+                                               &args[index + 1]);
+                               append.values = 2;
+                               break;
 
                default:
                        res = -EINVAL;
@@ -1787,12 +2168,13 @@ static int cmd_session(char *args[], int num, struct connman_option *options)
        case 1:
                if (session_path)
                        return -EALREADY;
-               return session_create(FALSE);
+               return session_create(FALSE, &args[2], num - 2, options);
 
        default:
                if (!strcmp(command, "connect")) {
                        if (!session_path)
-                               return session_create(TRUE);
+                               return session_create(TRUE, &args[2], num - 2,
+                                                     options);
 
                        return session_connect();
 
@@ -1826,22 +2208,21 @@ static int cmd_exit(char *args[], int num, struct connman_option *options)
        return 1;
 }
 
-static char *lookup_service(const char *text, int state)
+static char *lookup_key_from_table(GHashTable *hash, 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);
+               g_hash_table_iter_init(&iter, 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);
-       }
+       while (g_hash_table_iter_next(&iter, &key, &value))
+               if (strncmp(text, key, len) == 0)
+                       return strdup(key);
 
        return NULL;
 }
@@ -1853,7 +2234,7 @@ static char *lookup_service_arg(const char *text, int state)
                return NULL;
        }
 
-       return lookup_service(text, state);
+       return lookup_key_from_table(service_hash, text, state);
 }
 
 static char *lookup_peer(const char *text, int state)
@@ -1997,6 +2378,16 @@ static char *lookup_agent(const char *text, int state)
        return lookup_on_off(text, state);
 }
 
+static char *lookup_vpnconnection_arg(const char *text, int state)
+{
+       if (__connmanctl_input_calc_level() > 1) {
+               __connmanctl_input_lookup_end();
+               return NULL;
+       }
+
+       return lookup_key_from_table(vpnconnection_hash, text, state);
+}
+
 static struct connman_option service_options[] = {
        {"properties", 'p', "[<service>]      (obsolete)"},
        { NULL, }
@@ -2006,6 +2397,7 @@ static struct connman_option config_options[] = {
        {"nameservers", 'n', "<dns1> [<dns2>] [<dns3>]"},
        {"timeservers", 't', "<ntp1> [<ntp2>] [...]"},
        {"domains", 'd', "<domain1> [<domain2>] [...]"},
+       {"mdns", 'm', "yes|no"},
        {"ipv6", 'v', "off|auto [enable|disable|preferred]|\n"
                      "\t\t\tmanual <address> <prefixlength> <gateway>"},
        {"proxy", 'x', "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
@@ -2030,6 +2422,9 @@ static struct connman_option monitor_options[] = {
 static struct connman_option session_options[] = {
        {"bearers", 'b', "<technology1> [<technology2> [...]]"},
        {"type", 't', "local|internet|any"},
+       {"ifname", 'i', "[<interface_name>]"},
+       {"srciprule", 's', "yes|no"},
+       {"ctxid", 'c', "<context_identifier>"},
        { NULL, }
 };
 
@@ -2075,7 +2470,7 @@ static char *lookup_monitor(const char *text, int state)
 static char *lookup_config(const char *text, int state)
 {
        if (__connmanctl_input_calc_level() < 2)
-               return lookup_service(text, state);
+               return lookup_key_from_table(service_hash, text, state);
 
        return lookup_options(config_options, text, state);
 }
@@ -2085,6 +2480,273 @@ static char *lookup_session(const char *text, int state)
        return lookup_options(session_options, text, state);
 }
 
+static int peer_service_cb(DBusMessageIter *iter, int errnum,
+                       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;
@@ -2097,6 +2759,8 @@ static const struct {
          "Shows if the system is online or offline", NULL },
        { "technologies", NULL,           NULL,            cmd_technologies,
          "Display technologies", NULL },
+       { "clock",        NULL,           NULL,            cmd_clock,
+         "Get System Clock Properties", NULL },
        { "enable",       "<technology>|offline", NULL,    cmd_enable,
          "Enables given technology or offline mode",
          lookup_technology_offline },
@@ -2108,6 +2772,8 @@ static const struct {
                                          NULL,            cmd_tether,
          "Enable, disable tethering, set SSID and passphrase for wifi",
          lookup_tether },
+       { "tethering_clients", NULL,      NULL,            cmd_tethering_clients,
+         "Display tethering clients", NULL },
        { "services",     "[<service>]",  service_options, cmd_services,
          "Display services", lookup_service_arg },
        { "peers",        "[peer]",       NULL,            cmd_peers,
@@ -2115,22 +2781,34 @@ static const struct {
        { "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 },
+       { "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 },
+       { "move-before",   "<service> <target service>  ", NULL,
+         cmd_service_move_before, "Move <service> before <target service>",
+         lookup_service_arg },
+       { "move-after",   "<service> <target service>   ", NULL,
+         cmd_service_move_after, "Move <service> after <target 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 },
+       { "vpnconnections", "[<connection>]", NULL,        cmd_vpnconnections,
+         "Display VPN connections", lookup_vpnconnection_arg },
        { "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 },
        { "help",         NULL,           NULL,            cmd_help,
          "Show help", NULL },
        { "exit",         NULL,           NULL,            cmd_exit,
@@ -2312,13 +2990,84 @@ static void update_services(DBusMessageIter *iter)
        }
 }
 
-static int populate_service_hash(DBusMessageIter *iter, const char *error,
-                               void *user_data)
+static int populate_service_hash(DBusMessageIter *iter, int errnum,
+                               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_vpnconnection_id(const char *path)
+{
+       g_hash_table_replace(vpnconnection_hash, g_strdup(path),
+                       GINT_TO_POINTER(TRUE));
+}
+
+static void remove_vpnconnection_id(const char *path)
+{
+       g_hash_table_remove(vpnconnection_hash, path);
+}
+
+static void vpnconnection_added(DBusMessageIter *iter)
+{
+       char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       add_vpnconnection_id(get_path(path));
+}
+
+static void vpnconnection_removed(DBusMessageIter *iter)
+{
+       char *path = NULL;
+
+       dbus_message_iter_get_basic(iter, &path);
+       remove_vpnconnection_id(get_path(path));
+}
+
+static void add_vpnconnections(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_vpnconnection_id(get_path(path));
+
+               dbus_message_iter_next(iter);
+       }
+}
+
+static int populate_vpnconnection_hash(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
+{
+       DBusMessageIter array;
+
+       if (error) {
+               fprintf(stderr, "Error getting VPN connections: %s", error);
+               return 0;
+       }
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return 0;
+
+       dbus_message_iter_recurse(iter, &array);
+
+       add_vpnconnections(&array);
+
+       return 0;
+}
+
 static void add_peer_id(const char *path)
 {
        g_hash_table_replace(peer_hash, g_strdup(path), GINT_TO_POINTER(TRUE));
@@ -2373,9 +3122,14 @@ static void update_peers(DBusMessageIter *iter)
        }
 }
 
-static int populate_peer_hash(DBusMessageIter *iter,
-                                       const char *error, void *user_data)
+static int populate_peer_hash(DBusMessageIter *iter, int errnum,
+                       const char *error, void *user_data)
 {
+       if (error) {
+               fprintf(stderr, "Error getting peers: %s", error);
+               return 0;
+       }
+
        update_peers(iter);
        return 0;
 }
@@ -2433,9 +3187,14 @@ static void update_technologies(DBusMessageIter *iter)
        }
 }
 
-static int populate_technology_hash(DBusMessageIter *iter, const char *error,
-                               void *user_data)
+static int populate_technology_hash(DBusMessageIter *iter, int errnum,
+                               const char *error, void *user_data)
 {
+       if (error) {
+               fprintf(stderr, "Error getting technologies: %s\n", error);
+               return 0;
+       }
+
        update_technologies(iter);
 
        return 0;
@@ -2461,6 +3220,20 @@ static DBusHandlerResult monitor_completions_changed(
                return handled;
        }
 
+       if (dbus_message_is_signal(message, "net.connman.vpn.Manager",
+                                       "ConnectionAdded")) {
+               dbus_message_iter_init(message, &iter);
+               vpnconnection_added(&iter);
+               return handled;
+       }
+
+       if (dbus_message_is_signal(message, "net.connman.vpn.Manager",
+                                       "ConnectionRemoved")) {
+               dbus_message_iter_init(message, &iter);
+               vpnconnection_removed(&iter);
+               return handled;
+       }
+
        if (dbus_message_is_signal(message, "net.connman.Manager",
                                                "PeersChanged")) {
                dbus_message_iter_init(message, &iter);
@@ -2504,10 +3277,14 @@ void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
 
        if (!dbus_conn) {
                g_hash_table_destroy(service_hash);
+               g_hash_table_destroy(vpnconnection_hash);
                g_hash_table_destroy(technology_hash);
 
                dbus_bus_remove_match(connection,
                        "type='signal',interface='net.connman.Manager'", NULL);
+               dbus_bus_remove_match(connection,
+                       "type='signal',interface='net.connman.vpn.Manager'",
+                       NULL);
                dbus_connection_remove_filter(connection,
                                        monitor_completions_changed,
                                        manager_enabled);
@@ -2519,6 +3296,9 @@ void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
        service_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                                g_free, NULL);
 
+       vpnconnection_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);
 
@@ -2531,6 +3311,11 @@ void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
                                populate_service_hash, NULL, NULL, NULL);
 
        __connmanctl_dbus_method_call(connection,
+                               VPN_SERVICE, CONNMAN_PATH,
+                               "net.connman.vpn.Manager", "GetConnections",
+                               populate_vpnconnection_hash, NULL, NULL, NULL);
+
+       __connmanctl_dbus_method_call(connection,
                                CONNMAN_SERVICE, CONNMAN_PATH,
                                "net.connman.Manager", "GetPeers",
                                populate_peer_hash, NULL, NULL, NULL);
@@ -2548,6 +3333,15 @@ void __connmanctl_monitor_completions(DBusConnection *dbus_conn)
        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);
+               return;
+       }
+
+       dbus_bus_add_match(connection,
+                       "type='signal',interface='net.connman.vpn.Manager'",
+                       &err);
+
        if (dbus_error_is_set(&err))
                fprintf(stderr, "Error: %s\n", err.message);
 }