summaryrefslogtreecommitdiff
path: root/cli/src/connections.c
diff options
context:
space:
mode:
Diffstat (limited to 'cli/src/connections.c')
-rw-r--r--cli/src/connections.c1261
1 files changed, 1261 insertions, 0 deletions
diff --git a/cli/src/connections.c b/cli/src/connections.c
new file mode 100644
index 0000000000..ae09fe0b5f
--- /dev/null
+++ b/cli/src/connections.c
@@ -0,0 +1,1261 @@
+/* nmcli - command-line tool to control NetworkManager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2010 Red Hat, Inc.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <netinet/ether.h>
+
+#include <nm-client.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-wired.h>
+#include <nm-setting-pppoe.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-gsm.h>
+#include <nm-setting-cdma.h>
+#include <nm-setting-bluetooth.h>
+#include <nm-setting-olpc-mesh.h>
+#include <nm-device-ethernet.h>
+#include <nm-device-wifi.h>
+#include <nm-gsm-device.h>
+#include <nm-cdma-device.h>
+#include <nm-device-bt.h>
+//#include <nm-device-olpc-mesh.h>
+#include <nm-remote-settings.h>
+#include <nm-remote-settings-system.h>
+#include <nm-settings-interface.h>
+#include <nm-settings-connection-interface.h>
+#include <nm-vpn-connection.h>
+
+#include "utils.h"
+#include "connections.h"
+
+
+typedef struct {
+ NmCli *nmc;
+ int argc;
+ char **argv;
+} ArgsInfo;
+
+extern GMainLoop *loop; /* glib main loop variable */
+
+static ArgsInfo args_info;
+
+/* static function prototypes */
+static void usage (void);
+static void quit (void);
+static void show_connection (NMConnection *data, gpointer user_data);
+static NMConnection *find_connection (GSList *list, const char *filter_type, const char *filter_val);
+static gboolean find_device_for_connection (NmCli *nmc, NMConnection *connection, const char *iface, const char *ap,
+ NMDevice **device, const char **spec_object, GError **error);
+static const char *active_connection_state_to_string (NMActiveConnectionState state);
+static void active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data);
+static void activate_connection_cb (gpointer user_data, const char *path, GError *error);
+static void get_connections_cb (NMSettingsInterface *settings, gpointer user_data);
+static NMCResultCode do_connections_list (NmCli *nmc, int argc, char **argv);
+static NMCResultCode do_connections_status (NmCli *nmc, int argc, char **argv);
+static NMCResultCode do_connection_up (NmCli *nmc, int argc, char **argv);
+static NMCResultCode do_connection_down (NmCli *nmc, int argc, char **argv);
+
+static void
+usage (void)
+{
+ fprintf (stderr,
+ _("Usage: nmcli con { COMMAND | help }\n"
+ " COMMAND := { list | status | up | down }\n\n"
+ " list [id <id> | uuid <id> | system | user]\n"
+ " status\n"
+ " up id <id> | uuid <id> [iface <iface>] [ap <hwaddr>] [--nowait] [--timeout <timeout>]\n"
+ " down id <id> | uuid <id>\n"));
+}
+
+/* quit main loop */
+static void
+quit (void)
+{
+ g_main_loop_quit (loop); /* quit main loop */
+}
+
+static void
+show_connection (NMConnection *data, gpointer user_data)
+{
+ NMConnection *connection = (NMConnection *) data;
+ NMSettingConnection *s_con;
+ const char *id;
+ const char *uuid;
+ const char *con_type;
+
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ if (s_con) {
+ id = nm_setting_connection_get_id (s_con);
+ uuid = nm_setting_connection_get_uuid (s_con);
+ con_type = nm_setting_connection_get_connection_type (s_con);
+ print_table_line (0, con_type, 17, uuid, 38, id, 0, NULL);
+ }
+}
+
+static NMConnection *
+find_connection (GSList *list, const char *filter_type, const char *filter_val)
+{
+ NMSettingConnection *s_con;
+ NMConnection *connection;
+ GSList *iterator;
+ const char *id;
+ const char *uuid;
+
+ iterator = list;
+ while (iterator) {
+ connection = NM_CONNECTION (iterator->data);
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ if (s_con) {
+ id = nm_setting_connection_get_id (s_con);
+ uuid = nm_setting_connection_get_uuid (s_con);
+ if (filter_type) {
+ if ((strcmp (filter_type, "id") == 0 && strcmp (filter_val, id) == 0) ||
+ (strcmp (filter_type, "uuid") == 0 && strcmp (filter_val, uuid) == 0)) {
+ return connection;
+ }
+ }
+ }
+ iterator = g_slist_next (iterator);
+ }
+
+ return NULL;
+}
+
+static NMCResultCode
+do_connections_list (NmCli *nmc, int argc, char **argv)
+{
+ gboolean valid_param_specified = FALSE;
+
+ nmc->should_wait = FALSE;
+
+ if (argc == 0) {
+ valid_param_specified = TRUE;
+ if (nmc->print_output == NMC_PRINT_PRETTY)
+ print_table_header (_("Connections"), _("Type"), 17, _("UUID"), 38, _("Name"), 20, NULL);
+ else if (nmc->print_output == NMC_PRINT_NORMAL)
+ print_table_line (0, _("Type"), 17, _("UUID"), 38, _("Name"), 0, NULL);
+
+ if (nmc->print_output > NMC_PRINT_TERSE)
+ printf (_("System connections:\n"));
+ g_slist_foreach (nmc->system_connections, (GFunc) show_connection, NULL);
+
+ if (nmc->print_output > NMC_PRINT_TERSE)
+ printf (_("User connections:\n"));
+ g_slist_foreach (nmc->user_connections, (GFunc) show_connection, NULL);
+ }
+ else {
+ while (argc > 0) {
+ if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) {
+ const char *selector = *argv;
+ NMConnection *con1;
+ NMConnection *con2;
+
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+ valid_param_specified = TRUE;
+
+ con1 = find_connection (nmc->system_connections, selector, *argv);
+ con2 = find_connection (nmc->user_connections, selector, *argv);
+ if (con1) nm_connection_dump (con1);
+ if (con2) nm_connection_dump (con2);
+ if (!con1 && !con2) {
+ g_string_printf (nmc->return_text, _("Error: %s - no such connection."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ }
+ }
+ else if (strcmp (*argv, "system") == 0) {
+ valid_param_specified = TRUE;
+ if (nmc->print_output == NMC_PRINT_PRETTY)
+ print_table_header (_("System-wide connections"), _("Type"), 17, _("UUID"), 38, _("Name"), 20, NULL);
+ else if (nmc->print_output == NMC_PRINT_NORMAL)
+ print_table_line (0, _("Type"), 17, _("UUID"), 38, _("Name"), 0, NULL);
+
+ g_slist_foreach (nmc->system_connections, (GFunc) show_connection, NULL);
+ }
+ else if (strcmp (*argv, "user") == 0) {
+ valid_param_specified = TRUE;
+ if (nmc->print_output == NMC_PRINT_PRETTY)
+ print_table_header (_("User connections"), _("Type"), 17, _("UUID"), 38, _("Name"), 20, NULL);
+ else if (nmc->print_output == NMC_PRINT_NORMAL)
+ print_table_line (0, _("Type"), 17, _("UUID"), 38, _("Name"), 0, NULL);
+
+ g_slist_foreach (nmc->user_connections, (GFunc) show_connection, NULL);
+ }
+ else {
+ fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
+ }
+
+ argc--;
+ argv++;
+ }
+ }
+
+ if (!valid_param_specified) {
+ g_string_printf (nmc->return_text, _("Error: no valid parameter specified."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ }
+
+error:
+ return nmc->return_value;
+}
+
+static void
+show_active_connection (gpointer data, gpointer user_data)
+{
+ NMActiveConnection *active = NM_ACTIVE_CONNECTION (data);
+ GSList *con_list = (GSList *) user_data;
+ GSList *iter;
+ const char *active_path;
+ NMConnectionScope active_service_scope;
+ NMSettingConnection *s_con;
+ const GPtrArray *devices;
+ GString *dev_str;
+ int i;
+
+ dev_str = g_string_new (NULL);
+
+ active_path = nm_active_connection_get_connection (active);
+ active_service_scope = nm_active_connection_get_scope (active);
+
+ /* Get devices of the active connection */
+ devices = nm_active_connection_get_devices (active);
+ for (i = 0; devices && (i < devices->len); i++) {
+ NMDevice *device = g_ptr_array_index (devices, i);
+
+ g_string_append (dev_str, nm_device_get_iface (device));
+ g_string_append_c (dev_str, ',');
+ }
+ if (dev_str->len > 0)
+ g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */
+
+ for (iter = con_list; iter; iter = g_slist_next (iter)) {
+ NMConnection *connection = (NMConnection *) iter->data;
+ const char *con_path = nm_connection_get_path (connection);
+ NMConnectionScope con_scope = nm_connection_get_scope (connection);
+
+ if (!strcmp (active_path, con_path) && active_service_scope == con_scope) {
+ /* this connection is active */
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con != NULL);
+ // FIXME: Fix the output
+ print_table_line (0, nm_active_connection_get_default (active) ? _("yes") : _("no"), 8,
+ nm_active_connection_get_service_name (active), 45,
+// nm_active_connection_get_specific_object (active), 0,
+// nm_active_connection_get_connection (active), 0,
+ dev_str->str, 10,
+ nm_setting_connection_get_uuid (s_con), 38,
+ nm_setting_connection_get_id (s_con), 0, NULL);
+
+ }
+ }
+
+ g_string_free (dev_str, TRUE);
+}
+
+static NMCResultCode
+do_connections_status (NmCli *nmc, int argc, char **argv)
+{
+ const GPtrArray *active_cons;
+
+ nmc->should_wait = FALSE;
+
+ /* create NMClient */
+ if (!nmc->get_client (nmc))
+ return nmc->return_value;
+
+ active_cons = nm_client_get_active_connections (nmc->client);
+
+ // FIXME: Fix the output
+ if (nmc->print_output == NMC_PRINT_PRETTY)
+ print_table_header (_("Active connections"), _("Default"), 8, _("Service"), 45, _("Devices"), 10, _("UUID"), 38, _("Name"), 20, NULL);
+ else if (nmc->print_output == NMC_PRINT_NORMAL)
+ print_table_line (0, _("Default"), 8, _("Service"), 45, _("Devices"), 10, _("UUID"), 38, _("Name"), 0, NULL);
+
+ if (active_cons && active_cons->len) {
+ g_ptr_array_foreach ((GPtrArray *) active_cons, show_active_connection, (gpointer) nmc->system_connections);
+ g_ptr_array_foreach ((GPtrArray *) active_cons, show_active_connection, (gpointer) nmc->user_connections);
+ }
+
+ return NMC_RESULT_SUCCESS;
+}
+
+/* --------------------
+ * These function should be moved to libnm-glib in the end.
+ */
+static gboolean
+check_ethernet_compatible (NMDeviceEthernet *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ const char *connection_type;
+ gboolean is_pppoe = FALSE;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+
+ connection_type = nm_setting_connection_get_connection_type (s_con);
+ if ( strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)
+ && strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) {
+ g_set_error (error, 0, 0,
+ "The connection was not a wired or PPPoE connection.");
+ return FALSE;
+ }
+
+ if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
+ is_pppoe = TRUE;
+
+ s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+ /* Wired setting is optional for PPPoE */
+ if (!is_pppoe && !s_wired) {
+ g_set_error (error, 0, 0,
+ "The connection was not a valid wired connection.");
+ return FALSE;
+ }
+
+ if (s_wired) {
+ const GByteArray *mac;
+ const char *device_mac_str;
+ struct ether_addr *device_mac;
+
+ device_mac_str = nm_device_ethernet_get_hw_address (device);
+ device_mac = ether_aton (device_mac_str);
+ if (!device_mac) {
+ g_set_error (error, 0, 0, "Invalid device MAC address.");
+ return FALSE;
+ }
+
+ mac = nm_setting_wired_get_mac_address (s_wired);
+ if (mac && memcmp (mac->data, device_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, 0, 0,
+ "The connection's MAC address did not match this device.");
+ return FALSE;
+ }
+ }
+
+ // FIXME: check bitrate against device capabilities
+
+ return TRUE;
+}
+
+static gboolean
+check_wifi_compatible (NMDeviceWifi *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingWireless *s_wireless;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+
+ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_WIRELESS_SETTING_NAME)) {
+ g_set_error (error, 0, 0,
+ "The connection was not a WiFi connection.");
+ return FALSE;
+ }
+
+ s_wireless = NM_SETTING_WIRELESS (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS));
+ if (!s_wireless) {
+ g_set_error (error, 0, 0,
+ "The connection was not a valid WiFi connection.");
+ return FALSE;
+ }
+
+ if (s_wireless) {
+ const GByteArray *mac;
+ const char *device_mac_str;
+ struct ether_addr *device_mac;
+
+ device_mac_str = nm_device_wifi_get_hw_address (device);
+ device_mac = ether_aton (device_mac_str);
+ if (!device_mac) {
+ g_set_error (error, 0, 0, "Invalid device MAC address.");
+ return FALSE;
+ }
+
+ mac = nm_setting_wireless_get_mac_address (s_wireless);
+ if (mac && memcmp (mac->data, device_mac->ether_addr_octet, ETH_ALEN)) {
+ g_set_error (error, 0, 0,
+ "The connection's MAC address did not match this device.");
+ return FALSE;
+ }
+ }
+
+ // FIXME: check channel/freq/band against bands the hardware supports
+ // FIXME: check encryption against device capabilities
+ // FIXME: check bitrate against device capabilities
+
+ return TRUE;
+}
+
+static gboolean
+check_bt_compatible (NMDeviceBt *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingBluetooth *s_bt;
+ const GByteArray *array;
+ char *str;
+ const char *device_hw_str;
+ int addr_match = FALSE;
+ const char *bt_type_str;
+ guint32 bt_type, bt_capab;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+
+ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME)) {
+ g_set_error (error, 0, 0,
+ "The connection was not a Bluetooth connection.");
+ return FALSE;
+ }
+
+ s_bt = NM_SETTING_BLUETOOTH (nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH));
+ if (!s_bt) {
+ g_set_error (error, 0, 0,
+ "The connection was not a valid Bluetooth connection.");
+ return FALSE;
+ }
+
+ array = nm_setting_bluetooth_get_bdaddr (s_bt);
+ if (!array || (array->len != ETH_ALEN)) {
+ g_set_error (error, 0, 0,
+ "The connection did not contain a valid Bluetooth address.");
+ return FALSE;
+ }
+
+ bt_type_str = nm_setting_bluetooth_get_connection_type (s_bt);
+ g_assert (bt_type_str);
+
+ bt_type = NM_BT_CAPABILITY_NONE;
+ if (!strcmp (bt_type_str, NM_SETTING_BLUETOOTH_TYPE_DUN))
+ bt_type = NM_BT_CAPABILITY_DUN;
+ else if (!strcmp (bt_type_str, NM_SETTING_BLUETOOTH_TYPE_PANU))
+ bt_type = NM_BT_CAPABILITY_NAP;
+
+ bt_capab = nm_device_bt_get_capabilities (device);
+ if (!(bt_type & bt_capab)) {
+ g_set_error (error, 0, 0,
+ "The connection was not compatible with the device's capabilities.");
+ return FALSE;
+ }
+
+ device_hw_str = nm_device_bt_get_hw_address (device);
+
+ str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ array->data[0], array->data[1], array->data[2],
+ array->data[3], array->data[4], array->data[5]);
+ addr_match = !strcmp (device_hw_str, str);
+ g_free (str);
+
+ return addr_match;
+}
+
+#if 0
+static gboolean
+check_olpc_mesh_compatible (NMDeviceOlpcMesh *device, NMConnection *connection, GError **error)
+{
+ NMSettingConnection *s_con;
+ NMSettingOlpcMesh *s_mesh;
+
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+
+ if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_OLPC_MESH_SETTING_NAME)) {
+ g_set_error (error, 0, 0,
+ "The connection was not a Mesh connection.");
+ return FALSE;
+ }
+
+ s_mesh = NM_SETTING_OLPC_MESH (nm_connection_get_setting (connection, NM_TYPE_SETTING_OLPC_MESH));
+ if (!s_mesh) {
+ g_set_error (error, 0, 0,
+ "The connection was not a valid Mesh connection.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+#endif
+
+static gboolean
+nm_device_is_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
+{
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+ if (NM_IS_DEVICE_ETHERNET (device))
+ return check_ethernet_compatible (NM_DEVICE_ETHERNET (device), connection, error);
+ else if (NM_IS_DEVICE_WIFI (device))
+ return check_wifi_compatible (NM_DEVICE_WIFI (device), connection, error);
+ else if (NM_IS_DEVICE_BT (device))
+ return check_bt_compatible (NM_DEVICE_BT (device), connection, error);
+// else if (NM_IS_DEVICE_OLPC_MESH (device))
+// return check_olpc_mesh_compatible (NM_DEVICE_OLPC_MESH (device), connection, error);
+
+ g_set_error (error, 0, 0, "unhandled device type '%s'", G_OBJECT_TYPE_NAME (device));
+ return FALSE;
+}
+
+
+/**
+ * nm_client_get_active_connection_by_path:
+ * @client: a #NMClient
+ * @object_path: the object path to search for
+ *
+ * Gets a #NMActiveConnection from a #NMClient.
+ *
+ * Returns: the #NMActiveConnection for the given @object_path or %NULL if none is found.
+ **/
+static NMActiveConnection *
+nm_client_get_active_connection_by_path (NMClient *client, const char *object_path)
+{
+ const GPtrArray *actives;
+ int i;
+ NMActiveConnection *active = NULL;
+
+ g_return_val_if_fail (NM_IS_CLIENT (client), NULL);
+ g_return_val_if_fail (object_path, NULL);
+
+ actives = nm_client_get_active_connections (client);
+ if (!actives)
+ return NULL;
+
+ for (i = 0; i < actives->len; i++) {
+ NMActiveConnection *candidate = g_ptr_array_index (actives, i);
+ if (!strcmp (nm_object_get_path (NM_OBJECT (candidate)), object_path)) {
+ active = candidate;
+ break;
+ }
+ }
+
+ return active;
+}
+/* -------------------- */
+
+static NMActiveConnection *
+get_default_active_connection (NmCli *nmc, NMDevice **device)
+{
+ NMActiveConnection *default_ac = NULL;
+ NMDevice *non_default_device = NULL;
+ NMActiveConnection *non_default_ac = NULL;
+ const GPtrArray *connections;
+ int i;
+
+ g_return_val_if_fail (nmc != NULL, NULL);
+ g_return_val_if_fail (device != NULL, NULL);
+ g_return_val_if_fail (*device == NULL, NULL);
+
+ connections = nm_client_get_active_connections (nmc->client);
+ for (i = 0; connections && (i < connections->len); i++) {
+ NMActiveConnection *candidate = g_ptr_array_index (connections, i);
+ const GPtrArray *devices;
+
+ devices = nm_active_connection_get_devices (candidate);
+ if (!devices || !devices->len)
+ continue;
+
+ if (nm_active_connection_get_default (candidate)) {
+ if (!default_ac) {
+ *device = g_ptr_array_index (devices, 0);
+ default_ac = candidate;
+ }
+ } else {
+ if (!non_default_ac) {
+ non_default_device = g_ptr_array_index (devices, 0);
+ non_default_ac = candidate;
+ }
+ }
+ }
+
+ /* Prefer the default connection if one exists, otherwise return the first
+ * non-default connection.
+ */
+ if (!default_ac && non_default_ac) {
+ default_ac = non_default_ac;
+ *device = non_default_device;
+ }
+ return default_ac;
+}
+
+/* Find a device to activate the connection on.
+ * IN: connection: connection to activate
+ * iface: device interface name to use (optional)
+ * ap: access point to use (optional; valid just for 802-11-wireless)
+ * OUT: device: found device
+ * spec_object: specific_object path of NMAccessPoint
+ * RETURNS: TRUE when a device is found, FALSE otherwise.
+ */
+static gboolean
+find_device_for_connection (NmCli *nmc, NMConnection *connection, const char *iface, const char *ap,
+ NMDevice **device, const char **spec_object, GError **error)
+{
+ NMSettingConnection *s_con;
+ const char *con_type;
+ int i, j;
+
+ g_return_val_if_fail (nmc != NULL, FALSE);
+ g_return_val_if_fail (device != NULL && *device == NULL, FALSE);
+ g_return_val_if_fail (spec_object != NULL && *spec_object == NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ con_type = nm_setting_connection_get_connection_type (s_con);
+
+ if (strcmp (con_type, "vpn") == 0) {
+ /* VPN connections */
+ NMActiveConnection *active = NULL;
+ if (iface) {
+ const GPtrArray *connections = nm_client_get_active_connections (nmc->client);
+ for (i = 0; connections && (i < connections->len) && !active; i++) {
+ NMActiveConnection *candidate = g_ptr_array_index (connections, i);
+ const GPtrArray *devices = nm_active_connection_get_devices (candidate);
+ if (!devices || !devices->len)
+ continue;
+
+ for (j = 0; devices && (j < devices->len); j++) {
+ NMDevice *dev = g_ptr_array_index (devices, j);
+ if (!strcmp (iface, nm_device_get_iface (dev))) {
+ active = candidate;
+ *device = dev;
+ break;
+ }
+ }
+ }
+ if (!active) {
+ g_set_error (error, 0, 0, _("no active connection on device '%s'"), iface);
+ return FALSE;
+ }
+ *spec_object = nm_object_get_path (NM_OBJECT (active));
+ return TRUE;
+ } else {
+ active = get_default_active_connection (nmc, device);
+ if (!active) {
+ g_set_error (error, 0, 0, _("no active connection or device"));
+ return FALSE;
+ }
+ *spec_object = nm_object_get_path (NM_OBJECT (active));
+ return TRUE;
+ }
+ } else {
+ /* Other connections */
+ NMDevice *found_device = NULL;
+ const GPtrArray *devices = nm_client_get_devices (nmc->client);
+
+ for (i = 0; devices && (i < devices->len) && !found_device; i++) {
+ NMDevice *dev = g_ptr_array_index (devices, i);
+
+ if (iface) {
+ const char *dev_iface = nm_device_get_iface (dev);
+ if ( !strcmp (dev_iface, iface)
+ && nm_device_is_connection_compatible (dev, connection, NULL)) {
+ found_device = dev;
+ }
+ } else {
+ if (nm_device_is_connection_compatible (dev, connection, NULL)) {
+ found_device = dev;
+ }
+ }
+
+ if (found_device && ap && !strcmp (con_type, "802-11-wireless") && NM_IS_DEVICE_WIFI (dev)) {
+ char *hwaddr_up = g_ascii_strup (ap, -1);
+ const GPtrArray *aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev));
+ found_device = NULL; /* Mark as not found; set to the device again later, only if AP matches */
+
+ for (j = 0; aps && (j < aps->len); j++) {
+ NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j);
+ const char *candidate_hwaddr = nm_access_point_get_hw_address (candidate_ap);
+
+ if (!strcmp (hwaddr_up, candidate_hwaddr)) {
+ found_device = dev;
+ *spec_object = nm_object_get_path (NM_OBJECT (candidate_ap));
+ break;
+ }
+ }
+ g_free (hwaddr_up);
+ }
+ }
+
+ if (found_device) {
+ *device = found_device;
+ return TRUE;
+ } else {
+ if (iface)
+ g_set_error (error, 0, 0, "device '%s' not compatible with connection '%s'", iface, nm_setting_connection_get_id (s_con));
+ else
+ g_set_error (error, 0, 0, "no device found for connection '%s'", nm_setting_connection_get_id (s_con));
+ return FALSE;
+ }
+ }
+}
+
+static const char *
+active_connection_state_to_string (NMActiveConnectionState state)
+{
+ switch (state) {
+ case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
+ return _("activating");
+ case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
+ return _("activated");
+ case NM_ACTIVE_CONNECTION_STATE_UNKNOWN:
+ default:
+ return _("unknown");
+ }
+}
+
+static const char *
+vpn_connection_state_to_string (NMVPNConnectionState state)
+{
+ switch (state) {
+ case NM_VPN_CONNECTION_STATE_PREPARE:
+ return _("VPN connecting (prepare)");
+ case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+ return _("VPN connecting (need authentication)");
+ case NM_VPN_CONNECTION_STATE_CONNECT:
+ return _("VPN connecting");
+ case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+ return _("VPN connecting (getting IP configuration)");
+ case NM_VPN_CONNECTION_STATE_ACTIVATED:
+ return _("VPN connected");
+ case NM_VPN_CONNECTION_STATE_FAILED:
+ return _("VPN connection failed");
+ case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+ return _("VPN disconnected");
+ default:
+ return _("unknown");
+ }
+}
+
+static const char *
+vpn_connection_state_reason_to_string (NMVPNConnectionStateReason reason)
+{
+ switch (reason) {
+ case NM_VPN_CONNECTION_STATE_REASON_UNKNOWN:
+ return _("unknown reason");
+ case NM_VPN_CONNECTION_STATE_REASON_NONE:
+ return _("none");
+ case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
+ return _("the user was disconnected");
+ case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
+ return _("the base network connection was interrupted");
+ case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
+ return _("the VPN service stopped unexpectedly");
+ case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
+ return _("the VPN service returned invalid configuration");
+ case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
+ return _("the connection attempt timed out");
+ case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
+ return _("the VPN service did not start in time");
+ case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
+ return _("the VPN service failed to start");
+ case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
+ return _("no valid VPN secrets");
+ case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
+ return _("invalid VPN secrets");
+ case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
+ return _("the connection was removed");
+ default:
+ return _("unknown");
+ }
+}
+
+static void
+active_connection_state_cb (NMActiveConnection *active, GParamSpec *pspec, gpointer user_data)
+{
+ NmCli *nmc = (NmCli *) user_data;
+ NMActiveConnectionState state;
+
+ state = nm_active_connection_get_state (active);
+
+ printf (_("state: %s\n"), active_connection_state_to_string (state));
+
+ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+ printf (_("Connection activated\n"));
+ quit ();
+ } else if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
+ g_string_printf (nmc->return_text, _("Error: Connection activation failed."));
+ nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+ quit ();
+ }
+}
+
+static void
+vpn_connection_state_cb (NMVPNConnection *vpn,
+ NMVPNConnectionState state,
+ NMVPNConnectionStateReason reason,
+ gpointer user_data)
+{
+ NmCli *nmc = (NmCli *) user_data;
+
+ switch (state) {
+ case NM_VPN_CONNECTION_STATE_PREPARE:
+ case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+ case NM_VPN_CONNECTION_STATE_CONNECT:
+ case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+ printf (_("state: %s (%d)\n"), vpn_connection_state_to_string (state), state);
+ break;
+
+ case NM_VPN_CONNECTION_STATE_ACTIVATED:
+ printf (_("Connection activated\n"));
+ quit ();
+ break;
+
+ case NM_VPN_CONNECTION_STATE_FAILED:
+ case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+ g_string_printf (nmc->return_text, _("Error: Connection activation failed: %s."), vpn_connection_state_reason_to_string (reason));
+ nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+ quit ();
+ break;
+
+ default:
+ break;
+ }
+}
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+ /* Time expired -> exit nmcli */
+
+ NmCli *nmc = (NmCli *) user_data;
+
+ g_string_printf (nmc->return_text, _("Error: Timeout %d sec expired."), nmc->timeout);
+ nmc->return_value = NMC_RESULT_ERROR_TIMEOUT_EXPIRED;
+ quit ();
+ return FALSE;
+}
+
+static void
+foo_active_connections_changed_cb (NMClient *client,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ /* Call again activate_connection_cb with dummy arguments;
+ * the correct ones are taken from its first call.
+ */
+ activate_connection_cb (NULL, NULL, NULL);
+}
+
+static void
+activate_connection_cb (gpointer user_data, const char *path, GError *error)
+{
+ NmCli *nmc = (NmCli *) user_data;
+ NMActiveConnection *active;
+ NMActiveConnectionState state;
+ static gulong handler_id = 0;
+ static NmCli *orig_nmc;
+ static const char *orig_path;
+ static GError *orig_error;
+
+ if (nmc)
+ {
+ /* Called first time; store actual arguments */
+ orig_nmc = nmc;
+ orig_path = path;
+ orig_error = error;
+ }
+
+ /* Disconnect the handler not to be run any more */
+ if (handler_id != 0) {
+ g_signal_handler_disconnect (orig_nmc->client, handler_id);
+ handler_id = 0;
+ }
+
+ if (orig_error) {
+ g_string_printf (orig_nmc->return_text, _("Error: Connection activation failed: %s"), orig_error->message);
+ orig_nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+ quit ();
+ } else {
+ active = nm_client_get_active_connection_by_path (orig_nmc->client, orig_path);
+ if (!active) {
+ /* The active connection path is not in active connections list yet; wait for active-connections signal. */
+ /* This is basically the case for VPN connections. */
+ if (nmc) {
+ /* Called first time, i.e. by nm_client_activate_connection() */
+ handler_id = g_signal_connect (orig_nmc->client, "notify::active-connections",
+ G_CALLBACK (foo_active_connections_changed_cb), NULL);
+ return;
+ } else {
+ g_string_printf (orig_nmc->return_text, _("Error: Obtaining active connection for '%s' failed."), orig_path);
+ orig_nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+ quit ();
+ return;
+ }
+ }
+
+ state = nm_active_connection_get_state (active);
+
+ printf (_("Active connection state: %s\n"), active_connection_state_to_string (state));
+ printf (_("Active connection path: %s\n"), orig_path);
+
+ if (!orig_nmc->should_wait || state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+ /* don't want to wait or already activated */
+ quit ();
+ } else {
+ if (NM_IS_VPN_CONNECTION (active))
+ g_signal_connect (NM_VPN_CONNECTION (active), "vpn-state-changed", G_CALLBACK (vpn_connection_state_cb), orig_nmc);
+ else
+ g_signal_connect (active, "notify::state", G_CALLBACK (active_connection_state_cb), orig_nmc);
+
+ /* Start timer not to loop forever when signals are not emitted */
+ g_timeout_add_seconds (orig_nmc->timeout, timeout_cb, orig_nmc);
+ }
+ }
+}
+
+static NMCResultCode
+do_connection_up (NmCli *nmc, int argc, char **argv)
+{
+ NMDevice *device = NULL;
+ const char *spec_object = NULL;
+ gboolean device_found;
+ NMConnection *connection = NULL;
+ NMSettingConnection *s_con;
+ gboolean is_system;
+ const char *con_path;
+ const char *con_type;
+ const char *iface = NULL;
+ const char *ap = NULL;
+ gboolean id_specified = FALSE;
+ gboolean wait = TRUE;
+ GError *error = NULL;
+
+ /* Set default timeout for connection activation. It can take quite a long time.
+ * Using 90 seconds.
+ */
+ nmc->timeout = 90;
+
+ while (argc > 0) {
+ if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) {
+ const char *selector = *argv;
+ id_specified = TRUE;
+
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ if ((connection = find_connection (nmc->system_connections, selector, *argv)) == NULL)
+ connection = find_connection (nmc->user_connections, selector, *argv);
+
+ if (!connection) {
+ g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+ }
+ else if (strcmp (*argv, "iface") == 0) {
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ iface = *argv;
+ }
+ else if (strcmp (*argv, "ap") == 0) {
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ ap = *argv;
+ }
+ else if (strcmp (*argv, "--nowait") == 0) {
+ wait = FALSE;
+ } else if (strcmp (*argv, "--timeout") == 0) {
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ errno = 0;
+ nmc->timeout = strtol (*argv, NULL, 10);
+ if (errno || nmc->timeout < 0) {
+ g_string_printf (nmc->return_text, _("Error: timeout value '%s' is not valid."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+ } else {
+ fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
+ }
+
+ argc--;
+ argv++;
+ }
+
+ if (!id_specified) {
+ g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ /* create NMClient */
+ if (!nmc->get_client (nmc))
+ goto error;
+
+ is_system = (nm_connection_get_scope (connection) == NM_CONNECTION_SCOPE_SYSTEM) ? TRUE : FALSE;
+ con_path = nm_connection_get_path (connection);
+
+ s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
+ g_assert (s_con);
+ con_type = nm_setting_connection_get_connection_type (s_con);
+
+ device_found = find_device_for_connection (nmc, connection, iface, ap, &device, &spec_object, &error);
+
+ if (!device_found) {
+ if (error)
+ g_string_printf (nmc->return_text, _("Error: No suitable device found: %s."), error->message);
+ else
+ g_string_printf (nmc->return_text, _("Error: No suitable device found."));
+ nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
+ goto error;
+ }
+
+ nmc->should_wait = wait;
+ nm_client_activate_connection (nmc->client,
+ is_system ? NM_DBUS_SERVICE_SYSTEM_SETTINGS : NM_DBUS_SERVICE_USER_SETTINGS,
+ con_path,
+ device,
+ spec_object,
+ activate_connection_cb,
+ nmc);
+
+ return nmc->return_value;
+error:
+ nmc->should_wait = FALSE;
+ return nmc->return_value;
+}
+
+static NMCResultCode
+do_connection_down (NmCli *nmc, int argc, char **argv)
+{
+ NMConnection *connection = NULL;
+ NMActiveConnection *active = NULL;
+ const GPtrArray *active_cons;
+ const char *con_path;
+ const char *active_path;
+ NMConnectionScope active_service_scope, con_scope;
+ gboolean id_specified = FALSE;
+ gboolean wait = TRUE;
+ int i;
+
+ while (argc > 0) {
+ if (strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0) {
+ const char *selector = *argv;
+ id_specified = TRUE;
+
+ if (next_arg (&argc, &argv) != 0) {
+ g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ if ((connection = find_connection (nmc->system_connections, selector, *argv)) == NULL)
+ connection = find_connection (nmc->user_connections, selector, *argv);
+
+ if (!connection) {
+ g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), *argv);
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+ }
+ else if (strcmp (*argv, "--nowait") == 0) {
+ wait = FALSE;
+ }
+ else {
+ fprintf (stderr, _("Unknown parameter: %s\n"), *argv);
+ }
+
+ argc--;
+ argv++;
+ }
+
+ if (!id_specified) {
+ g_string_printf (nmc->return_text, _("Error: id or uuid has to be specified."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ goto error;
+ }
+
+ /* create NMClient */
+ if (!nmc->get_client (nmc))
+ goto error;
+
+ con_path = nm_connection_get_path (connection);
+ con_scope = nm_connection_get_scope (connection);
+
+ active_cons = nm_client_get_active_connections (nmc->client);
+ for (i = 0; active_cons && (i < active_cons->len); i++) {
+ NMActiveConnection *candidate = g_ptr_array_index (active_cons, i);
+
+ active_path = nm_active_connection_get_connection (candidate);
+ active_service_scope = nm_active_connection_get_scope (candidate);
+ if (!strcmp (active_path, con_path) && active_service_scope == con_scope) {
+ active = candidate;
+ break;
+ }
+ }
+
+ if (active)
+ nm_client_deactivate_connection (nmc->client, active);
+ else {
+ fprintf (stderr, _("Warning: Connection not active\n"));
+ }
+
+error:
+ nmc->should_wait = FALSE;
+ return nmc->return_value;
+}
+
+/* callback called when connections are obtained from the settings service */
+static void
+get_connections_cb (NMSettingsInterface *settings, gpointer user_data)
+{
+ ArgsInfo *args = (ArgsInfo *) user_data;
+ static gboolean system_cb_called = FALSE;
+ static gboolean user_cb_called = FALSE;
+
+ if (NM_IS_REMOTE_SETTINGS_SYSTEM (settings)) {
+ system_cb_called = TRUE;
+ args->nmc->system_connections = nm_settings_interface_list_connections (settings);
+ }
+ else {
+ user_cb_called = TRUE;
+ args->nmc->user_connections = nm_settings_interface_list_connections (settings);
+ }
+
+ /* return and wait for the callback of the second settings is called */
+ if ((args->nmc->system_settings_running && !system_cb_called) ||
+ (args->nmc->user_settings_running && !user_cb_called))
+ return;
+
+ if (args->argc == 0) {
+ args->nmc->return_value = do_connections_list (args->nmc, args->argc, args->argv);
+ } else {
+
+ if (matches (*args->argv, "list") == 0) {
+ args->nmc->return_value = do_connections_list (args->nmc, args->argc-1, args->argv+1);
+ }
+ else if (matches(*args->argv, "status") == 0) {
+ args->nmc->return_value = do_connections_status (args->nmc, args->argc-1, args->argv+1);
+ }
+ else if (matches(*args->argv, "up") == 0) {
+ args->nmc->return_value = do_connection_up (args->nmc, args->argc-1, args->argv+1);
+ }
+ else if (matches(*args->argv, "down") == 0) {
+ args->nmc->return_value = do_connection_down (args->nmc, args->argc-1, args->argv+1);
+ }
+ else if (matches (*args->argv, "help") == 0) {
+ usage ();
+ args->nmc->should_wait = FALSE;
+ } else {
+ usage ();
+ g_string_printf (args->nmc->return_text, _("Error: 'con' command '%s' is not valid."), *args->argv);
+ args->nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ args->nmc->should_wait = FALSE;
+ }
+ }
+
+ if (!args->nmc->should_wait)
+ quit ();
+}
+
+
+/* Entry point function for connections-related commands: 'nmcli con' */
+NMCResultCode
+do_connections (NmCli *nmc, int argc, char **argv)
+{
+ DBusGConnection *bus;
+ GError *error = NULL;
+
+ nmc->should_wait = TRUE;
+
+ args_info.nmc = nmc;
+ args_info.argc = argc;
+ args_info.argv = argv;
+
+ /* connect to DBus' system bus */
+ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (error || !bus) {
+ g_string_printf (nmc->return_text, _("Error: could not connect to D-Bus."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ return nmc->return_value;
+ }
+
+ /* get system settings */
+ if (!(nmc->system_settings = nm_remote_settings_system_new (bus))) {
+ g_string_printf (nmc->return_text, _("Error: Could not get system settings."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ return nmc->return_value;
+
+ }
+
+ /* get user settings */
+ if (!(nmc->user_settings = nm_remote_settings_new (bus, NM_CONNECTION_SCOPE_USER))) {
+ g_string_printf (nmc->return_text, _("Error: Could not get user settings."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ return nmc->return_value;
+ }
+
+ /* find out whether setting services are running */
+ g_object_get (nmc->system_settings, NM_REMOTE_SETTINGS_SERVICE_RUNNING, &nmc->system_settings_running, NULL);
+ g_object_get (nmc->user_settings, NM_REMOTE_SETTINGS_SERVICE_RUNNING, &nmc->user_settings_running, NULL);
+
+ if (!nmc->system_settings_running && !nmc->user_settings_running) {
+ g_string_printf (nmc->return_text, _("Error: Can't obtain connections: settings services are not running."));
+ nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+ return nmc->return_value;
+ }
+
+ /* connect to signal "connections-read" - emitted when connections are fetched and ready */
+ if (nmc->system_settings_running)
+ g_signal_connect (nmc->system_settings, NM_SETTINGS_INTERFACE_CONNECTIONS_READ,
+ G_CALLBACK (get_connections_cb), &args_info);
+
+ if (nmc->user_settings_running)
+ g_signal_connect (nmc->user_settings, NM_SETTINGS_INTERFACE_CONNECTIONS_READ,
+ G_CALLBACK (get_connections_cb), &args_info);
+
+ dbus_g_connection_unref (bus);
+
+ /* The rest will be done in get_connection_cb() callback.
+ * We need to wait for signals that connections are read.
+ */
+ return NMC_RESULT_SUCCESS;
+}