diff --git a/clients/cli/common.c b/clients/cli/common.c index b988679ff9..8317764e3a 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -1011,6 +1011,29 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, } } +char * +nmc_unique_connection_name (const GPtrArray *connections, const char *try_name) +{ + NMConnection *connection; + const char *name; + char *new_name; + unsigned int num = 1; + int i = 0; + + new_name = g_strdup (try_name); + while (i < connections->len) { + connection = NM_CONNECTION (connections->pdata[i]); + + name = nm_connection_get_id (connection); + if (g_strcmp0 (new_name, name) == 0) { + g_free (new_name); + new_name = g_strdup_printf ("%s-%d", try_name, num++); + i = 0; + } else + i++; + } + return new_name; +} /** * nmc_cleanup_readline: diff --git a/clients/cli/common.h b/clients/cli/common.h index bb3f44e851..c7acb653da 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -57,6 +57,9 @@ void nmc_secrets_requested (NMSecretAgentSimple *agent, GPtrArray *secrets, gpointer user_data); +char *nmc_unique_connection_name (const GPtrArray *connections, + const char *try_name); + void nmc_cleanup_readline (void); char *nmc_readline (const char *prompt_fmt, ...) G_GNUC_PRINTF (1, 2); char *nmc_rl_gen_func_basic (const char *text, int state, const char **words); diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 94d0ff73c2..cb95694b07 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -5810,30 +5810,6 @@ cleanup_bridge_slave: return TRUE; } -static char * -unique_connection_name (const GPtrArray *connections, const char *try_name) -{ - NMConnection *connection; - const char *name; - char *new_name; - unsigned int num = 1; - int i = 0; - - new_name = g_strdup (try_name); - while (i < connections->len) { - connection = NM_CONNECTION (connections->pdata[i]); - - name = nm_connection_get_id (connection); - if (g_strcmp0 (new_name, name) == 0) { - g_free (new_name); - new_name = g_strdup_printf ("%s-%d", try_name, num++); - i = 0; - } else - i++; - } - return new_name; -} - typedef struct { NmCli *nmc; char *con_name; @@ -6180,7 +6156,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv) char *try_name = ifname ? g_strdup_printf ("%s-%s", get_name_alias (setting_name, nmc_valid_connection_types), ifname) : g_strdup (get_name_alias (setting_name, nmc_valid_connection_types)); - default_name = unique_connection_name (nmc->connections, try_name); + default_name = nmc_unique_connection_name (nmc->connections, try_name); g_free (try_name); } @@ -9015,8 +8991,8 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) if (con_name) default_name = g_strdup (con_name); else - default_name = unique_connection_name (nmc->connections, - get_name_alias (connection_type, nmc_valid_connection_types)); + default_name = nmc_unique_connection_name (nmc->connections, + get_name_alias (connection_type, nmc_valid_connection_types)); g_object_set (s_con, NM_SETTING_CONNECTION_ID, default_name, diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 5dce08c220..0c38419234 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -267,6 +267,8 @@ usage (void) " wifi [list [ifname ] [bssid ]]\n\n" " wifi connect <(B)SSID> [password ] [wep-key-type key|phrase] [ifname ]\n" " [bssid ] [name ] [private yes|no] [hidden yes|no]\n\n" + " wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ]\n\n" + " [password ] [--show-password]\n\n" " wifi rescan [ifname ] [[ssid ] ...]\n\n" )); } @@ -371,6 +373,21 @@ usage_device_wifi (void) "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" "\n" + "ARGUMENTS := wifi hotspot [ifname ] [con-name ] [ssid ]\n" + " [band a|bg] [channel ] [password ]\n" + " [--show-password]\n" + "\n" + "Create a Wi-Fi hotspot. Use 'connection down' or 'device disconnect'\n" + "to stop the hotspot.\n" + "Parameters of the hotspot can be influenced by the optional parameters:\n" + "ifname - Wi-Fi device to use\n" + "con-name - name of the created hotspot connection profile\n" + "ssid - SSID of the hotspot\n" + "band - Wi-Fi band to use\n" + "channel - Wi-Fi channel to use\n" + "password - password to use for the hotspot\n" + "--show-password - tell nmcli to print password to stdout\n" + "\n" "ARGUMENTS := rescan [ifname ] [[ssid ] ...]\n" "\n" "Request that NetworkManager immediately re-scan for available access points.\n" @@ -1349,6 +1366,7 @@ connected_state_cb (NMDevice *device, NMActiveConnection *active) typedef struct { NmCli *nmc; NMDevice *device; + gboolean hotspot; } AddAndActivateInfo; static void @@ -1366,8 +1384,12 @@ add_and_activate_cb (GObject *client, active = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error); if (error) { - g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), - error->message); + if (info->hotspot) + g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot: %s"), + error->message); + else + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: %s"), + error->message); g_error_free (error); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; quit (); @@ -1375,7 +1397,10 @@ add_and_activate_cb (GObject *client, state = nm_active_connection_get_state (active); if (state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) { - g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); + if (info->hotspot) + g_string_printf (nmc->return_text, _("Error: Failed to setup a Wi-Fi hotspot")); + else + g_string_printf (nmc->return_text, _("Error: Failed to add/activate new connection: Unknown error")); nmc->return_value = NMC_RESULT_ERROR_CON_ACTIVATION; g_object_unref (active); quit (); @@ -1386,8 +1411,12 @@ add_and_activate_cb (GObject *client, if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - g_print (_("Connection with UUID '%s' created and activated on device '%s'\n"), - nm_active_connection_get_uuid (active), nm_device_get_iface (device)); + if (info->hotspot) + 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_object_unref (active); quit (); @@ -1566,6 +1595,7 @@ do_device_connect (NmCli *nmc, int argc, char **argv) info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; info->device = device; + info->hotspot = FALSE; nm_client_activate_connection_async (nmc->client, NULL, /* let NM find a connection automatically */ @@ -2659,6 +2689,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; info->device = device; + info->hotspot = FALSE; nm_client_add_and_activate_connection_async (nmc->client, connection, @@ -2679,6 +2710,342 @@ error: return nmc->return_value; } +static GBytes * +generate_ssid_for_hotspot (const char *ssid) +{ + GBytes *ssid_bytes; + char *hotspot_ssid = NULL; + + if (!ssid) { + hotspot_ssid = g_strdup_printf ("Hotspot-%s", g_get_host_name ()); + if (strlen (hotspot_ssid) > 32) + hotspot_ssid[32] = '\0'; + ssid = hotspot_ssid; + } + ssid_bytes = g_bytes_new (ssid, strlen (ssid)); + g_free (hotspot_ssid); + return ssid_bytes; +} + +#define WPA_PASSKEY_SIZE 8 +static void +generate_wpa_key (char *key, size_t len) +{ + guint i; + + g_return_if_fail (key); + g_return_if_fail (len > WPA_PASSKEY_SIZE); + + /* generate a 8-chars ASCII WPA key */ + for (i = 0; i < WPA_PASSKEY_SIZE; i++) { + int c; + c = g_random_int_range (33, 126); + /* too many non alphanumeric characters are hard to remember for humans */ + while (!g_ascii_isalnum (c)) + c = g_random_int_range (33, 126); + + key[i] = (gchar) c; + } + key[WPA_PASSKEY_SIZE] = '\0'; +} + +static void +generate_wep_key (char *key, size_t len) +{ + int i; + const char *hexdigits = "0123456789abcdef"; + + g_return_if_fail (key); + g_return_if_fail (len > 10); + + /* generate a 10-digit hex WEP key */ + for (i = 0; i < 10; i++) { + int digit; + digit = g_random_int_range (0, 16); + key[i] = hexdigits[digit]; + } + key[10] = '\0'; +} + +static gboolean +set_wireless_security_for_hotspot (NMSettingWirelessSecurity *s_wsec, + const char *wifi_mode, + NMDeviceWifiCapabilities caps, + const char *password, + gboolean show_password, + GError **error) +{ + char generated_key[11]; + const char *key; + const char *key_mgmt; + + if (g_strcmp0 (wifi_mode, NM_SETTING_WIRELESS_MODE_AP) == 0) { + if (caps & NM_WIFI_DEVICE_CAP_RSN) { + nm_setting_wireless_security_add_proto (s_wsec, "rsn"); + nm_setting_wireless_security_add_pairwise (s_wsec, "ccmp"); + nm_setting_wireless_security_add_group (s_wsec, "ccmp"); + key_mgmt = "wpa-psk"; + } else if (caps & NM_WIFI_DEVICE_CAP_WPA) { + nm_setting_wireless_security_add_proto (s_wsec, "wpa"); + nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"); + nm_setting_wireless_security_add_group (s_wsec, "tkip"); + key_mgmt = "wpa-psk"; + } else + key_mgmt = "none"; + } else + key_mgmt = "none"; + + if (g_strcmp0 (key_mgmt, "wpa-psk") == 0) { + /* use WPA */ + if (password) { + if (!nm_utils_wpa_psk_valid (password)) { + g_set_error (error, NMCLI_ERROR, 0, _("'%s' is not valid WPA PSK"), password); + return FALSE; + } + key = password; + } else { + generate_wpa_key (generated_key, sizeof (generated_key)); + key = generated_key; + } + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, + NM_SETTING_WIRELESS_SECURITY_PSK, key, + NULL); + } else { + /* use WEP */ + if (password) { + if (!nm_utils_wep_key_valid (password, NM_WEP_KEY_TYPE_KEY)) { + g_set_error (error, NMCLI_ERROR, 0, + _("'%s' is not valid WEP key (it should be 5 or 13 ASCII chars)"), + password); + return FALSE; + } + key = password; + } else { + generate_wep_key (generated_key, sizeof (generated_key)); + key = generated_key; + } + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, key_mgmt, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, key, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, + NULL); + } + if (show_password) + g_print (_("Hotspot password: %s\n"), key); + + return TRUE; +} + +static NMCResultCode +do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) +{ + AddAndActivateInfo *info; + const char *ifname = NULL; + const char *con_name = NULL; + char *default_name = NULL; + const char *ssid = NULL; + const char *wifi_mode; + const char *band = NULL; + const char *channel = NULL; + unsigned long channel_int; + const char *password = NULL; + gboolean show_password = FALSE; + NMDevice *device = NULL; + int devices_idx; + const GPtrArray *devices; + NMDeviceWifiCapabilities caps; + NMConnection *connection = NULL; + NMSettingConnection *s_con; + NMSettingWireless *s_wifi; + NMSettingWirelessSecurity *s_wsec; + NMSettingIPConfig *s_ip4, *s_ip6; + GBytes *ssid_bytes; + GError *error = NULL; + + /* Set default timeout waiting for operation completion. */ + if (nmc->timeout == -1) + nmc->timeout = 60; + + while (argc > 0) { + if (strcmp (*argv, "ifname") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + ifname = *argv; + } else if (strcmp (*argv, "con-name") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + con_name = *argv; + } else if (strcmp (*argv, "ssid") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + ssid = *argv; + if (strlen (ssid) > 32) { + g_string_printf (nmc->return_text, _("Error: ssid is too long.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "band") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + band = *argv; + if (strcmp (band, "a") && strcmp (band, "bg")) { + g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."), + band); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } else if (strcmp (*argv, "channel") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + channel = *argv; + } else if (strcmp (*argv, "password") == 0) { + if (next_arg (&argc, &argv) != 0) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + password = *argv; + } else if (nmc_arg_is_option (*argv, "show-password")) { + show_password = TRUE; + } else { + g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + + argc--; + argv++; + } + + /* Verify band and channel parameters */ + if (!channel) { + if (g_strcmp0 (band, "bg") == 0) + channel = "1"; + if (g_strcmp0 (band, "a") == 0) + channel = "7"; + } + if (channel) { + if (!band) { + g_string_printf (nmc->return_text, _("Error: channel requires band too.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + if ( !nmc_string_to_uint (channel, TRUE, 1, 5825, &channel_int) + || !nm_utils_wifi_is_channel_valid (channel_int, band)) { + g_string_printf (nmc->return_text, _("Error: channel '%s' not valid for band '%s'."), + channel, band); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + /* Find Wi-Fi device. When no ifname is provided, the first Wi-Fi is used. */ + devices = nm_client_get_devices (nmc->client); + devices_idx = 0; + device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + + if (!device) { + if (ifname) + g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); + else + g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* Check device supported mode */ + caps = nm_device_wifi_get_capabilities (NM_DEVICE_WIFI (device)); + if (caps & NM_WIFI_DEVICE_CAP_AP) + wifi_mode = NM_SETTING_WIRELESS_MODE_AP; + else if (caps & NM_WIFI_DEVICE_CAP_ADHOC) + wifi_mode = NM_SETTING_WIRELESS_MODE_ADHOC; + else { + g_string_printf (nmc->return_text, _("Error: Device '%s' supports neither AP nor Ad-Hoc mode."), + nm_device_get_iface (device)); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + goto error; + } + + /* Create a connection with appropriate parameters */ + connection = nm_simple_connection_new (); + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + if (!con_name) + con_name = default_name = nmc_unique_connection_name (nm_client_get_connections (nmc->client), "Hotspot"); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, con_name, + NM_SETTING_CONNECTION_AUTOCONNECT, FALSE, + NULL); + g_free (default_name); + + s_wifi = (NMSettingWireless *) nm_setting_wireless_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wifi)); + ssid_bytes = generate_ssid_for_hotspot (ssid); + g_object_set (s_wifi, NM_SETTING_WIRELESS_MODE, wifi_mode, + NM_SETTING_WIRELESS_SSID, ssid_bytes, + NULL); + g_bytes_unref (ssid_bytes); + if (channel) + g_object_set (s_wifi, + NM_SETTING_WIRELESS_CHANNEL, (guint32) channel_int, + NM_SETTING_WIRELESS_BAND, band, + NULL); + + s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wsec)); + if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, show_password, &error)) { + g_object_unref (connection); + g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_clear_error (&error); + goto error; + } + + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_SHARED, NULL); + + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + g_object_set (s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); + + /* Activate the connection now */ + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + info = g_malloc0 (sizeof (AddAndActivateInfo)); + info->nmc = nmc; + info->device = device; + info->hotspot = TRUE; + + nm_client_add_and_activate_connection_async (nmc->client, + connection, + device, + NULL, + NULL, + add_and_activate_cb, + info); + +error: + return nmc->return_value; +} + static void request_rescan_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -2791,6 +3158,8 @@ do_device_wifi (NmCli *nmc, int argc, char **argv) nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); } else if (matches (*argv, "connect") == 0) { nmc->return_value = do_device_wifi_connect_network (nmc, argc-1, argv+1); + } else if (matches (*argv, "hotspot") == 0) { + nmc->return_value = do_device_wifi_hotspot (nmc, argc-1, argv+1); } else if (matches (*argv, "rescan") == 0) { nmc->return_value = do_device_wifi_rescan (nmc, argc-1, argv+1); } else { diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index 6d9b6ae1cb..02364c65cd 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -512,6 +512,7 @@ _nmcli_compl_ARGS() save| \ hidden| \ private) + show-password) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "yes no" return 0 @@ -589,6 +590,12 @@ _nmcli_compl_ARGS() return 0 fi ;; + band) + if [[ "${#words[@]}" -eq 2 ]]; then + _nmcli_list "a bg" + return 0 + fi + ;; *) return 1 ;; @@ -1317,7 +1324,7 @@ _nmcli() ;; w|wi|wif|wifi) if [[ ${#words[@]} -eq 3 ]]; then - _nmcli_compl_COMMAND "${words[2]}" list connect rescan + _nmcli_compl_COMMAND "${words[2]}" list connect hotspot rescan else case "${words[2]}" in l|li|lis|list) @@ -1338,6 +1345,11 @@ _nmcli() _nmcli_compl_ARGS fi ;; + h|ho|hot|hots|hotsp|hotspo|hotspot) + _nmcli_array_delete_at words 0 2 + OPTIONS=(ifname con-name ssid band channel password show-password) + _nmcli_compl_ARGS + ;; r|re|res|resc|resca|rescan) _nmcli_array_delete_at words 0 2 OPTIONS_REPEATABLE=(ssid) diff --git a/man/nmcli.1.in b/man/nmcli.1.in index a54023d936..0b4d90a5bf 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -853,6 +853,36 @@ Otherwise the connection is system\(hywide, which is the default. Otherwise the SSID would not be found and the connection attempt would fail. .RE .TP +.B wifi hotspot [ifname ] [con-name ] [ssid ] [band a|bg] [channel ] +.B [password ] [--show-password] +.br +Create a Wi-Fi hotspot. The command creates a hotspot connection profile according to +Wi-Fi device capabilities and activates it on the device. The hotspot is secured with WPA +if device/driver supports that, otherwise WEP is used. +Use \fIconnection down\fP or \fIdevice disconnect\fP to stop the hotspot. +.br +.RS +.PP +Parameters of the hotspot can be influenced by the optional parameters: +.IP \fIifname\fP 17 +\(en what Wi-Fi device is used +.IP \fIcon-name\fP 17 +\(en name of the created hotspot connection profile +.IP \fIssid\fP 17 +\(en SSID of the hotspot +.IP \fIband\fP 17 +\(en Wi-Fi band to use +.IP \fIchannel\fP 17 +\(en Wi-Fi channel to use +.IP \fIpassword\fP 17 +\(en password to use for the created hotspot. If not provided, +nmcli will generate a password. The password is either WPA +pre-shared key or WEP key. +.IP \fI--show-password\fP 17 +\(en tell nmcli to print the password to stdout. It is useful +when the user did not provide his own password. +.RE +.TP .B wifi rescan [ifname ] [[ssid ] ...] .br Request that \fINetworkManager\fP immediately re-scan for available access points. @@ -1040,6 +1070,11 @@ using password "caffeine". This is mainly useful when connecting to "Cafe Hotspo the first time. Next time, it is better to use 'nmcli con up id "My cafe"' so that the existing connection profile can be used and no additional is created. +.IP "\fB\f(CWnmcli dev wifi hotspot -s con-name QuickHotspot\fP\fP" +.IP +creates a hotspot profile and connects it. Prints the hotspot password the user should use +to connect to the hotspot from other devices. + .IP "\fB\f(CWnmcli connection add type ethernet autoconnect no ifname eth0\fP\fP" .IP non-interactively adds an Ethernet connection tied to eth0 interface with automatic IP configuration (DHCP),