diff --git a/cli/completion/nmcli b/cli/completion/nmcli index e2aff2969b..86e42fec5a 100644 --- a/cli/completion/nmcli +++ b/cli/completion/nmcli @@ -502,6 +502,13 @@ _nmcli_complete_COMMAND_CONNECTION() fi words=("${words[@]:2}") ;; + ifname) + if [[ ${#words[@]} -eq 2 ]]; then + _nmcli_list_nl "$(_nmcli_NM_devices)" + return 0 + fi + words=("${words[@]:2}") + ;; *) COMMAND_CONNECTION_TYPE= COMMAND_CONNECTION_ID="${words[0]}" @@ -601,12 +608,18 @@ _nmcli() ;; u|up) if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_list_nl "$(printf "id\nuuid\npath\n%s" "$(_nmcli_con_id)")" + _nmcli_list_nl "$(printf "ifname\nid\nuuid\npath\n%s" "$(_nmcli_con_id)")" elif [[ ${#words[@]} -gt 3 ]]; then + local COMMAND_CONNECTION_TYPE='' words=("${words[@]:2}") - OPTIONS=(id uuid path) + OPTIONS=(ifname id uuid path) _nmcli_complete_COMMAND_CONNECTION && return 0 - OPTIONS=(ifname ap nsp) + + if [[ "$COMMAND_CONNECTION_TYPE" = "ifname" ]]; then + OPTIONS=(ap nsp) + else + OPTIONS=(ifname ap nsp) + fi _nmcli_complete_COMMAND_ARGS fi ;; @@ -841,12 +854,13 @@ _nmcli() ;; d|de|dev|devi|devic|device) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_list "status show disconnect wifi wimax help" + _nmcli_list "status show connect disconnect wifi wimax help" elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|st|sta|stat|statu|status) ;; sh|sho|show| \ + c|co|con|conn|conne|connec|connect| \ d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect) if [[ ${#words[@]} -eq 3 ]]; then _nmcli_list_nl "$(_nmcli_NM_devices)" diff --git a/cli/src/connections.c b/cli/src/connections.c index aea6ecd36c..8dac4f10c9 100644 --- a/cli/src/connections.c +++ b/cli/src/connections.c @@ -218,9 +218,9 @@ usage (void) " show configured [[ id | uuid | path ] ]\n\n" " show active [[ id | uuid | path | apath ] ]\n\n" #if WITH_WIMAX - " up [ id | uuid | path ] [ifname ] [ap ] [nsp ]\n\n" + " up [[ id | uuid | path ] ] [ifname ] [ap ] [nsp ]\n\n" #else - " up [ id | uuid | path ] [ifname ] [ap ]\n\n" + " up [[ id | uuid | path ] ] [ifname ] [ap ]\n\n" #endif " down [ id | uuid | path | apath ] \n\n" " add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n" @@ -1517,18 +1517,31 @@ nmc_activate_connection (NmCli *nmc, GError *local = NULL; g_return_val_if_fail (nmc != NULL, FALSE); - g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection) || ifname, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (nm_connection_get_virtual_iface_name (connection)) - is_virtual = TRUE; + if (connection) { + if (nm_connection_get_virtual_iface_name (connection)) + is_virtual = TRUE; - device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local); - /* Virtual connection may not have their interfaces created yet */ - if (!device_found && !is_virtual) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, - "%s", local && local->message ? local->message : _("unknown error")); - g_clear_error (&local); + device_found = find_device_for_connection (nmc, connection, ifname, ap, nsp, &device, &spec_object, &local); + /* Virtual connection may not have their interfaces created yet */ + if (!device_found && !is_virtual) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, + "%s", local && local->message ? local->message : _("unknown error")); + g_clear_error (&local); + return FALSE; + } + } else if (ifname) { + device = nm_client_get_device_by_iface (nmc->client, ifname); + if (!device) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, + _("unknown device '%s'."), ifname); + return FALSE; + } + } else { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_CON_ACTIVATION, + _("no connection and no device given.")); return FALSE; } @@ -1554,7 +1567,7 @@ do_connection_up (NmCli *nmc, int argc, char **argv) const char *nsp = NULL; GError *error = NULL; const char *selector = NULL; - const char *name; + const char *name = NULL; char *line = NULL; /* @@ -1570,12 +1583,8 @@ do_connection_up (NmCli *nmc, int argc, char **argv) name = line ? line : ""; // TODO: enhancement: when just Enter is pressed (line is NULL), list // available connections so that the user can select one - } else { - g_string_printf (nmc->return_text, _("Error: No connection specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; } - } else { + } else if (strcmp (*argv, "ifname") != 0) { if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0) { @@ -1589,16 +1598,11 @@ do_connection_up (NmCli *nmc, int argc, char **argv) name = *argv; } name = *argv; + next_arg (&argc, &argv); } - connection = find_connection (nmc->system_connections, selector, name); - - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection: %s."), name); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; - } - next_arg (&argc, &argv); + if (name) + connection = find_connection (nmc->system_connections, selector, name); while (argc > 0) { if (strcmp (*argv, "ifname") == 0) { diff --git a/cli/src/devices.c b/cli/src/devices.c index 47ae792046..b0e9f510e6 100644 --- a/cli/src/devices.c +++ b/cli/src/devices.c @@ -262,12 +262,13 @@ usage (void) fprintf (stderr, _("Usage: nmcli device { COMMAND | help }\n\n" #if WITH_WIMAX - " COMMAND := { status | show | disconnect | wifi | wimax }\n\n" + " COMMAND := { status | show | connect | disconnect | wifi | wimax }\n\n" #else - " COMMAND := { status | show | disconnect | wifi }\n\n" + " COMMAND := { status | show | connect | disconnect | wifi }\n\n" #endif " status\n\n" " show []\n\n" + " connect \n\n" " disconnect \n\n" " wifi [list [ifname ] [bssid ]]\n\n" " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ] [bssid ] [name ]\n\n" @@ -1142,20 +1143,6 @@ error: return nmc->return_value; } -static void -device_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) -{ - NmCli *nmc = (NmCli *) user_data; - NMDeviceState state; - - state = nm_device_get_state (device); - - if (state == NM_DEVICE_STATE_DISCONNECTED) { - g_string_printf (nmc->return_text, _("Success: Device '%s' successfully disconnected."), nm_device_get_iface (device)); - quit (); - } -} - static gboolean timeout_cb (gpointer user_data) { @@ -1179,6 +1166,160 @@ progress_cb (gpointer user_data) return TRUE; } +static void +connected_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + NMDeviceState state; + + state = nm_device_get_state (device); + + if (state == NM_DEVICE_STATE_ACTIVATED) { + g_string_printf (nmc->return_text, _("Success: Device '%s' successfully activated."), nm_device_get_iface (device)); + quit (); + } +} + +static void +connect_device_cb (NMClient *client, NMActiveConnection *active, GError *error, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + const GPtrArray *devices; + NMDevice *device; + NMDeviceState state; + + if (error) { + g_string_printf (nmc->return_text, _("Error: Device activation failed: %s"), + error->message ? error->message : _("(unknown)")); + nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; + quit (); + } else { + g_assert (active); + devices = nm_active_connection_get_devices (active); + g_assert (devices && devices->len); + device = g_ptr_array_index (devices, 0); + g_assert (device); + + state = nm_device_get_state (device); + + if (nmc->nowait_flag || state == NM_DEVICE_STATE_ACTIVATED) { + /* Don't want to wait or device already activated */ + if (state == NM_DEVICE_STATE_ACTIVATED && nmc->print_output == NMC_PRINT_PRETTY) { + nmc_terminal_erase_line (); + printf (_("Device '%s' has been connected.\n"), nm_device_get_iface (device)); + } + quit (); + } else { + g_signal_connect (device, "notify::state", G_CALLBACK (connected_state_cb), nmc); + /* Start timer not to loop forever if "notify::state" signal is not issued */ + g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); + } + + } +} + +static NMCResultCode +do_device_connect (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device = NULL; + const char *ifname = NULL; + char *ifname_ask = NULL; + int i; + + /* Set default timeout for connect operation. */ + if (nmc->timeout == -1) + nmc->timeout = 90; + + if (argc == 0) { + if (nmc->ask) { + ifname = ifname_ask = nmc_get_user_input (_("Interface: ")); + // TODO: list available devices when just Enter is pressed ? + } + if (!ifname_ask) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else { + ifname = *argv; + } + + if (!ifname) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + if (next_arg (&argc, &argv) == 0) { + g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + nmc->get_client (nmc); + if (!nm_client_get_manager_running (nmc->client)) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + goto error; + } + + if (!nmc_versions_match (nmc)) + goto error; + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + device = candidate; + } + g_free (devices); + + if (!device) { + g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* + * Use nowait_flag instead of should_wait, because exiting has to be postponed + * till connect_device_cb() is called, giving NM time to check our permissions. + */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + nm_client_activate_connection (nmc->client, + NULL, /* let NM find a connection automatically */ + device, + NULL, + connect_device_cb, + nmc); + + /* Start progress indication */ + if (nmc->print_output == NMC_PRINT_PRETTY) + progress_id = g_timeout_add (120, progress_cb, device); + +error: + g_free (ifname_ask); + + return nmc->return_value; +} + +static void +disconnect_state_cb (NMDevice *device, GParamSpec *pspec, gpointer user_data) +{ + NmCli *nmc = (NmCli *) user_data; + NMDeviceState state; + + state = nm_device_get_state (device); + + if (state == NM_DEVICE_STATE_DISCONNECTED) { + g_string_printf (nmc->return_text, _("Success: Device '%s' successfully disconnected."), nm_device_get_iface (device)); + quit (); + } +} + static void disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data) { @@ -1204,7 +1345,7 @@ disconnect_device_cb (NMDevice *device, GError *error, gpointer user_data) } quit (); } else { - g_signal_connect (device, "notify::state", G_CALLBACK (device_state_cb), nmc); + g_signal_connect (device, "notify::state", G_CALLBACK (disconnect_state_cb), nmc); /* Start timer not to loop forever if "notify::state" signal is not issued */ g_timeout_add_seconds (nmc->timeout, timeout_cb, nmc); } @@ -2296,6 +2437,9 @@ do_devices (NmCli *nmc, int argc, char **argv) nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ nmc->return_value = do_devices_show (nmc, argc-1, argv+1); } + else if (matches (*argv, "connect") == 0) { + nmc->return_value = do_device_connect (nmc, argc-1, argv+1); + } else if (matches (*argv, "disconnect") == 0) { nmc->return_value = do_device_disconnect (nmc, argc-1, argv+1); } diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index a3d6647926..e076e0516e 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -41,12 +41,17 @@ - The connection to activate the devices with. + The connection to activate. If "/" is given, a valid device path must + be given, and NetworkManager picks the best connection to activate for + the given device. VPN connections must always pass a valid connection + path. - The object path of device to be activated for physical connections. This parameter is ignored for VPN connections, because the specific_object (if provided) specifies the device to use. + The object path of device to be activated for physical connections. + This parameter is ignored for VPN connections, because the + specific_object (if provided) specifies the device to use. @@ -55,7 +60,7 @@ This parameter is currently ignored for wired and mobile broadband connections, and the value of "/" should be used (ie, no specific object). For WiFi connections, pass the object path of a specific AP from the card's scan - list, or "/" to pick and AP automatically. For VPN connections, pass + list, or "/" to pick an AP automatically. For VPN connections, pass the object path of an ActiveConnection object that should serve as the "base" connection (to which the VPN connections lifetime will be tied), or pass "/" and NM will automatically use the current default device. diff --git a/libnm-glib/nm-client.c b/libnm-glib/nm-client.c index c424037bc0..1ffb8668f7 100644 --- a/libnm-glib/nm-client.c +++ b/libnm-glib/nm-client.c @@ -594,7 +594,7 @@ activate_nm_not_running (gpointer user_data) /** * nm_client_activate_connection: * @client: a #NMClient - * @connection: an #NMConnection + * @connection: (allow-none): an #NMConnection * @device: (allow-none): the #NMDevice * @specific_object: (allow-none): the object path of a connection-type-specific * object this activation should use. This parameter is currently ignored for @@ -613,6 +613,9 @@ activate_nm_not_running (gpointer user_data) * #NMWimaxNsp for WiMAX connections, to which you wish to connect. If the * specific object is not given, NetworkManager can, in some cases, automatically * determine which network to connect to given the settings in @connection. + * + * If @connection is not given for a device-based activation, NetworkManager + * picks the best available connection for the device and activates it. **/ void nm_client_activate_connection (NMClient *client, @@ -628,7 +631,8 @@ nm_client_activate_connection (NMClient *client, g_return_if_fail (NM_IS_CLIENT (client)); if (device) g_return_if_fail (NM_IS_DEVICE (device)); - g_return_if_fail (NM_IS_CONNECTION (connection)); + if (connection) + g_return_if_fail (NM_IS_CONNECTION (connection)); info = g_slice_new0 (ActivateInfo); info->act_fn = callback; @@ -645,7 +649,7 @@ nm_client_activate_connection (NMClient *client, dbus_g_proxy_begin_call (priv->client_proxy, "ActivateConnection", activate_cb, info, NULL, - DBUS_TYPE_G_OBJECT_PATH, nm_connection_get_path (connection), + DBUS_TYPE_G_OBJECT_PATH, connection ? nm_connection_get_path (connection) : "/", DBUS_TYPE_G_OBJECT_PATH, device ? nm_object_get_path (NM_OBJECT (device)) : "/", DBUS_TYPE_G_OBJECT_PATH, specific_object ? specific_object : "/", G_TYPE_INVALID); diff --git a/man/nmcli.1.in b/man/nmcli.1.in index 351453229d..df2e74d872 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -304,14 +304,20 @@ When no command is given to the \fIconnection\fP object, the default action is 'nmcli connection show configured'. .TP .B up [ id | uuid | path ] [ifname ] [ap ] [nsp ] +.RE +.RS +.B up ifname [ap ] [nsp ] +.RS .br Activate a connection. The connection is identified by its name, UUID or D-Bus path. If is ambiguous, a keyword \fIid\fP, \fIuuid\fP or \fIpath\fP can be -used. When requiring a particular device to activate the connection on, the -\fIifname\fP option with interface name should be given. In case of a VPN -connection, the \fIifname\fP option specify the device of the base connection. -The \fIap\fP option specify what particular AP should be used in case of -a Wi\(hyFi connection. +used. When requiring a particular device to activate the connection on, the +\fIifname\fP option with interface name should be given. If the is not +given an \fIifname\fP is required, and NetworkManager will activate the best +available connection for the given \fIifname\fP. In case of a VPN connection, +the \fIifname\fP option specifies the device of the base connection. The +\fIap\fP option specify what particular AP should be used in case of a Wi\(hyFi +connection. .br If '--wait' option is not specified, the default timeout will be 90 seconds. .br @@ -326,6 +332,7 @@ Available options are: .IP \fInsp\fP 13 \(en NSP (Network Service Provider) which the command should connect to (for WiMAX connections) .RE +.RE .TP .B down [ id | uuid | path | apath ] .br @@ -608,7 +615,7 @@ then \fINetworkManager\fP will reload connection files any time they change .B device - show and manage network interfaces .br .TP -.SS \fICOMMAND\fP := { status | show | disconnect | wifi | wimax } +.SS \fICOMMAND\fP := { status | show | connect | disconnect | wifi | wimax } .sp .RS .TP @@ -624,6 +631,13 @@ Show detailed information about devices. Without an argument, all devices are examined. To get information for a specific device, the interface name has to be provided. .TP +.B connect +.br +Connect the device. NetworkManager will try to find a suitable connection that +will be activated. It will also consider connections that are not set to auto connect. +.br +If '--wait' option is not specified, the default timeout will be 90 seconds. +.TP .B disconnect .br Disconnect a device and prevent the device from automatically activating further diff --git a/src/devices/nm-device-bt.c b/src/devices/nm-device-bt.c index 08c0f6dfad..e22c96eee3 100644 --- a/src/devices/nm-device-bt.c +++ b/src/devices/nm-device-bt.c @@ -221,7 +221,9 @@ check_connection_compatible (NMDevice *device, } static gboolean -check_connection_available (NMDevice *device, NMConnection *connection) +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); guint32 bt_type; diff --git a/src/devices/nm-device-wifi.c b/src/devices/nm-device-wifi.c index e4c16613d9..855c1e7a51 100644 --- a/src/devices/nm-device-wifi.c +++ b/src/devices/nm-device-wifi.c @@ -1021,13 +1021,22 @@ check_connection_compatible (NMDevice *device, static gboolean -check_connection_available (NMDevice *device, NMConnection *connection) +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device); NMSettingWireless *s_wifi; const char *mode; GSList *ap_iter = NULL; + if (specific_object) { + NMAccessPoint *ap; + + ap = get_ap_by_path (NM_DEVICE_WIFI (device), specific_object); + return ap ? nm_ap_check_compatible (ap, connection) : FALSE; + } + s_wifi = nm_connection_get_setting_wireless (connection); /* Ad-Hoc and AP connections are always available because they may be diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 660e26a59d..7fda6332a7 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -320,7 +320,9 @@ static gboolean nm_device_set_ip6_config (NMDevice *dev, static gboolean nm_device_activate_ip6_config_commit (gpointer user_data); -static gboolean check_connection_available (NMDevice *device, NMConnection *connection); +static gboolean check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object); static gboolean spec_match_list (NMDevice *device, const GSList *specs); @@ -6613,7 +6615,7 @@ _try_add_available_connection (NMDevice *self, NMConnection *connection) if (nm_device_check_connection_compatible (self, connection, NULL)) { /* Let subclasses implement additional checks on the connection */ if ( NM_DEVICE_GET_CLASS (self)->check_connection_available - && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection)) { + && NM_DEVICE_GET_CLASS (self)->check_connection_available (self, connection, NULL)) { g_hash_table_insert (NM_DEVICE_GET_PRIVATE (self)->available_connections, g_object_ref (connection), @@ -6631,7 +6633,9 @@ _del_available_connection (NMDevice *device, NMConnection *connection) } static gboolean -check_connection_available (NMDevice *device, NMConnection *connection) +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) { /* Default is to assume the connection is available unless a subclass * overrides this with more specific checks. @@ -6658,6 +6662,42 @@ nm_device_recheck_available_connections (NMDevice *device) _signal_available_connections_changed (device); } +/** + * nm_device_get_available_connections: + * @device: the #NMDevice + * @specific_object: a specific object path if any + * + * Returns a list of connections available to activate on the device, taking + * into account any device-specific details given by @specific_object (like + * WiFi access point path). + * + * Returns: caller-owned #GPtrArray of #NMConnections + */ +GPtrArray * +nm_device_get_available_connections (NMDevice *device, const char *specific_object) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + GHashTableIter iter; + guint num_available; + NMConnection *connection = NULL; + GPtrArray *array = NULL; + + num_available = g_hash_table_size (priv->available_connections); + if (num_available > 0) { + array = g_ptr_array_sized_new (num_available); + g_hash_table_iter_init (&iter, priv->available_connections); + while (g_hash_table_iter_next (&iter, (gpointer) &connection, NULL)) { + /* If a specific object is given, only include connections that are + * compatible with it. + */ + if ( !specific_object + || NM_DEVICE_GET_CLASS (device)->check_connection_available (device, connection, specific_object)) + g_ptr_array_add (array, connection); + } + } + return array; +} + static void cp_connection_added (NMConnectionProvider *cp, NMConnection *connection, gpointer user_data) { diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 9993ec5439..b62f45ddc7 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -140,11 +140,13 @@ typedef struct { GError **error); /* Checks whether the connection is likely available to be activated, - * including any live network information like scan lists. Returns - * TRUE if the connection is available; FALSE if not. + * including any live network information like scan lists. The connection + * is checked against the object defined by @specific_object, if given. + * Returns TRUE if the connection is available; FALSE if not. */ gboolean (* check_connection_available) (NMDevice *self, - NMConnection *connection); + NMConnection *connection, + const char *specific_object); gboolean (* complete_connection) (NMDevice *self, NMConnection *connection, @@ -337,6 +339,9 @@ void nm_device_add_pending_action (NMDevice *device, const char *action); void nm_device_remove_pending_action (NMDevice *device, const char *action); gboolean nm_device_has_pending_action (NMDevice *device); +GPtrArray *nm_device_get_available_connections (NMDevice *device, + const char *specific_object); + G_END_DECLS /* For testing only */ diff --git a/src/devices/wimax/nm-device-wimax.c b/src/devices/wimax/nm-device-wimax.c index da92e014c4..0061e79db2 100644 --- a/src/devices/wimax/nm-device-wimax.c +++ b/src/devices/wimax/nm-device-wimax.c @@ -261,6 +261,22 @@ get_nsp_by_name (NMDeviceWimax *self, const char *name) return NULL; } +static NMWimaxNsp * +get_nsp_by_path (NMDeviceWimax *self, const char *path) +{ + NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (self); + GSList *iter; + + for (iter = priv->nsp_list; iter; iter = iter->next) { + NMWimaxNsp *nsp = NM_WIMAX_NSP (iter->data); + + if (!g_strcmp0 (nm_wimax_nsp_get_dbus_path (nsp), path)) + return nsp; + } + + return NULL; +} + static gboolean update_availability (NMDeviceWimax *self, gboolean old_available) { @@ -375,10 +391,18 @@ check_connection_compatible (NMDevice *device, } static gboolean -check_connection_available (NMDevice *device, NMConnection *connection) +check_connection_available (NMDevice *device, + NMConnection *connection, + const char *specific_object) { NMDeviceWimaxPrivate *priv = NM_DEVICE_WIMAX_GET_PRIVATE (device); const GSList *ns_iter = NULL; + NMWimaxNsp *nsp; + + if (specific_object) { + nsp = get_nsp_by_path (NM_DEVICE_WIMAX (device), specific_object); + return nsp ? nm_wimax_nsp_check_compatible (nsp, connection) : FALSE; + } /* Ensure the connection applies to an NSP in the scan list */ for (ns_iter = priv->nsp_list; ns_iter; ns_iter = ns_iter->next) { diff --git a/src/nm-manager.c b/src/nm-manager.c index af663c95ab..9b6a48fac2 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -3110,6 +3110,61 @@ impl_manager_activate_connection (NMManager *self, gboolean is_vpn = FALSE; GError *error = NULL; + /* Normalize object paths */ + if (g_strcmp0 (connection_path, "/") == 0) + connection_path = NULL; + if (g_strcmp0 (specific_object_path, "/") == 0) + specific_object_path = NULL; + if (g_strcmp0 (device_path, "/") == 0) + device_path = NULL; + + /* If the connection path is given and valid, that connection is activated. + * Otherwise the "best" connection for the device is chosen and activated, + * regardless of whether that connection is autoconnect-enabled or not + * (since this is an explicit request, not an auto-activation request). + */ + if (!connection_path) { + GPtrArray *available; + guint64 best_timestamp = 0; + guint i; + + /* If no connection is given, find a suitable connection for the given device path */ + if (!device_path) { + error = g_error_new_literal (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Only devices may be activated without a specifying a connection"); + goto error; + } + device = nm_manager_get_device_by_path (self, device_path); + if (!device) { + error = g_error_new (NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_DEVICE, + "Cannot activate unknown device %s", device_path); + goto error; + } + + available = nm_device_get_available_connections (device, specific_object_path); + for (i = 0; available && i < available->len; i++) { + NMSettingsConnection *candidate = g_ptr_array_index (available, i); + guint64 candidate_timestamp = 0; + + nm_settings_connection_get_timestamp (candidate, &candidate_timestamp); + if (!connection_path || (candidate_timestamp > best_timestamp)) { + connection_path = nm_connection_get_path (NM_CONNECTION (candidate)); + best_timestamp = candidate_timestamp; + } + } + + if (available) + g_ptr_array_free (available, TRUE); + + if (!connection_path) { + error = g_error_new_literal (NM_MANAGER_ERROR, + NM_MANAGER_ERROR_UNKNOWN_CONNECTION, + "The device has no connections available."); + goto error; + } + } + + g_assert (connection_path); connection = (NMConnection *) nm_settings_get_connection_by_path (priv->settings, connection_path); if (!connection) { error = g_error_new_literal (NM_MANAGER_ERROR,