cli: reuse connections in nmcli dev wifi con

Try to locate an existing connection before creating a new one when
handling "nmcli device wifi connect".  This allows WPA-Enterprise
networks to be activated this way, consistent with the comment that this
command is equivalent to clicking on an SSID in a GUI client.
This commit is contained in:
Andrew Zaborowski
2018-06-12 06:52:01 +02:00
committed by Andrew Zaborowski
parent 0c70a9ef6d
commit 3593237527
2 changed files with 139 additions and 65 deletions

View File

@@ -847,14 +847,13 @@ usage_device_wifi (void)
"ARGUMENTS := connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n" "ARGUMENTS := connect <(B)SSID> [password <password>] [wep-key-type key|phrase] [ifname <ifname>]\n"
" [bssid <BSSID>] [name <name>] [private yes|no] [hidden yes|no]\n" " [bssid <BSSID>] [name <name>] [private yes|no] [hidden yes|no]\n"
"\n" "\n"
"Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n" "Connect to a Wi-Fi network specified by SSID or BSSID. The command finds a\n"
"a new connection and then activates it on a device. This is a command-line\n" "matching connection or creates one and then activates it on a device. This\n"
"counterpart of clicking an SSID in a GUI client. The command always creates\n" "is a command-line counterpart of clicking an SSID in a GUI client. If a\n"
"a new connection and thus it is mainly useful for connecting to new Wi-Fi\n" "connection for the network already exists, it is possible to bring up the\n"
"networks. If a connection for the network already exists, it is better to\n" "existing profile as follows: nmcli con up id <name>. Note that only open,\n"
"bring up the existing profile as follows: nmcli con up id <name>. Note that\n" "WEP and WPA-PSK networks are supported if no previous connection exists.\n"
"only open, WEP and WPA-PSK networks are supported at the moment. It is also\n" "It is also assumed that IP configuration is obtained via DHCP.\n"
"assumed that IP configuration is obtained via DHCP.\n"
"\n" "\n"
"ARGUMENTS := hotspot [ifname <ifname>] [con-name <name>] [ssid <SSID>]\n" "ARGUMENTS := hotspot [ifname <ifname>] [con-name <name>] [ssid <SSID>]\n"
" [band a|bg] [channel <channel>] [password <password>]\n" " [band a|bg] [channel <channel>] [password <password>]\n"
@@ -1839,6 +1838,7 @@ typedef struct {
NmCli *nmc; NmCli *nmc;
NMDevice *device; NMDevice *device;
gboolean hotspot; gboolean hotspot;
gboolean create;
} AddAndActivateInfo; } AddAndActivateInfo;
static void static void
@@ -1853,15 +1853,21 @@ add_and_activate_cb (GObject *client,
NMActiveConnection *active; NMActiveConnection *active;
GError *error = NULL; GError *error = NULL;
active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error); if (info->create)
active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error);
else
active = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
if (error) { if (error) {
if (info->hotspot) if (info->hotspot)
g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"), g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"),
error->message); error->message);
else else if (info->create)
g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"),
error->message); error->message);
else
g_string_printf (nmc->return_text, _("Error: Failed to activate connection: %s"),
error->message);
g_error_free (error); g_error_free (error);
nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
quit (); quit ();
@@ -1871,8 +1877,10 @@ add_and_activate_cb (GObject *client,
if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
if (info->hotspot) if (info->hotspot)
g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot")); g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot"));
else else if (info->create)
g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error"));
else
g_string_printf (nmc->return_text, _("Error: Failed to activate connection: Unknown error"));
nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
g_object_unref (active); g_object_unref (active);
quit (); quit ();
@@ -1883,12 +1891,16 @@ add_and_activate_cb (GObject *client,
if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY) if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
nmc_terminal_erase_line (); nmc_terminal_erase_line ();
if (!info->hotspot) if (info->hotspot)
g_print (_("Hotspot '%s' activated on device '%s'\n"),
nm_active_connection_get_id (active), nm_device_get_iface (device));
else if (info->create)
g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"), g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"),
nm_active_connection_get_uuid (active), nm_device_get_iface (device)); nm_active_connection_get_uuid (active), nm_device_get_iface (device));
else else
g_print (_("Hotspot '%s' activated on device '%s'\n"), g_print (_("Connection with ID '%s', UUID '%s' activated on device '%s'\n"),
nm_active_connection_get_id (active), nm_device_get_iface (device)); nm_active_connection_get_id (active), nm_active_connection_get_uuid (active),
nm_device_get_iface (device));
} }
g_object_unref (active); g_object_unref (active);
quit (); quit ();
@@ -1947,6 +1959,7 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
if (error) { if (error) {
/* If no connection existed for the device, create one and activate it */ /* If no connection existed for the device, create one and activate it */
if (g_error_matches (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION)) { if (g_error_matches (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION)) {
info->create = TRUE;
create_connect_connection_for_device (info); create_connect_connection_for_device (info);
return; return;
} }
@@ -3139,7 +3152,6 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
NMConnection *connection = NULL; NMConnection *connection = NULL;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingWireless *s_wifi; NMSettingWireless *s_wifi;
NMSettingWirelessSecurity *s_wsec;
AddAndActivateInfo *info; AddAndActivateInfo *info;
const char *param_user = NULL; const char *param_user = NULL;
const char *ifname = NULL; const char *ifname = NULL;
@@ -3155,6 +3167,10 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
int devices_idx; int devices_idx;
char *ssid_ask = NULL; char *ssid_ask = NULL;
char *passwd_ask = NULL; char *passwd_ask = NULL;
const GPtrArray *avail_cons;
gboolean name_match = FALSE;
gboolean existing_con = FALSE;
int i;
/* Set default timeout waiting for operation completion. */ /* Set default timeout waiting for operation completion. */
if (nmc->timeout == -1) if (nmc->timeout == -1)
@@ -3384,45 +3400,75 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
goto finish; goto finish;
} }
/* If there are some connection data from user, create a connection and avail_cons = nm_device_get_available_connections (device);
* fill them into proper settings. */ for (i = 0; i < avail_cons->len; i++) {
if (con_name || private || bssid2_arr || password || hidden) NMRemoteConnection *avail_con = g_ptr_array_index (avail_cons, i);
connection = nm_simple_connection_new (); const char *id = nm_connection_get_id (NM_CONNECTION (avail_con));
if (con_name || private) { if (con_name) {
s_con = (NMSettingConnection *) nm_setting_connection_new (); if (!id || strcmp (id, con_name))
nm_connection_add_setting (connection, NM_SETTING (s_con)); continue;
/* Set user provided connection name */ name_match = TRUE;
if (con_name) }
g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
/* Connection will only be visible to this user when 'private' is specified */ if (nm_access_point_connection_valid (ap, NM_CONNECTION (avail_con))) {
if (private) /* ap has been checked against bssid1, bssid2 and the ssid
nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL); * and now avail_con has been checked against ap.
*/
connection = NM_CONNECTION (avail_con);
existing_con = TRUE;
break;
}
} }
if (bssid2_arr || hidden) {
s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wifi));
/* 'bssid' parameter is used to restrict the connection only to the BSSID */ if (name_match && !existing_con) {
if (bssid2_arr) g_string_printf (nmc->return_text, _("Error: Connection '%s' exists but properties don't match."), con_name);
g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
goto finish;
}
/* 'hidden' parameter is used to indicate that SSID is not broadcasted */ if (!existing_con) {
if (hidden) { /* If there are some connection data from user, create a connection and
GBytes *ssid = g_bytes_new (param_user, strlen (param_user)); * fill them into proper settings. */
if (con_name || private || bssid2_arr || password || hidden)
connection = nm_simple_connection_new ();
g_object_set (s_wifi, if (con_name || private) {
NM_SETTING_WIRELESS_SSID, ssid, s_con = (NMSettingConnection *) nm_setting_connection_new ();
NM_SETTING_WIRELESS_HIDDEN, hidden, nm_connection_add_setting (connection, NM_SETTING (s_con));
NULL);
g_bytes_unref (ssid);
/* Warn when the provided AP identifier looks like BSSID instead of SSID */ /* Set user provided connection name */
if (bssid1_arr) if (con_name)
g_printerr (_("Warning: '%s' should be SSID for hidden APs; but it looks like a BSSID.\n"), g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
param_user);
/* Connection will only be visible to this user when 'private' is specified */
if (private)
nm_setting_connection_add_permission (s_con, "user", g_get_user_name (), NULL);
}
if (bssid2_arr || hidden) {
s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wifi));
/* 'bssid' parameter is used to restrict the connection only to the BSSID */
if (bssid2_arr)
g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL);
/* 'hidden' parameter is used to indicate that SSID is not broadcasted */
if (hidden) {
GBytes *ssid = g_bytes_new (param_user, strlen (param_user));
g_object_set (s_wifi,
NM_SETTING_WIRELESS_SSID, ssid,
NM_SETTING_WIRELESS_HIDDEN, hidden,
NULL);
g_bytes_unref (ssid);
/* Warn when the provided AP identifier looks like BSSID instead of SSID */
if (bssid1_arr)
g_printerr (_("Warning: '%s' should be SSID for hidden APs; but it looks like a BSSID.\n"),
param_user);
}
} }
} }
@@ -3435,8 +3481,25 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY) if ( (ap_flags & NM_802_11_AP_FLAGS_PRIVACY)
|| ap_wpa_flags != NM_802_11_AP_SEC_NONE || ap_wpa_flags != NM_802_11_AP_SEC_NONE
|| ap_rsn_flags != NM_802_11_AP_SEC_NONE) { || ap_rsn_flags != NM_802_11_AP_SEC_NONE) {
const char *con_password = NULL;
NMSettingWirelessSecurity *s_wsec = NULL;
if (connection) {
s_wsec = nm_connection_get_setting_wireless_security (connection);
if (s_wsec) {
if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) {
/* WEP */
con_password = nm_setting_wireless_security_get_wep_key (s_wsec, 0);
} else if ( (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)
|| (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) {
/* WPA PSK */
con_password = nm_setting_wireless_security_get_psk (s_wsec);
}
}
}
/* Ask for missing password when one is expected and '--ask' is used */ /* Ask for missing password when one is expected and '--ask' is used */
if (!password && nmc->ask) { if (!password && !con_password && nmc->ask) {
password = passwd_ask = nmc_readline_echo (&nmc->nmc_config, password = passwd_ask = nmc_readline_echo (&nmc->nmc_config,
nmc->nmc_config.show_secrets, nmc->nmc_config.show_secrets,
_("Password: ")); _("Password: "));
@@ -3445,8 +3508,10 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
if (password) { if (password) {
if (!connection) if (!connection)
connection = nm_simple_connection_new (); connection = nm_simple_connection_new ();
s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); if (!s_wsec) {
nm_connection_add_setting (connection, NM_SETTING (s_wsec)); s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wsec));
}
if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) { if (ap_wpa_flags == NM_802_11_AP_SEC_NONE && ap_rsn_flags == NM_802_11_AP_SEC_NONE) {
/* WEP */ /* WEP */
@@ -3462,7 +3527,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
} }
} }
} }
// FIXME: WPA-Enterprise is not supported yet. // FIXME: Creating WPA-Enterprise connections is not supported yet.
// We are not able to determine and fill all the parameters for // We are not able to determine and fill all the parameters for
// 802.1X authentication automatically without user providing // 802.1X authentication automatically without user providing
// the data. Adding nmcli options for the 8021x setting would // the data. Adding nmcli options for the 8021x setting would
@@ -3480,14 +3545,24 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
info->nmc = nmc; info->nmc = nmc;
info->device = device; info->device = device;
info->hotspot = FALSE; info->hotspot = FALSE;
info->create = !existing_con;
nm_client_add_and_activate_connection_async (nmc->client, if (existing_con) {
connection, nm_client_activate_connection_async (nmc->client,
device, connection,
nm_object_get_path (NM_OBJECT (ap)), device,
NULL, nm_object_get_path (NM_OBJECT (ap)),
add_and_activate_cb, NULL,
info); add_and_activate_cb,
info);
} else {
nm_client_add_and_activate_connection_async (nmc->client,
connection,
device,
nm_object_get_path (NM_OBJECT (ap)),
NULL,
add_and_activate_cb,
info);
}
finish: finish:
if (bssid1_arr) if (bssid1_arr)

