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"
" [bssid <BSSID>] [name <name>] [private yes|no] [hidden yes|no]\n"
"\n"
"Connect to a Wi-Fi network specified by SSID or BSSID. The command creates\n"
"a new connection and then activates it on a device. This is a command-line\n"
"counterpart of clicking an SSID in a GUI client. The command always creates\n"
"a new connection and thus it is mainly useful for connecting to new Wi-Fi\n"
"networks. If a connection for the network already exists, it is better to\n"
"bring up the existing profile as follows: nmcli con up id <name>. Note that\n"
"only open, WEP and WPA-PSK networks are supported at the moment. It is also\n"
"assumed that IP configuration is obtained via DHCP.\n"
"Connect to a Wi-Fi network specified by SSID or BSSID. The command finds a\n"
"matching connection or creates one and then activates it on a device. This\n"
"is a command-line counterpart of clicking an SSID in a GUI client. If a\n"
"connection for the network already exists, it is possible to bring up the\n"
"existing profile as follows: nmcli con up id <name>. Note that only open,\n"
"WEP and WPA-PSK networks are supported if no previous connection exists.\n"
"It is also assumed that IP configuration is obtained via DHCP.\n"
"\n"
"ARGUMENTS := hotspot [ifname <ifname>] [con-name <name>] [ssid <SSID>]\n"
" [band a|bg] [channel <channel>] [password <password>]\n"
@@ -1839,6 +1838,7 @@ typedef struct {
NmCli *nmc;
NMDevice *device;
gboolean hotspot;
gboolean create;
} AddAndActivateInfo;
static void
@@ -1853,15 +1853,21 @@ add_and_activate_cb (GObject *client,
NMActiveConnection *active;
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 (info->hotspot)
g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"),
error->message);
else
else if (info->create)
g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"),
error->message);
else
g_string_printf (nmc->return_text, _("Error: Failed to activate connection: %s"),
error->message);
g_error_free (error);
nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
quit ();
@@ -1871,8 +1877,10 @@ add_and_activate_cb (GObject *client,
if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
if (info->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"));
else
g_string_printf (nmc->return_text, _("Error: Failed to activate connection: Unknown error"));
nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION;
g_object_unref (active);
quit ();
@@ -1883,12 +1891,16 @@ add_and_activate_cb (GObject *client,
if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
if (nmc->nmc_config.print_output == NMC_PRINT_PRETTY)
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"),
nm_active_connection_get_uuid (active), nm_device_get_iface (device));
else
g_print (_("Hotspot '%s' activated on device '%s'\n"),
nm_active_connection_get_id (active), nm_device_get_iface (device));
g_print (_("Connection with ID '%s', UUID '%s' activated on device '%s'\n"),
nm_active_connection_get_id (active), nm_active_connection_get_uuid (active),
nm_device_get_iface (device));
}
g_object_unref (active);
quit ();
@@ -1947,6 +1959,7 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
if (error) {
/* 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)) {
info->create = TRUE;
create_connect_connection_for_device (info);
return;
}
@@ -3139,7 +3152,6 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
NMConnection *connection = NULL;
NMSettingConnection *s_con;
NMSettingWireless *s_wifi;
NMSettingWirelessSecurity *s_wsec;
AddAndActivateInfo *info;
const char *param_user = NULL;
const char *ifname = NULL;
@@ -3155,6 +3167,10 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
int devices_idx;
char *ssid_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. */
if (nmc->timeout == -1)
@@ -3384,45 +3400,75 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
goto finish;
}
/* If there are some connection data from user, create a connection and
* fill them into proper settings. */
if (con_name || private || bssid2_arr || password || hidden)
connection = nm_simple_connection_new ();
avail_cons = nm_device_get_available_connections (device);
for (i = 0; i < avail_cons->len; i++) {
NMRemoteConnection *avail_con = g_ptr_array_index (avail_cons, i);
const char *id = nm_connection_get_id (NM_CONNECTION (avail_con));
if (con_name || private) {
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_connection_add_setting (connection, NM_SETTING (s_con));
if (con_name) {
if (!id || strcmp (id, con_name))
continue;
/* Set user provided connection name */
if (con_name)
g_object_set (s_con, NM_SETTING_CONNECTION_ID, con_name, NULL);
name_match = TRUE;
}
/* 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 (nm_access_point_connection_valid (ap, NM_CONNECTION (avail_con))) {
/* ap has been checked against bssid1, bssid2 and the ssid
* 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 (bssid2_arr)
g_object_set (s_wifi, NM_SETTING_WIRELESS_BSSID, bssid2_arr, NULL);
if (name_match && !existing_con) {
g_string_printf (nmc->return_text, _("Error: Connection '%s' exists but properties don't match."), con_name);
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
goto finish;
}
/* 'hidden' parameter is used to indicate that SSID is not broadcasted */
if (hidden) {
GBytes *ssid = g_bytes_new (param_user, strlen (param_user));
if (!existing_con) {
/* If there are some connection data from user, create a connection and
* fill them into proper settings. */
if (con_name || private || bssid2_arr || password || hidden)
connection = nm_simple_connection_new ();
g_object_set (s_wifi,
NM_SETTING_WIRELESS_SSID, ssid,
NM_SETTING_WIRELESS_HIDDEN, hidden,
NULL);
g_bytes_unref (ssid);
if (con_name || private) {
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_connection_add_setting (connection, NM_SETTING (s_con));
/* 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);
/* Set user provided connection name */
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 (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)
|| ap_wpa_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 */
if (!password && nmc->ask) {
if (!password && !con_password && nmc->ask) {
password = passwd_ask = nmc_readline_echo (&nmc->nmc_config,
nmc->nmc_config.show_secrets,
_("Password: "));
@@ -3445,8 +3508,10 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv)
if (password) {
if (!connection)
connection = nm_simple_connection_new ();
s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wsec));
if (!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) {
/* 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
// 802.1X authentication automatically without user providing
// 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->device = device;
info->hotspot = FALSE;
nm_client_add_and_activate_connection_async (nmc->client,
connection,
device,
nm_object_get_path (NM_OBJECT (ap)),
NULL,
add_and_activate_cb,
info);
info->create = !existing_con;
if (existing_con) {
nm_client_activate_connection_async (nmc->client,
connection,
device,
nm_object_get_path (NM_OBJECT (ap)),
NULL,
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:
if (bssid1_arr)

View File

@@ -1445,14 +1445,13 @@
<listitem>
<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
command-line counterpart of clicking an SSID in a GUI client. The command
always creates a new connection and thus it is mainly useful for connecting to
new Wi-Fi networks. If a connection for the network already exists, it is
better to bring up (activate) the existing connection as follows:
finds a matching connnection or creates one and then activates it on a device.
This is a command-line counterpart of clicking an SSID in a GUI client. If
a connection for the network already exists, it is possible to bring up
(activate) the existing profile as follows:
<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
supposed that IP configuration is obtained via DHCP.</para>
only open, WEP and WPA-PSK networks are supported if no previous connection
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
seconds.</para>