View File

@@ -1445,14 +1445,13 @@
<listitem> <listitem>
<para>Connect to a Wi-Fi network specified by SSID or BSSID. The command <para>Connect to a Wi-Fi network specified by SSID or BSSID. The command
creates a new connection and then activates it on a device. This is a finds a matching connnection or creates one and then activates it on a device.
command-line counterpart of clicking an SSID in a GUI client. The command This is a command-line counterpart of clicking an SSID in a GUI client. If
always creates a new connection and thus it is mainly useful for connecting to a connection for the network already exists, it is possible to bring up
new Wi-Fi networks. If a connection for the network already exists, it is (activate) the existing profile as follows:
better to bring up (activate) the existing connection as follows:
<command>nmcli con up id <replaceable>name</replaceable></command>. Note that <command>nmcli con up id <replaceable>name</replaceable></command>. Note that
only open, WEP and WPA-PSK networks are supported at the moment. It is also only open, WEP and WPA-PSK networks are supported if no previous connection
supposed that IP configuration is obtained via DHCP.</para> exists. It is also assumed that IP configuration is obtained via DHCP.</para>
<para>If <option>--wait</option> option is not specified, the default timeout will be 90 <para>If <option>--wait</option> option is not specified, the default timeout will be 90
seconds.</para> seconds.</para>