diff options
| author | Michael Biebl <biebl@debian.org> | 2018-06-17 14:54:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2018-06-17 14:54:10 +0200 |
| commit | 069cb5c3a525ebcc19cc2927964258acaca87b13 (patch) | |
| tree | 66ac6b21a44d630c0fc281c0df327239d491226a /clients/cli | |
| parent | 04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (diff) | |
New upstream version 1.11.90 upstream/1.11.90
Diffstat (limited to 'clients/cli')
| -rw-r--r-- | clients/cli/common.c | 21 | ||||
| -rw-r--r-- | clients/cli/connections.c | 1264 | ||||
| -rw-r--r-- | clients/cli/connections.h | 4 | ||||
| -rw-r--r-- | clients/cli/devices.c | 437 | ||||
| -rw-r--r-- | clients/cli/general.c | 3 | ||||
| -rw-r--r-- | clients/cli/nmcli.c | 4 | ||||
| -rw-r--r-- | clients/cli/nmcli.h | 8 | ||||
| -rw-r--r-- | clients/cli/utils.c | 40 | ||||
| -rw-r--r-- | clients/cli/utils.h | 31 |
9 files changed, 1124 insertions, 688 deletions
diff --git a/clients/cli/common.c b/clients/cli/common.c index e566de47..09c86334 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -49,6 +49,9 @@ _ip_config_get_routes (NMIPConfig *cfg) if (!ptr_array) return NULL; + if (ptr_array->len == 0) + return NULL; + arr = g_new (char *, ptr_array->len + 1); for (i = 0; i < ptr_array->len; i++) { NMIPRoute *route = g_ptr_array_index (ptr_array, i); @@ -384,7 +387,7 @@ print_dhcp_config (NMDhcpConfig *dhcp, /* * nmc_find_connection: * @connections: array of NMConnections to search in - * @filter_type: "id", "uuid", "path" or %NULL + * @filter_type: "id", "uuid", "path", "filename", or %NULL * @filter_val: connection to find (connection name, UUID or path) * @out_result: if not NULL, attach all matching connection to this * list. If necessary, a new array will be allocated. If the array @@ -452,6 +455,14 @@ nmc_find_connection (const GPtrArray *connections, goto found; } + if (NM_IN_STRSET (filter_type, NULL, "filename")) { + v = nm_remote_connection_get_filename (NM_REMOTE_CONNECTION (connections->pdata[i])); + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, v, NULL); + if (nm_streq0 (filter_val, v)) + goto found; + } + continue; found: if (!out_result) @@ -523,6 +534,14 @@ nmc_find_active_connection (const GPtrArray *active_cons, goto found; } + if (NM_IN_STRSET (filter_type, NULL, "filename")) { + v = nm_remote_connection_get_filename (con); + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, v, NULL); + if (nm_streq0 (filter_val, v)) + goto found; + } + if (NM_IN_STRSET (filter_type, NULL, "apath")) { v = nm_object_get_path (NM_OBJECT (candidate)); v_num = nm_utils_dbus_path_get_last_component (v); diff --git a/clients/cli/connections.c b/clients/cli/connections.c index a4f129b6..1563178d 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -101,27 +101,57 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (vpn_connection_state_to_string, NMVpnConnecti * prefers an alias instead of the settings name when in pretty print mode. * That is so that we print "wifi" instead of "802-11-wireless" in "nmcli c". */ static const char * -connection_type_pretty (const char *type, NMCPrintOutput print_output) +connection_type_to_display (const char *type, NMMetaAccessorGetType get_type) { const NMMetaSettingInfoEditor *editor; int i; - if (print_output == NMC_PRINT_TERSE) + nm_assert (NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_PRETTY, NM_META_ACCESSOR_GET_TYPE_PARSABLE)); + + if (!type) + return NULL; + + if (get_type != NM_META_ACCESSOR_GET_TYPE_PRETTY) return type; for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { editor = &nm_meta_setting_infos_editor[i]; - if (strcmp (type, editor->general->setting_name) == 0) { - if (editor->alias) - return editor->alias; - break; - } + if (nm_streq (type, editor->general->setting_name)) + return editor->alias ?: type; } - return type; } -/* Caller has to free the returned string */ +static int +active_connection_get_state_ord (NMActiveConnection *active) +{ + /* returns an integer related to @active's state, that can be used for sorting + * active connections based on their activation state. */ + if (!active) + return -2; + + switch (nm_active_connection_get_state (active)) { + case NM_ACTIVE_CONNECTION_STATE_UNKNOWN: return 0; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATED: return 1; + case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING: return 2; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATING: return 3; + case NM_ACTIVE_CONNECTION_STATE_ACTIVATED: return 4; + } + return -1; +} + +static int +active_connection_cmp (NMActiveConnection *ac_a, NMActiveConnection *ac_b) +{ + NM_CMP_SELF (ac_a, ac_b); + NM_CMP_DIRECT (active_connection_get_state_ord (ac_b), + active_connection_get_state_ord (ac_a)); + NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_id (ac_a), nm_active_connection_get_id (ac_b)); + NM_CMP_DIRECT_STRCMP0 (nm_active_connection_get_connection_type (ac_a), nm_active_connection_get_connection_type (ac_b)); + NM_CMP_DIRECT_STRCMP0 (nm_object_get_path (NM_OBJECT (ac_a)), nm_object_get_path (NM_OBJECT (ac_b))); + return 0; +} + static char * get_ac_device_string (NMActiveConnection *active) { @@ -152,41 +182,451 @@ get_ac_device_string (NMActiveConnection *active) /*****************************************************************************/ -const NmcMetaGenericInfo *const nmc_fields_con_show[] = { - NMC_META_GENERIC ("NAME"), /* 0 */ - NMC_META_GENERIC ("UUID"), /* 1 */ - NMC_META_GENERIC ("TYPE"), /* 2 */ - NMC_META_GENERIC ("TIMESTAMP"), /* 3 */ - NMC_META_GENERIC ("TIMESTAMP-REAL"), /* 4 */ - NMC_META_GENERIC ("AUTOCONNECT"), /* 5 */ - NMC_META_GENERIC ("AUTOCONNECT-PRIORITY"), /* 6 */ - NMC_META_GENERIC ("READONLY"), /* 7 */ - NMC_META_GENERIC ("DBUS-PATH"), /* 8 */ - NMC_META_GENERIC ("ACTIVE"), /* 9 */ - NMC_META_GENERIC ("DEVICE"), /* 10 */ - NMC_META_GENERIC ("STATE"), /* 11 */ - NMC_META_GENERIC ("ACTIVE-PATH"), /* 12 */ - NMC_META_GENERIC ("SLAVE"), /* 13 */ - NULL, +/* FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c), + * and in gnome-control-center as well. It could probably be shared somehow. */ + +static char * +get_vpn_connection_type (NMConnection *connection) +{ + const char *type, *p; + + /* The service type is in form of "org.freedesktop.NetworkManager.vpnc". + * Extract end part after last dot, e.g. "vpnc" + */ + type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); + p = strrchr (type, '.'); + return g_strdup (p ? p + 1 : type); +} + +/* VPN parameters can be found at: + * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h + * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h + * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h + * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h + * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h + * See also 'properties' directory in these plugins. + */ +static const gchar * +find_vpn_gateway_key (const char *vpn_type) +{ + if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote"; + if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway"; + if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; + if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; + if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right"; + if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway"; + return ""; +} + +static const gchar * +find_vpn_username_key (const char *vpn_type) +{ + if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username"; + if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username"; + if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; + if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; + if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername"; + if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user"; + return ""; +} + +enum VpnDataItem { + VPN_DATA_ITEM_GATEWAY, + VPN_DATA_ITEM_USERNAME +}; + +static const gchar * +get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) +{ + const char *key; + gs_free char *type = NULL; + + type = get_vpn_connection_type (connection); + + switch (vpn_data_item) { + case VPN_DATA_ITEM_GATEWAY: + key = find_vpn_gateway_key (type); + break; + case VPN_DATA_ITEM_USERNAME: + key = find_vpn_username_key (type); + break; + default: + key = ""; + break; + } + + return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key); +} + +/*****************************************************************************/ + +typedef struct { + NMConnection *connection; + NMActiveConnection *primary_active; + GPtrArray *all_active; + bool show_active_fields; +} MetagenConShowRowData; + +static MetagenConShowRowData * +_metagen_con_show_row_data_new_for_connection (NMRemoteConnection *connection, gboolean show_active_fields) +{ + MetagenConShowRowData *row_data; + + row_data = g_slice_new0 (MetagenConShowRowData); + row_data->connection = g_object_ref (NM_CONNECTION (connection)); + row_data->show_active_fields = show_active_fields; + return row_data; +} + +static MetagenConShowRowData * +_metagen_con_show_row_data_new_for_active_connection (NMRemoteConnection *connection, NMActiveConnection *active, gboolean show_active_fields) +{ + MetagenConShowRowData *row_data; + + row_data = g_slice_new0 (MetagenConShowRowData); + if (connection) + row_data->connection = g_object_ref (NM_CONNECTION (connection)); + row_data->primary_active = g_object_ref (active); + row_data->show_active_fields = show_active_fields; + return row_data; +} + +static void +_metagen_con_show_row_data_add_active_connection (MetagenConShowRowData *row_data, NMActiveConnection *active) +{ + if (!row_data->primary_active) { + row_data->primary_active = g_object_ref (active); + return; + } + if (!row_data->all_active) { + row_data->all_active = g_ptr_array_new_with_free_func (g_object_unref); + g_ptr_array_add (row_data->all_active, g_object_ref (row_data->primary_active)); + } + g_ptr_array_add (row_data->all_active, g_object_ref (active)); +} + +static void +_metagen_con_show_row_data_init_primary_active (MetagenConShowRowData *row_data) +{ + NMActiveConnection *ac, *best_ac; + guint i; + + if (!row_data->all_active) + return; + + best_ac = row_data->all_active->pdata[0]; + for (i = 1; i < row_data->all_active->len; i++) { + ac = row_data->all_active->pdata[i]; + + if (active_connection_get_state_ord (ac) > active_connection_get_state_ord (best_ac)) + best_ac = ac; + } + + if (row_data->primary_active != best_ac) { + g_object_unref (row_data->primary_active); + row_data->primary_active = g_object_ref (best_ac); + } + g_clear_pointer (&row_data->all_active, g_ptr_array_unref); +} + +static void +_metagen_con_show_row_data_destroy (gpointer data) +{ + MetagenConShowRowData *row_data = data; + + if (!row_data) + return; + + g_clear_object (&row_data->connection); + g_clear_object (&row_data->primary_active); + g_clear_pointer (&row_data->all_active, g_ptr_array_unref); + g_slice_free (MetagenConShowRowData, row_data); +} + +static const char * +_con_show_fcn_get_id (NMConnection *c, NMActiveConnection *ac) +{ + NMSettingConnection *s_con = NULL; + const char *s; + + if (c) + s_con = nm_connection_get_setting_connection (c); + + s = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (!s && ac) { + /* note that if we have no s_con, that usually means that the user has no permissions + * to see the connection. We still fall to get the ID from the active-connection, + * which exposes it despite the user having no permissions. + * + * That might be unexpected, because the user is shown an ID, which he later + * is unable to resolve in other operations. */ + s = nm_active_connection_get_id (ac); + } + return s; +} + +static const char * +_con_show_fcn_get_type (NMConnection *c, NMActiveConnection *ac, NMMetaAccessorGetType get_type) +{ + NMSettingConnection *s_con = NULL; + const char *s; + + if (c) + s_con = nm_connection_get_setting_connection (c); + + s = s_con ? nm_setting_connection_get_connection_type (s_con) : NULL; + if (!s && ac) { + /* see _con_show_fcn_get_id() for why we fallback to get the value + * from @ac. */ + s = nm_active_connection_get_connection_type (ac); + } + return connection_type_to_display (s, get_type); +} + +static gconstpointer +_metagen_con_show_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS) +{ + const MetagenConShowRowData *row_data = target; + NMConnection *c = row_data->connection; + NMActiveConnection *ac = row_data->primary_active; + NMSettingConnection *s_con = NULL; + const char *s; + char *s_mut; + + NMC_HANDLE_COLOR ( ac + ? nmc_active_connection_state_to_color (nm_active_connection_get_state (ac)) + : NM_META_COLOR_CONNECTION_UNKNOWN); + + if (c) + s_con = nm_connection_get_setting_connection (c); + + if (!row_data->show_active_fields) { + /* we are not supposed to show any fields of the active connection. + * We only tracked the primary_active to get the coloring right. + * From now on, there is no active connection. */ + ac = NULL; + + /* in this mode, we expect that we are called only with connections that + * have a [connection] setting and a UUID. Otherwise, the connection is + * effectively invisible to the user, and should be hidden. + * + * But in that case, we expect that the caller pre-filtered this row out. + * So assert(). */ + nm_assert (s_con); + nm_assert (nm_setting_connection_get_uuid (s_con)); + } + + nm_assert (NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_PRETTY, NM_META_ACCESSOR_GET_TYPE_PARSABLE)); + + switch (info->info_type) { + case NMC_GENERIC_INFO_TYPE_CON_SHOW_NAME: + return _con_show_fcn_get_id (c, ac); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_UUID: + s = s_con ? nm_setting_connection_get_uuid (s_con) : NULL; + if (!s && ac) { + /* see _con_show_fcn_get_id() for why we fallback to get the value + * from @ac. */ + s = nm_active_connection_get_uuid (ac); + } + return s; + case NMC_GENERIC_INFO_TYPE_CON_SHOW_TYPE: + return _con_show_fcn_get_type (c, ac, get_type); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP: + case NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP_REAL: + if (!s_con) + return NULL; + { + guint64 timestamp; + time_t timestamp_real; + + timestamp = nm_setting_connection_get_timestamp (s_con); + + if (info->info_type == NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP) + return (*out_to_free = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp)); + else { + if (!timestamp) { + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + return _("never"); + return "never"; + } + timestamp_real = timestamp; + s_mut = g_malloc0 (128); + strftime (s_mut, 64, "%c", localtime (×tamp_real)); + return (*out_to_free = s_mut); + } + } + case NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT: + if (!s_con) + return NULL; + return nmc_meta_generic_get_bool (nm_setting_connection_get_autoconnect (s_con), get_type); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT_PRIORITY: + if (!s_con) + return NULL; + return (*out_to_free = g_strdup_printf ("%d", nm_setting_connection_get_autoconnect_priority (s_con))); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_READONLY: + if (!s_con) + return NULL; + return nmc_meta_generic_get_bool (nm_setting_connection_get_read_only (s_con), get_type); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_DBUS_PATH: + if (!c) + return NULL; + return nm_connection_get_path (c); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE: + return nmc_meta_generic_get_bool (!!ac, get_type); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_DEVICE: + if (ac) + return (*out_to_free = get_ac_device_string (ac)); + return NULL; + case NMC_GENERIC_INFO_TYPE_CON_SHOW_STATE: + return nmc_meta_generic_get_str_i18n (ac + ? active_connection_state_to_string (nm_active_connection_get_state (ac)) + : NULL, + get_type); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE_PATH: + if (ac) + return nm_object_get_path (NM_OBJECT (ac)); + return NULL; + case NMC_GENERIC_INFO_TYPE_CON_SHOW_SLAVE: + if (!s_con) + return NULL; + return nm_setting_connection_get_slave_type (s_con); + case NMC_GENERIC_INFO_TYPE_CON_SHOW_FILENAME: + if (!NM_IS_REMOTE_CONNECTION (c)) + return NULL; + return nm_remote_connection_get_filename (NM_REMOTE_CONNECTION (c)); + default: + break; + } + + g_return_val_if_reached (NULL); +} + +const NmcMetaGenericInfo *const metagen_con_show[_NMC_GENERIC_INFO_TYPE_CON_SHOW_NUM + 1] = { +#define _METAGEN_CON_SHOW(type, name) \ + [type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_con_show_get_fcn) + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_NAME, "NAME"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_UUID, "UUID"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_TYPE, "TYPE"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP, "TIMESTAMP"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP_REAL, "TIMESTAMP-REAL"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT, "AUTOCONNECT"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT_PRIORITY, "AUTOCONNECT-PRIORITY"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_READONLY, "READONLY"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_DBUS_PATH, "DBUS-PATH"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE, "ACTIVE"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_DEVICE, "DEVICE"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_STATE, "STATE"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE_PATH, "ACTIVE-PATH"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_SLAVE, "SLAVE"), + _METAGEN_CON_SHOW (NMC_GENERIC_INFO_TYPE_CON_SHOW_FILENAME, "FILENAME"), }; #define NMC_FIELDS_CON_SHOW_COMMON "NAME,UUID,TYPE,DEVICE" -const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[] = { - NMC_META_GENERIC ("GROUP"), /* 0 */ - NMC_META_GENERIC ("NAME"), /* 1 */ - NMC_META_GENERIC ("UUID"), /* 2 */ - NMC_META_GENERIC ("DEVICES"), /* 3 */ - NMC_META_GENERIC ("STATE"), /* 4 */ - NMC_META_GENERIC ("DEFAULT"), /* 5 */ - NMC_META_GENERIC ("DEFAULT6"), /* 6 */ - NMC_META_GENERIC ("SPEC-OBJECT"), /* 7 */ - NMC_META_GENERIC ("VPN"), /* 8 */ - NMC_META_GENERIC ("DBUS-PATH"), /* 9 */ - NMC_META_GENERIC ("CON-PATH"), /* 10 */ - NMC_META_GENERIC ("ZONE"), /* 11 */ - NMC_META_GENERIC ("MASTER-PATH"), /* 12 */ - NULL, +/*****************************************************************************/ + +static gconstpointer +_metagen_con_active_general_get_fcn (NMC_META_GENERIC_INFO_GET_FCN_ARGS) +{ + NMActiveConnection *ac = target; + NMConnection *c; + NMSettingConnection *s_con = NULL; + NMDevice *dev; + guint i; + const char *s; + + NMC_HANDLE_COLOR (NM_META_COLOR_NONE); + + nm_assert (NM_IN_SET (get_type, NM_META_ACCESSOR_GET_TYPE_PRETTY, NM_META_ACCESSOR_GET_TYPE_PARSABLE)); + + c = NM_CONNECTION (nm_active_connection_get_connection (ac)); + if (c) + s_con = nm_connection_get_setting_connection (c); + + switch (info->info_type) { + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NAME: + return nm_active_connection_get_id (ac); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_UUID: + return nm_active_connection_get_uuid (ac); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEVICES: + { + GString *str = NULL; + const GPtrArray *devices; + + s = NULL; + devices = nm_active_connection_get_devices (ac); + if (devices) { + for (i = 0; i < devices->len; i++) { + NMDevice *device = devices->pdata[i]; + const char *iface; + + iface = nm_device_get_iface (device); + if (!iface) + continue; + if (!s) { + s = iface; + continue; + } + if (!str) + str = g_string_new (s); + g_string_append_c (str, ','); + g_string_append (str, iface); + } + } + if (str) + return (*out_to_free = g_string_free (str, FALSE)); + return s; + } + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_STATE: + return nmc_meta_generic_get_str_i18n (active_connection_state_to_string (nm_active_connection_get_state (ac)), + get_type); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT: + return nmc_meta_generic_get_bool (nm_active_connection_get_default (ac), get_type); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT6: + return nmc_meta_generic_get_bool (nm_active_connection_get_default6 (ac), get_type); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_SPEC_OBJECT: + return nm_active_connection_get_specific_object_path (ac); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_VPN: + return nmc_meta_generic_get_bool (NM_IS_VPN_CONNECTION (ac), get_type); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DBUS_PATH: + return nm_object_get_path (NM_OBJECT (ac)); + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CON_PATH: + return c ? nm_connection_get_path (c) : NULL; + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_ZONE: + /* this is really ugly, because the zone is not a property of the active-connection, + * but the settings-connection profile. There is no guarantee, that they agree. */ + return s_con ? nm_setting_connection_get_zone (s_con) : NULL; + case NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH: + dev = nm_active_connection_get_master (ac); + return dev ? nm_object_get_path (NM_OBJECT (dev)) : NULL; + default: + break; + } + + g_return_val_if_reached (NULL); +} + +const NmcMetaGenericInfo *const metagen_con_active_general[_NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NUM + 1] = { +#define _METAGEN_CON_ACTIVE_GENERAL(type, name) \ + [type] = NMC_META_GENERIC(name, .info_type = type, .get_fcn = _metagen_con_active_general_get_fcn) + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NAME, "NAME"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_UUID, "UUID"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEVICES, "DEVICES"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_STATE, "STATE"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT, "DEFAULT"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT6, "DEFAULT6"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_SPEC_OBJECT, "SPEC-OBJECT"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_VPN, "VPN"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DBUS_PATH, "DBUS-PATH"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CON_PATH, "CON-PATH"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_ZONE, "ZONE"), + _METAGEN_CON_ACTIVE_GENERAL (NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH, "MASTER-PATH"), }; + +/*****************************************************************************/ + #define NMC_FIELDS_SETTINGS_NAMES_ALL NM_SETTING_CONNECTION_SETTING_NAME","\ NM_SETTING_WIRED_SETTING_NAME","\ NM_SETTING_802_1X_SETTING_NAME","\ @@ -237,7 +677,7 @@ const NmcMetaGenericInfo *const nmc_fields_con_active_details_vpn[] = { }; const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[] = { - NMC_META_GENERIC_WITH_NESTED ("GENERAL", nmc_fields_con_active_details_general + 1), /* 0 */ + NMC_META_GENERIC_WITH_NESTED ("GENERAL", metagen_con_active_general), /* 0 */ NMC_META_GENERIC_WITH_NESTED ("IP4", metagen_ip4_config), /* 1 */ NMC_META_GENERIC_WITH_NESTED ("DHCP4", nmc_fields_dhcp_config + 1), /* 2 */ NMC_META_GENERIC_WITH_NESTED ("IP6", nmc_fields_ip6_config + 1), /* 3 */ @@ -595,36 +1035,42 @@ construct_header_name (const char *base, const char *spec) return g_strdup_printf ("%s (%s)", base, spec); } +static int +get_ac_for_connection_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + NMActiveConnection *ac_a = *((NMActiveConnection *const*) pa); + NMActiveConnection *ac_b = *((NMActiveConnection *const*) pb); + + return active_connection_cmp (ac_a, ac_b); +} + static NMActiveConnection * get_ac_for_connection (const GPtrArray *active_cons, NMConnection *connection, GPtrArray **out_result) { - const char *con_path, *ac_con_path; guint i; NMActiveConnection *best_candidate = NULL; GPtrArray *result = out_result ? *out_result : NULL; - con_path = nm_connection_get_path (connection); for (i = 0; i < active_cons->len; i++) { NMActiveConnection *candidate = g_ptr_array_index (active_cons, i); NMRemoteConnection *con; con = nm_active_connection_get_connection (candidate); - if (NM_CONNECTION (con) != connection) { - /* also compare the D-Bus paths. Why? I don't know. */ - ac_con_path = con ? nm_connection_get_path (NM_CONNECTION (con)) : NULL; - if (!nm_streq0 (ac_con_path, con_path)) - continue; - } + if (NM_CONNECTION (con) != connection) + continue; if (!out_result) return candidate; - if (!best_candidate) - best_candidate = candidate; if (!result) result = g_ptr_array_new_with_free_func (g_object_unref); g_ptr_array_add (result, g_object_ref (candidate)); } + if (result) { + g_ptr_array_sort_with_data (result, get_ac_for_connection_cmp, NULL); + best_candidate = result->pdata[0]; + } + NM_SET_OUT (out_result, result); return best_candidate; } @@ -767,178 +1213,6 @@ nmc_active_connection_state_to_color (NMActiveConnectionState state) return NM_META_COLOR_CONNECTION_UNKNOWN; } -static void -fill_output_connection (NMConnection *connection, NMClient *client, NMCPrintOutput print_output, - GPtrArray *output_data, gboolean active_only) -{ - NMSettingConnection *s_con; - guint64 timestamp; - time_t timestamp_real; - char *timestamp_str; - char *timestamp_real_str = ""; - char *prio_str; - NmcOutputField *arr; - NMActiveConnection *ac = NULL; - const char *ac_path = NULL; - const char *ac_state = NULL; - NMActiveConnectionState ac_state_int = NM_ACTIVE_CONNECTION_STATE_UNKNOWN; - char *ac_dev = NULL; - NMMetaColor color; - - s_con = nm_connection_get_setting_connection (connection); - g_assert (s_con); - - ac = get_ac_for_connection (nm_client_get_active_connections (client), connection, NULL); - if (active_only && !ac) - return; - - if (ac) { - ac_path = nm_object_get_path (NM_OBJECT (ac)); - ac_state_int = nm_active_connection_get_state (ac); - ac_state = gettext (active_connection_state_to_string (ac_state_int)); - ac_dev = get_ac_device_string (ac); - } - - /* Obtain field values */ - timestamp = nm_setting_connection_get_timestamp (s_con); - timestamp_str = g_strdup_printf ("%" G_GUINT64_FORMAT, timestamp); - if (timestamp) { - timestamp_real = timestamp; - timestamp_real_str = g_malloc0 (64); - strftime (timestamp_real_str, 64, "%c", localtime (×tamp_real)); - } - prio_str = g_strdup_printf ("%u", nm_setting_connection_get_autoconnect_priority (s_con)); - - arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_con_show, 0); - - /* Show active connections in color */ - color = nmc_active_connection_state_to_color (ac_state_int); - set_val_color_all (arr, color); - - set_val_strc (arr, 0, nm_setting_connection_get_id (s_con)); - set_val_strc (arr, 1, nm_setting_connection_get_uuid (s_con)); - set_val_strc (arr, 2, connection_type_pretty (nm_setting_connection_get_connection_type (s_con), print_output)); - set_val_str (arr, 3, timestamp_str); - set_val_str (arr, 4, timestamp ? timestamp_real_str : g_strdup (_("never"))); - set_val_strc (arr, 5, nm_setting_connection_get_autoconnect (s_con) ? _("yes") : _("no")); - set_val_str (arr, 6, prio_str); - set_val_strc (arr, 7, nm_setting_connection_get_read_only (s_con) ? _("yes") : _("no")); - set_val_strc (arr, 8, nm_connection_get_path (connection)); - set_val_strc (arr, 9, ac ? _("yes") : _("no")); - set_val_str (arr, 10, ac_dev); - set_val_strc (arr, 11, ac_state); - set_val_strc (arr, 12, ac_path); - set_val_strc (arr, 13, nm_setting_connection_get_slave_type (s_con)); - - g_ptr_array_add (output_data, arr); -} - -static void -fill_output_connection_for_invisible (NMActiveConnection *ac, NMCPrintOutput print_output, GPtrArray *output_data) -{ - NmcOutputField *arr; - const char *ac_path = NULL; - const char *ac_state = NULL; - char *name, *ac_dev = NULL; - - name = g_strdup_printf ("<invisible> %s", nm_active_connection_get_id (ac)); - ac_path = nm_object_get_path (NM_OBJECT (ac)); - ac_state = active_connection_state_to_string (nm_active_connection_get_state (ac)); - ac_dev = get_ac_device_string (ac); - - arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_con_show, 0); - - set_val_str (arr, 0, name); - set_val_strc (arr, 1, nm_active_connection_get_uuid (ac)); - set_val_strc (arr, 2, connection_type_pretty (nm_active_connection_get_connection_type (ac), print_output)); - set_val_strc (arr, 3, NULL); - set_val_strc (arr, 4, NULL); - set_val_strc (arr, 5, NULL); - set_val_strc (arr, 6, NULL); - set_val_strc (arr, 7, NULL); - set_val_strc (arr, 8, NULL); - set_val_strc (arr, 9, _("yes")); - set_val_str (arr, 10, ac_dev); - set_val_strc (arr, 11, ac_state); - set_val_strc (arr, 12, ac_path); - set_val_strc (arr, 13, NULL); - - set_val_color_all (arr, NM_META_COLOR_CONNECTION_INVISIBLE); - - g_ptr_array_add (output_data, arr); -} - -static void -fill_output_active_connection (NMActiveConnection *active, - GPtrArray *output_data, - gboolean with_group, - guint32 o_flags) -{ - NMRemoteConnection *con; - NMSettingConnection *s_con = NULL; - const GPtrArray *devices; - GString *dev_str; - NMActiveConnectionState state; - NMDevice *master; - const char *con_path = NULL, *con_zone = NULL; - int i; - const NMMetaAbstractInfo *const*tmpl; - NmcOutputField *arr; - int idx_start = with_group ? 0 : 1; - - con = nm_active_connection_get_connection (active); - if (con) { - con_path = nm_connection_get_path (NM_CONNECTION (con)); - s_con = nm_connection_get_setting_connection (NM_CONNECTION (con)); - g_assert (s_con); - con_zone = nm_setting_connection_get_zone (s_con); - } - - state = nm_active_connection_get_state (active); - master = nm_active_connection_get_master (active); - - /* Get devices of the active connection */ - dev_str = g_string_new (NULL); - devices = nm_active_connection_get_devices (active); - for (i = 0; i < devices->len; i++) { - NMDevice *device = g_ptr_array_index (devices, i); - const char *dev_iface = nm_device_get_iface (device); - - if (dev_iface) { - g_string_append (dev_str, dev_iface); - g_string_append_c (dev_str, ','); - } - } - if (dev_str->len > 0) - g_string_truncate (dev_str, dev_str->len - 1); /* Cut off last ',' */ - - tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_general; - if (!with_group) - tmpl++; - - /* Fill field values */ - arr = nmc_dup_fields_array (tmpl, o_flags); - if (with_group) - set_val_strc (arr, 0, nmc_fields_con_active_details_groups[0]->name); - set_val_strc (arr, 1-idx_start, nm_active_connection_get_id (active)); - set_val_strc (arr, 2-idx_start, nm_active_connection_get_uuid (active)); - set_val_str (arr, 3-idx_start, dev_str->str); - set_val_strc (arr, 4-idx_start, active_connection_state_to_string (state)); - set_val_strc (arr, 5-idx_start, nm_active_connection_get_default (active) ? _("yes") : _("no")); - set_val_strc (arr, 6-idx_start, nm_active_connection_get_default6 (active) ? _("yes") : _("no")); - set_val_strc (arr, 7-idx_start, nm_active_connection_get_specific_object_path (active)); - set_val_strc (arr, 8-idx_start, NM_IS_VPN_CONNECTION (active) ? _("yes") : _("no")); - set_val_strc (arr, 9-idx_start, nm_object_get_path (NM_OBJECT (active))); - set_val_strc (arr, 10-idx_start, con_path); - set_val_strc (arr, 11-idx_start, con_zone); - set_val_strc (arr, 12-idx_start, master ? nm_object_get_path (NM_OBJECT (master)) : NULL); - set_val_strc (arr, 13-idx_start, s_con ? nm_setting_connection_get_slave_type (s_con) : NULL); - - g_ptr_array_add (output_data, arr); - - g_string_free (dev_str, FALSE); -} - typedef struct { char **array; guint32 idx; @@ -952,85 +1226,6 @@ fill_vpn_data_item (const char *key, const char *value, gpointer user_data) info->array[info->idx++] = g_strdup_printf ("%s = %s", key, value); } -// FIXME: The same or similar code for VPN info appears also in nm-applet (applet-dialogs.c), -// and in gnome-control-center as well. It could probably be shared somehow. -static char * -get_vpn_connection_type (NMConnection *connection) -{ - const char *type, *p; - - /* The service type is in form of "org.freedesktop.NetworkManager.vpnc". - * Extract end part after last dot, e.g. "vpnc" - */ - type = nm_setting_vpn_get_service_type (nm_connection_get_setting_vpn (connection)); - p = strrchr (type, '.'); - return g_strdup (p ? p + 1 : type); -} - -/* VPN parameters can be found at: - * http://git.gnome.org/browse/network-manager-openvpn/tree/src/nm-openvpn-service.h - * http://git.gnome.org/browse/network-manager-vpnc/tree/src/nm-vpnc-service.h - * http://git.gnome.org/browse/network-manager-pptp/tree/src/nm-pptp-service.h - * http://git.gnome.org/browse/network-manager-openconnect/tree/src/nm-openconnect-service.h - * http://git.gnome.org/browse/network-manager-openswan/tree/src/nm-openswan-service.h - * See also 'properties' directory in these plugins. - */ -static const gchar * -find_vpn_gateway_key (const char *vpn_type) -{ - if (g_strcmp0 (vpn_type, "openvpn") == 0) return "remote"; - if (g_strcmp0 (vpn_type, "vpnc") == 0) return "IPSec gateway"; - if (g_strcmp0 (vpn_type, "pptp") == 0) return "gateway"; - if (g_strcmp0 (vpn_type, "openconnect") == 0) return "gateway"; - if (g_strcmp0 (vpn_type, "openswan") == 0) return "right"; - if (g_strcmp0 (vpn_type, "libreswan") == 0) return "right"; - if (g_strcmp0 (vpn_type, "ssh") == 0) return "remote"; - if (g_strcmp0 (vpn_type, "l2tp") == 0) return "gateway"; - return ""; -} - -static const gchar * -find_vpn_username_key (const char *vpn_type) -{ - if (g_strcmp0 (vpn_type, "openvpn") == 0) return "username"; - if (g_strcmp0 (vpn_type, "vpnc") == 0) return "Xauth username"; - if (g_strcmp0 (vpn_type, "pptp") == 0) return "user"; - if (g_strcmp0 (vpn_type, "openconnect") == 0) return "username"; - if (g_strcmp0 (vpn_type, "openswan") == 0) return "leftxauthusername"; - if (g_strcmp0 (vpn_type, "libreswan") == 0) return "leftxauthusername"; - if (g_strcmp0 (vpn_type, "l2tp") == 0) return "user"; - return ""; -} - -enum VpnDataItem { - VPN_DATA_ITEM_GATEWAY, - VPN_DATA_ITEM_USERNAME -}; - -static const gchar * -get_vpn_data_item (NMConnection *connection, enum VpnDataItem vpn_data_item) -{ - const char *key; - gs_free char *type = NULL; - - type = get_vpn_connection_type (connection); - - switch (vpn_data_item) { - case VPN_DATA_ITEM_GATEWAY: - key = find_vpn_gateway_key (type); - break; - case VPN_DATA_ITEM_USERNAME: - key = find_vpn_username_key (type); - break; - default: - key = ""; - break; - } - - return nm_setting_vpn_get_data_item (nm_connection_get_setting_vpn (connection), key); -} -/* FIXME end */ - static gboolean nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) { @@ -1083,29 +1278,27 @@ nmc_active_connection_details (NMActiveConnection *acon, NmCli *nmc) int group_idx = g_array_index (print_groups, int, i); char *group_fld = (char *) g_ptr_array_index (group_fields, i); - if (nmc->nmc_config.print_output != NMC_PRINT_TERSE && !nmc->nmc_config.multiline_output && was_output) - g_print ("\n"); /* Empty line */ + if ( nmc->nmc_config.print_output != NMC_PRINT_TERSE + && !nmc->nmc_config.multiline_output + && was_output) + g_print ("\n"); was_output = FALSE; - /* GENERAL */ - if (strcasecmp (nmc_fields_con_active_details_groups[group_idx]->name, nmc_fields_con_active_details_groups[0]->name) == 0) { - NMC_OUTPUT_DATA_DEFINE_SCOPED (out); - - /* Add field names */ - tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_general; - out_indices = parse_output_fields (group_fld, - tmpl, FALSE, NULL, NULL); - arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_FIELD_NAMES); - g_ptr_array_add (out.output_data, arr); - - /* Fill in values */ - fill_output_active_connection (acon, out.output_data, TRUE, NMC_OF_FLAG_SECTION_PREFIX); + if (nmc_fields_con_active_details_groups[group_idx]->nested == metagen_con_active_general) { + gs_free char *f = NULL; - print_data_prepare_width (out.output_data); - print_data (&nmc->nmc_config, out_indices, NULL, 0, &out); + if (group_fld) + f = g_strdup_printf ("GENERAL.%s", group_fld); + nmc_print (&nmc->nmc_config, + (gpointer[]) { acon, NULL }, + NULL, + NMC_META_GENERIC_GROUP ("GENERAL", metagen_con_active_general, N_("GROUP")), + f, + NULL); was_output = TRUE; + continue; } /* IP4 */ @@ -1347,154 +1540,220 @@ typedef enum { typedef struct { NmCli *nmc; const GArray *order; -} NmcSortInfo; + gboolean show_active_fields; +} ConShowSortInfo; static int -compare_connections (gconstpointer a, gconstpointer b, gpointer user_data) -{ - NMConnection *ca = *(NMConnection **) a; - NMConnection *cb = *(NMConnection **) b; - const NmcSortInfo *info = user_data; - NMActiveConnection *aca, *acb; - const NmcSortOrder *order_arr; - guint i, order_len; - const char *tmp1, *tmp2; - unsigned long tmp1_int, tmp2_int; - - if (info->order) { - order_arr = &g_array_index (info->order, NmcSortOrder, 0); - order_len = info->order->len; - } else { - static const NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH }; +con_show_get_items_cmp (gconstpointer pa, gconstpointer pb, gpointer user_data) +{ + const ConShowSortInfo *sort_info = user_data; + const MetagenConShowRowData *row_data_a = *((const MetagenConShowRowData *const*) pa); + const MetagenConShowRowData *row_data_b = *((const MetagenConShowRowData *const*) pb); + NMConnection *c_a = row_data_a->connection; + NMConnection *c_b = row_data_b->connection; + NMActiveConnection *ac_a = row_data_a->primary_active; + NMActiveConnection *ac_b = row_data_b->primary_active; + NMActiveConnection *ac_a_effective = sort_info->show_active_fields ? ac_a : NULL; + NMActiveConnection *ac_b_effective = sort_info->show_active_fields ? ac_b : NULL; + + /* first sort active-connections which are invisible, i.e. that have no connection */ + if (!c_a && c_b) + return -1; + if (!c_b && c_a) + return 1; + + /* we have two connections... */ + if (c_a && c_b && c_a != c_b) { + const NmcSortOrder *order_arr; + guint i, order_len; + NMMetaAccessorGetType get_type = nmc_print_output_to_accessor_get_type (sort_info->nmc->nmc_config.print_output); + + if (sort_info->order) { + order_arr = &g_array_index (sort_info->order, NmcSortOrder, 0); + order_len = sort_info->order->len; + } else { + static const NmcSortOrder def[] = { NMC_SORT_ACTIVE, NMC_SORT_NAME, NMC_SORT_PATH }; - order_arr = def; - order_len = G_N_ELEMENTS (def); - } + /* Note: the default order does not consider whether a column is shown. + * That means, the selection of the output fields, does not affect the + * order (although there could be an argument that it should). */ + order_arr = def; + order_len = G_N_ELEMENTS (def); + } - for (i = 0; i < order_len; i++) { - NmcSortOrder item = order_arr[i]; - int cmp = 0; + for (i = 0; i < order_len; i++) { + NmcSortOrder item = order_arr[i]; - switch (item) { - case NMC_SORT_ACTIVE: - case NMC_SORT_ACTIVE_INV: - aca = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), ca, NULL); - acb = get_ac_for_connection (nm_client_get_active_connections (info->nmc->client), cb, NULL); - cmp = (aca && !acb) ? -1 : (!aca && acb) ? 1 : 0; - if (item == NMC_SORT_ACTIVE_INV) - cmp = -(cmp); - break; - case NMC_SORT_TYPE: - case NMC_SORT_TYPE_INV: - cmp = g_strcmp0 (nm_connection_get_connection_type (ca), - nm_connection_get_connection_type (cb)); - if (item == NMC_SORT_TYPE_INV) - cmp = -(cmp); - break; - case NMC_SORT_NAME: - case NMC_SORT_NAME_INV: - cmp = g_strcmp0 (nm_connection_get_id (ca), - nm_connection_get_id (cb)); - if (item == NMC_SORT_NAME_INV) - cmp = -(cmp); - break; - case NMC_SORT_PATH: - case NMC_SORT_PATH_INV: - tmp1 = nm_connection_get_path (ca); - tmp2 = nm_connection_get_path (cb); - tmp1 = tmp1 ? strrchr (tmp1, '/') : "0"; - tmp2 = tmp2 ? strrchr (tmp2, '/') : "0"; - nmc_string_to_uint (tmp1 ? tmp1+1 : "0", FALSE, 0, 0, &tmp1_int); - nmc_string_to_uint (tmp2 ? tmp2+1 : "0", FALSE, 0, 0, &tmp2_int); - cmp = (int) tmp1_int - tmp2_int; - if (item == NMC_SORT_PATH_INV) - cmp = -(cmp); - break; - default: - cmp = 0; - break; - } - if (cmp != 0) - return cmp; - } + switch (item) { - return 0; -} + case NMC_SORT_ACTIVE: + NM_CMP_DIRECT (active_connection_get_state_ord (ac_b), + active_connection_get_state_ord (ac_a)); + break; + case NMC_SORT_ACTIVE_INV: + NM_CMP_DIRECT (active_connection_get_state_ord (ac_a), + active_connection_get_state_ord (ac_b)); + break; -static GPtrArray * -sort_connections (const GPtrArray *cons, NmCli *nmc, const GArray *order) -{ - GPtrArray *sorted; - int i; - NmcSortInfo compare_info; + case NMC_SORT_TYPE: + NM_CMP_DIRECT_STRCMP0 (_con_show_fcn_get_type (c_a, ac_a_effective, get_type), + _con_show_fcn_get_type (c_b, ac_b_effective, get_type)); + break; + case NMC_SORT_TYPE_INV: + NM_CMP_DIRECT_STRCMP0 (_con_show_fcn_get_type (c_b, ac_b_effective, get_type), + _con_show_fcn_get_type (c_a, ac_a_effective, get_type)); + break; - if (!cons) - return NULL; + case NMC_SORT_NAME: + NM_CMP_RETURN (nm_utf8_collate0 (_con_show_fcn_get_id (c_a, ac_a_effective), + _con_show_fcn_get_id (c_b, ac_b_effective))); + break; + case NMC_SORT_NAME_INV: + NM_CMP_RETURN (nm_utf8_collate0 (_con_show_fcn_get_id (c_b, ac_b_effective), + _con_show_fcn_get_id (c_a, ac_a_effective))); + break; - compare_info.nmc = nmc; - compare_info.order = order; + case NMC_SORT_PATH: + NM_CMP_RETURN (nm_utils_dbus_path_cmp (nm_connection_get_path (c_a), nm_connection_get_path (c_b))); + break; - sorted = g_ptr_array_sized_new (cons->len); - for (i = 0; i < cons->len; i++) - g_ptr_array_add (sorted, cons->pdata[i]); - g_ptr_array_sort_with_data (sorted, compare_connections, &compare_info); - return sorted; -} + case NMC_SORT_PATH_INV: + NM_CMP_RETURN (nm_utils_dbus_path_cmp (nm_connection_get_path (c_b), nm_connection_get_path (c_a))); + break; -static int -compare_ac_connections (gconstpointer a, gconstpointer b, gpointer user_data) -{ - NMActiveConnection *ca = *(NMActiveConnection **)a; - NMActiveConnection *cb = *(NMActiveConnection **)b; - int cmp; + default: + nm_assert_not_reached (); + break; + } + } - /* Sort states first */ - cmp = nm_active_connection_get_state (cb) - nm_active_connection_get_state (ca); - if (cmp != 0) - return cmp; + NM_CMP_DIRECT_STRCMP0 (nm_connection_get_uuid (c_a), + nm_connection_get_uuid (c_b)); + NM_CMP_DIRECT_STRCMP0 (nm_connection_get_path (c_a), + nm_connection_get_path (c_b)); - cmp = g_strcmp0 (nm_active_connection_get_id (ca), - nm_active_connection_get_id (cb)); - if (cmp != 0) - return cmp; + /* This line is not expected to be reached, because there shouldn't be two + * different connections with the same path. Anyway, fall-through and compare by + * active connections... */ + } - return g_strcmp0 (nm_active_connection_get_connection_type (ca), - nm_active_connection_get_connection_type (cb)); + return active_connection_cmp (ac_a, ac_b); } static GPtrArray * -get_invisible_active_connections (NmCli *nmc) -{ - const GPtrArray *acons; - const GPtrArray *connections; - GPtrArray *invisibles; - int a, c; +con_show_get_items (NmCli *nmc, gboolean active_only, gboolean show_active_fields, GArray *order) +{ + gs_unref_hashtable GHashTable *row_hash = NULL; + GHashTableIter hiter; + GPtrArray *result; + const GPtrArray *arr; + NMRemoteConnection *c; + MetagenConShowRowData *row_data; + guint i; + const ConShowSortInfo sort_info = { + .nmc = nmc, + .order = order, + .show_active_fields = show_active_fields, + }; - g_return_val_if_fail (nmc, NULL); + row_hash = g_hash_table_new (nm_direct_hash, NULL); + + arr = nm_client_get_connections (nmc->client); + for (i = 0; i < arr->len; i++) { + /* Note: libnm will not expose connection that are invisible + * to the user but currently inactive. + * + * That differs from get-active-connection(). If an invisible connection + * is active, we can get its NMActiveConnection. We can even obtain + * the corresponding NMRemoteConnection (although, of course it has + * no visible settings). + * + * I think this inconsistency is a bug in libnm. Anyway, the result is, + * that we print invisible connections if they are active, but otherwise + * we exclude them. */ + c = arr->pdata[i]; + g_hash_table_insert (row_hash, + c, + _metagen_con_show_row_data_new_for_connection (c, + show_active_fields)); + } + + arr = nm_client_get_active_connections (nmc->client); + for (i = 0; i < arr->len; i++) { + NMActiveConnection *ac = arr->pdata[i]; + + c = nm_active_connection_get_connection (ac); + if (!show_active_fields && !c) { + /* the active connection has no connection, and we don't show + * any active fields. Skip this row. */ + continue; + } - invisibles = g_ptr_array_new (); - acons = nm_client_get_active_connections (nmc->client); - connections = nm_client_get_connections (nmc->client); - for (a = 0; a < acons->len; a++) { - gboolean found = FALSE; - NMActiveConnection *acon = g_ptr_array_index (acons, a); - const char *a_uuid = nm_active_connection_get_uuid (acon); + row_data = c + ? g_hash_table_lookup (row_hash, c) + : NULL; + + if (show_active_fields || !c) { + /* the active connection either has no connection (in which we create a + * connection-less row), or we are interested in showing each active + * connection in its own row. Add a row. */ + if (row_data) { + /* we create a rowdata for this connection earlier. We drop it, because this + * connection is tracked via the rowdata of the active connection. */ + g_hash_table_remove (row_hash, c); + _metagen_con_show_row_data_destroy (row_data); + } + row_data = _metagen_con_show_row_data_new_for_active_connection (c, ac, show_active_fields); + g_hash_table_insert (row_hash, ac, row_data); + continue; + } - for (c = 0; c < connections->len; c++) { - NMConnection *con = g_ptr_array_index (connections, c); - const char *c_uuid = nm_connection_get_uuid (con); + /* we add the active connection to the row for the referenced + * connection. We need to group them this way, to print the proper + * color (activated or not) based on primary_active. */ + if (!row_data) { + /* this is unexpected. The active connection references a connection that + * seemingly no longer exists. It's a bug in libnm. Add a row nontheless. */ + row_data = _metagen_con_show_row_data_new_for_connection (c, show_active_fields); + g_hash_table_insert (row_hash, c, row_data); + } + _metagen_con_show_row_data_add_active_connection (row_data, ac); + } - if (strcmp (a_uuid, c_uuid) == 0) { - found = TRUE; - break; - } + result = g_ptr_array_new_with_free_func (_metagen_con_show_row_data_destroy); + + g_hash_table_iter_init (&hiter, row_hash); + while (g_hash_table_iter_next (&hiter, NULL, (gpointer *) &row_data)) { + if ( active_only + && !row_data->primary_active) { + /* We only print connections that are active. Skip this row. */ + _metagen_con_show_row_data_destroy (row_data); + continue; } - /* Active connection is not in connections array, add it to */ - if (!found) - g_ptr_array_add (invisibles, acon); + if (!show_active_fields) { + NMSettingConnection *s_con; + + nm_assert (NM_IS_REMOTE_CONNECTION (row_data->connection)); + s_con = nm_connection_get_setting_connection (row_data->connection); + if ( !s_con + || !nm_setting_connection_get_uuid (s_con)) { + /* we are in a mode, where we only print rows for connection. + * For that we require that all rows are visible to the user, + * meaning: the have a [connection] setting and a UUID. + * + * Otherwise, this connection is likely invisible to the user. + * Skip it. */ + _metagen_con_show_row_data_destroy (row_data); + continue; + } + _metagen_con_show_row_data_init_primary_active (row_data); + } else + nm_assert (!row_data->all_active); + g_ptr_array_add (result, row_data); } - g_ptr_array_sort_with_data (invisibles, compare_ac_connections, NULL); - return invisibles; + + g_ptr_array_sort_with_data (result, con_show_get_items_cmp, (gpointer) &sort_info); + return result; } static GArray * @@ -1579,9 +1838,9 @@ get_connection (NmCli *nmc, } if (*argc == 1 && nmc->complete) - nmc_complete_strings (**argv, "id", "uuid", "path", NULL); + nmc_complete_strings (**argv, "id", "uuid", "path", "filename", NULL); - if (NM_IN_STRSET (**argv, "id", "uuid", "path")) { + if (NM_IN_STRSET (**argv, "id", "uuid", "path", "filename")) { if (*argc == 1) { if (!nmc->complete) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -1616,10 +1875,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv) gs_free_error GError *err = NULL; gs_free char *profile_flds = NULL; gs_free char *active_flds = NULL; - GPtrArray *invisibles, *sorted_cons; gboolean active_only = FALSE; gs_unref_array GArray *order = NULL; - guint i, j; + guint i; int option; /* check connection show options [--active] [--order <order spec>] */ @@ -1647,50 +1905,59 @@ do_connections_show (NmCli *nmc, int argc, char **argv) } if (argc == 0) { - const GPtrArray *connections; const char *fields_str = NULL; - char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; - const NMMetaAbstractInfo *const*tmpl; - NmcOutputField *arr; - NMC_OUTPUT_DATA_DEFINE_SCOPED (out); + gs_unref_ptrarray GPtrArray *items = NULL; + gs_free NMMetaSelectionResultList *selection = NULL; + gboolean show_active_fields = TRUE; if (nmc->complete) goto finish; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) - fields_str = fields_common; + fields_str = NMC_FIELDS_CON_SHOW_COMMON; else if (!nmc->required_fields || strcasecmp (nmc->required_fields, "all") == 0) { } else fields_str = nmc->required_fields; - tmpl = (const NMMetaAbstractInfo *const*) nmc_fields_con_show; - out_indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, &err); - if (err) - goto finish; - - /* Add headers */ - arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); - g_ptr_array_add (out.output_data, arr); - - /* There might be active connections not present in connection list - * (e.g. private connections of a different user). Show them as well. */ - invisibles = get_invisible_active_connections (nmc); - for (i = 0; i < invisibles->len; i++) - fill_output_connection_for_invisible (invisibles->pdata[i], nmc->nmc_config.print_output, out.output_data); - g_ptr_array_free (invisibles, TRUE); + /* determine whether the user wants to see any fields that are related to active-connections + * (e.g. the apath, the current state, or the device where the profile is active). + * + * If that's the case, then we will show one line for each active connection. In case + * a profile has multiple active connections, it will be listed multiple times. + * If that's not the case, we filter out these duplicate lines. */ + selection = nm_meta_selection_create_parse_list ((const NMMetaAbstractInfo *const*) metagen_con_show, + NULL, + fields_str, + FALSE, + NULL); + if (selection && selection->num > 0) { + show_active_fields = FALSE; + for (i = 0; i < selection->num; i++) { + const NmcMetaGenericInfo *info = (const NmcMetaGenericInfo *) selection->items[i].info; + + if (NM_IN_SET (info->info_type, NMC_GENERIC_INFO_TYPE_CON_SHOW_DEVICE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_STATE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE_PATH)) { + show_active_fields = TRUE; + break; + } + } + } - /* Sort the connections and fill the output data */ - connections = nm_client_get_connections (nmc->client); - sorted_cons = sort_connections (connections, nmc, order); - for (i = 0; i < sorted_cons->len; i++) - fill_output_connection (sorted_cons->pdata[i], nmc->client, nmc->nmc_config.print_output, out.output_data, active_only); - g_ptr_array_free (sorted_cons, TRUE); - - print_data_prepare_width (out.output_data); - print_data (&nmc->nmc_config, out_indices, - active_only ? _("NetworkManager active profiles") - : _("NetworkManager connection profiles"), - 0, &out); + /* Optionally start paging the output. */ + nmc_terminal_spawn_pager (&nmc->nmc_config); + + items = con_show_get_items (nmc, active_only, show_active_fields, order); + g_ptr_array_add (items, NULL); + if (!nmc_print (&nmc->nmc_config, + items->pdata, + active_only + ? _("NetworkManager active profiles") + : _("NetworkManager connection profiles"), + (const NMMetaAbstractInfo *const*) metagen_con_show, + fields_str, + &err)) + goto finish; } else { gboolean new_line = FALSE; gboolean without_fields = (nmc->required_fields == NULL); @@ -1713,7 +1980,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv) char **argv_cp = argv; do { - if (NM_IN_STRSET (*argv_cp, "id", "uuid", "path", "apath")) { + if (NM_IN_STRSET (*argv_cp, "id", "uuid", "path", "filename", "apath")) { argc_cp--; argv_cp++; } @@ -1731,9 +1998,9 @@ do_connections_show (NmCli *nmc, int argc, char **argv) guint i_found_cons; if (argc == 1 && nmc->complete) - nmc_complete_strings (*argv, "id", "uuid", "path", "apath", NULL); + nmc_complete_strings (*argv, "id", "uuid", "path", "filename", "apath", NULL); - if (NM_IN_STRSET (*argv, "id", "uuid", "path", "apath")) { + if (NM_IN_STRSET (*argv, "id", "uuid", "path", "filename", "apath")) { selector = *argv; argc--; argv++; @@ -1827,10 +2094,10 @@ do_connections_show (NmCli *nmc, int argc, char **argv) if (without_fields || active_flds) { guint l = explicit_acon ? 1 : (found_acons ? found_acons->len : 0); - for (j = 0; j < l; j++) { + for (i = 0; i < l; i++) { NMActiveConnection *acon; - if (j > 0) { + if (i > 0) { /* if there are multiple active connections, separate them with newline. * that is a bit odd, because we already separate connections with newlines, * and commonly don't separate the connection from the first active connection. */ @@ -1840,7 +2107,7 @@ do_connections_show (NmCli *nmc, int argc, char **argv) if (explicit_acon) acon = explicit_acon; else - acon = found_acons->pdata[j]; + acon = found_acons->pdata[i]; nmc->required_fields = active_flds; res = nmc_active_connection_details (acon, nmc); @@ -2579,7 +2846,7 @@ connection_cb_info_finish (ConnectionCbInfo *info, gpointer obj) } else { while (info->obj_list->len > 0) { obj = info->obj_list->pdata[info->obj_list->len - 1]; - g_ptr_array_remove_index (info->obj_list, info->obj_list->len); + g_ptr_array_remove_index (info->obj_list, info->obj_list->len - 1); connection_cb_info_obj_list_destroy (info, obj); } } @@ -2680,9 +2947,9 @@ do_connection_down (NmCli *nmc, int argc, char **argv) const char *selector = NULL; if (arg_num == 1 && nmc->complete) - nmc_complete_strings (*arg_ptr, "id", "uuid", "path", "apath", NULL); + nmc_complete_strings (*arg_ptr, "id", "uuid", "path", "filename", "apath", NULL); - if (NM_IN_STRSET (*arg_ptr, "id", "uuid", "path", "apath")) { + if (NM_IN_STRSET (*arg_ptr, "id", "uuid", "path", "filename", "apath")) { selector = *arg_ptr; arg_num--; arg_ptr++; @@ -7681,6 +7948,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) const char *con_id = NULL; const char *con_uuid = NULL; const char *con_path = NULL; + const char *con_filename = NULL; const char *selector = NULL; gs_free_error GError *error = NULL; GError *err1 = NULL; @@ -7689,11 +7957,12 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) { "id", TRUE, &con_id, FALSE }, { "uuid", TRUE, &con_uuid, FALSE }, { "path", TRUE, &con_path, FALSE }, + { "filename", TRUE, &con_filename, FALSE }, { NULL } }; next_arg (nmc, &argc, &argv, NULL); if (argc == 1 && nmc->complete) - nmc_complete_strings (*argv, "type", "con-name", "id", "uuid", "path", NULL); + nmc_complete_strings (*argv, "type", "con-name", "id", "uuid", "path", "filename", NULL); nmc->return_value = NMC_RESULT_SUCCESS; @@ -7715,20 +7984,23 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) connections = nm_client_get_connections (nmc->client); if (!con) { - if (con_id && !con_uuid && !con_path) { + if (con_id && !con_uuid && !con_path && !con_filename) { con = con_id; selector = "id"; - } else if (con_uuid && !con_id && !con_path) { + } else if (con_uuid && !con_id && !con_path && !con_filename) { con = con_uuid; selector = "uuid"; - } else if (con_path && !con_id && !con_uuid) { + } else if (con_path && !con_id && !con_uuid && !con_filename) { con = con_path; selector = "path"; - } else if (!con_path && !con_id && !con_uuid) { + } else if (con_filename && !con_path && !con_id && !con_uuid) { + con = con_filename; + selector = "filename"; + } else if (!con_path && !con_id && !con_uuid && !con_filename) { /* no-op */ } else { g_string_printf (nmc->return_text, - _("Error: only one of 'id', uuid, or 'path' can be provided.")); + _("Error: only one of 'id', 'filename', uuid, or 'path' can be provided.")); NMC_RETURN (nmc, NMC_RESULT_ERROR_USER_INPUT); } } diff --git a/clients/cli/connections.h b/clients/cli/connections.h index 591e9cda..43cd97f6 100644 --- a/clients/cli/connections.h +++ b/clients/cli/connections.h @@ -35,8 +35,8 @@ nmc_read_connection_properties (NmCli *nmc, NMMetaColor nmc_active_connection_state_to_color (NMActiveConnectionState state); -extern const NmcMetaGenericInfo *const nmc_fields_con_show[]; -extern const NmcMetaGenericInfo *const nmc_fields_con_active_details_general[]; +extern const NmcMetaGenericInfo *const metagen_con_show[]; +extern const NmcMetaGenericInfo *const metagen_con_active_general[]; extern const NmcMetaGenericInfo *const nmc_fields_con_active_details_vpn[]; extern const NmcMetaGenericInfo *const nmc_fields_con_active_details_groups[]; diff --git a/clients/cli/devices.c b/clients/cli/devices.c index b99e606a..58102ed3 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -1725,7 +1725,7 @@ 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 (_("Connection with UUID '%s' created and activated on device '%s'\n"), nm_active_connection_get_uuid (active), nm_device_get_iface (device)); else @@ -2488,41 +2488,6 @@ do_devices_monitor (NmCli *nmc, int argc, char **argv) return nmc->return_value; } -static void -show_access_point_info (NMDevice *device, NmCli *nmc, NmcOutputData *out) -{ - NMAccessPoint *active_ap = NULL; - const char *active_bssid = NULL; - GPtrArray *aps; - NmcOutputField *arr; - - if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { - active_ap = nm_device_wifi_get_active_access_point (NM_DEVICE_WIFI (device)); - active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; - } - - arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_dev_wifi_list, - NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); - g_ptr_array_add (out->output_data, arr); - - { - APInfo info = { - .nmc = nmc, - .index = 1, - .output_flags = 0, - .active_bssid = active_bssid, - .device = nm_device_get_iface (device), - .output_data = out->output_data, - }; - - aps = sort_access_points (nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device))); - g_ptr_array_foreach (aps, fill_output_access_point, &info); - g_ptr_array_free (aps, FALSE); - } - - print_data_prepare_width (out->output_data); -} - /* * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, * the first Wi-Fi device is returned. 'idx' parameter is updated to the point @@ -2616,6 +2581,217 @@ find_ap_on_device (NMDevice *device, const char *bssid, const char *ssid, gboole } static void +show_access_point_info (NMDeviceWifi *wifi, NmCli *nmc, NmcOutputData *out) +{ + NMAccessPoint *active_ap = NULL; + const char *active_bssid = NULL; + GPtrArray *aps; + NmcOutputField *arr; + + if (nm_device_get_state (NM_DEVICE (wifi)) == NM_DEVICE_STATE_ACTIVATED) { + active_ap = nm_device_wifi_get_active_access_point (wifi); + active_bssid = active_ap ? nm_access_point_get_bssid (active_ap) : NULL; + } + + arr = nmc_dup_fields_array ((const NMMetaAbstractInfo *const*) nmc_fields_dev_wifi_list, + NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (out->output_data, arr); + + { + APInfo info = { + .nmc = nmc, + .index = 1, + .output_flags = 0, + .active_bssid = active_bssid, + .device = nm_device_get_iface (NM_DEVICE (wifi)), + .output_data = out->output_data, + }; + + aps = sort_access_points (nm_device_wifi_get_access_points (wifi)); + g_ptr_array_foreach (aps, fill_output_access_point, &info); + g_ptr_array_free (aps, FALSE); + } + + print_data_prepare_width (out->output_data); +} + +static void +wifi_print_aps (NMDeviceWifi *wifi, + NmCli *nmc, + GArray *_out_indices, + const NMMetaAbstractInfo *const*tmpl, + const char *bssid_user) +{ + NMAccessPoint *ap = NULL; + const GPtrArray *aps; + APInfo *info; + guint i; + NmcOutputField *arr; + const char *base_hdr = _("Wi-Fi scan list"); + NMC_OUTPUT_DATA_DEFINE_SCOPED (out); + gs_free char *header_name = NULL; + static gboolean empty_line = FALSE; + + if (empty_line) + g_print ("\n"); /* Empty line between devices' APs */ + + /* Main header name */ + header_name = construct_header_name (base_hdr, nm_device_get_iface (NM_DEVICE (wifi))); + + out_indices = g_array_ref (_out_indices); + + if (bssid_user) { + /* Specific AP requested - list only that */ + aps = nm_device_wifi_get_access_points (wifi); + for (i = 0; i < aps->len; i++) { + char *bssid_up; + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + bssid_up = g_ascii_strup (bssid_user, -1); + if (!strcmp (bssid_up, candidate_bssid)) + ap = candidate_ap; + g_free (bssid_up); + } + if (ap) { + /* Add headers (field names) */ + arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); + g_ptr_array_add (out.output_data, arr); + + info = g_malloc0 (sizeof (APInfo)); + info->nmc = nmc; + info->index = 1; + info->output_flags = 0; + info->active_bssid = NULL; + info->device = nm_device_get_iface (NM_DEVICE (wifi)); + info->output_data = out.output_data; + + fill_output_access_point (ap, info); + + print_data_prepare_width (out.output_data); + print_data (&nmc->nmc_config, out_indices, header_name, 0, &out); + g_free (info); + + nmc->return_value = NMC_RESULT_SUCCESS; + empty_line = TRUE; + } + } else { + show_access_point_info (wifi, nmc, &out); + print_data (&nmc->nmc_config, out_indices, header_name, 0, &out); + empty_line = TRUE; + } +} + +typedef struct { + NmCli *nmc; + NMDeviceWifi *wifi; + const NMMetaAbstractInfo *const*tmpl; + + const char *bssid_user; + gulong last_scan_id; + guint timeout_id; + GCancellable *scan_cancellable; + GArray *out_indices; +} WifiListData; + +static void +wifi_list_finish (WifiListData *data) +{ + NmCli *nmc = data->nmc; + + wifi_print_aps (data->wifi, data->nmc, data->out_indices, + data->tmpl, data->bssid_user); + + if (--nmc->should_wait == 0) { + if (nmc->return_value == NMC_RESULT_ERROR_NOT_FOUND) { + g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), + data->bssid_user); + } + g_main_loop_quit (loop); + } + + g_signal_handler_disconnect (data->wifi, data->last_scan_id); + nm_clear_g_source (&data->timeout_id); + nm_clear_g_cancellable (&data->scan_cancellable); + g_array_unref (data->out_indices); + g_object_unref (data->wifi); + g_slice_free (WifiListData, data); +} + +static void +wifi_last_scan_updated (GObject *gobject, GParamSpec *pspec, gpointer user_data) +{ + WifiListData *data = user_data; + + wifi_list_finish (data); +} + +static void +wifi_list_rescan_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + NMDeviceWifi *wifi = NM_DEVICE_WIFI (source_object); + WifiListData *data = user_data; + gs_free_error GError *error = NULL; + + if (!nm_device_wifi_request_scan_finish (wifi, res, &error)) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + if (g_error_matches (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED)) { + /* This likely means that scanning is already in progress. There's + * a good chance we'll get updated results soon; wait for them. */ + return; + } + + /* Scanning could not be initiated for unknown reason, + * no point in waiting for results. */ + wifi_list_finish (data); + } +} + +static gboolean +wifi_list_scan_timeout (gpointer user_data) +{ + WifiListData *data = user_data; + + wifi_list_finish (data); + + return G_SOURCE_REMOVE; +} + +static void +wifi_list_aps (NMDeviceWifi *wifi, + NmCli *nmc, + GArray *out_indices, + const NMMetaAbstractInfo *const*tmpl, + const char *bssid_user, + gint64 rescan_cutoff) +{ + gboolean needs_rescan; + WifiListData *data; + + needs_rescan = rescan_cutoff < 0 || (rescan_cutoff > 0 && nm_device_wifi_get_last_scan (wifi) < rescan_cutoff); + + if (needs_rescan) { + data = g_slice_new0 (WifiListData); + data->nmc = nmc; + data->wifi = g_object_ref (wifi); + data->tmpl = tmpl; + data->out_indices = g_array_ref (out_indices);; + data->bssid_user = bssid_user; + data->last_scan_id = g_signal_connect (wifi, "notify::" NM_DEVICE_WIFI_LAST_SCAN, + G_CALLBACK (wifi_last_scan_updated), data); + data->scan_cancellable = g_cancellable_new (); + data->timeout_id = g_timeout_add_seconds (15, wifi_list_scan_timeout, data); + nm_device_wifi_request_scan_async (wifi, data->scan_cancellable, wifi_list_rescan_cb, data); + + nmc->should_wait++; + } else { + wifi_print_aps (wifi, nmc, out_indices, tmpl, bssid_user); + } +} + +static void complete_aps (NMDevice **devices, const char *ifname, const char *bssid_prefix, const char *ssid_prefix) { @@ -2640,28 +2816,22 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) { GError *error = NULL; NMDevice *device = NULL; - NMAccessPoint *ap = NULL; const char *ifname = NULL; const char *bssid_user = NULL; + const char *rescan = NULL; gs_free NMDevice **devices = NULL; - const GPtrArray *aps; - APInfo *info; - int i, j; + guint i; const char *fields_str = NULL; const NMMetaAbstractInfo *const*tmpl; - NmcOutputField *arr; - const char *base_hdr = _("Wi-Fi scan list"); - NMC_OUTPUT_DATA_DEFINE_SCOPED (out); - gs_free char *header_name = NULL; + gs_unref_array GArray *out_indices = NULL; + int option; + guint64 rescan_cutoff; devices = nmc_get_devices_sorted (nmc->client); - next_arg (nmc, &argc, &argv, NULL); - while (argc > 0) { - if (argc == 1 && nmc->complete) - nmc_complete_strings (*argv, "ifname", "bssid", NULL); - - if (strcmp (*argv, "ifname") == 0) { + while ((option = next_arg (nmc, &argc, &argv, "ifname", "hwaddr", "bssid", "--rescan", NULL)) > 0) { + switch (option) { + case 1: /* ifname */ argc--; argv++; if (!argc) { @@ -2671,8 +2841,9 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) ifname = *argv; if (argc == 1 && nmc->complete) complete_device (devices, ifname, TRUE); - } else if (strcmp (*argv, "bssid") == 0 || strcmp (*argv, "hwaddr") == 0) { - /* hwaddr is deprecated and will be removed later */ + break; + case 2: /* hwaddr is deprecated and will be removed later */ + case 3: /* bssid */ argc--; argv++; if (!argc) { @@ -2682,11 +2853,24 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) bssid_user = *argv; if (argc == 1 && nmc->complete) complete_aps (devices, NULL, bssid_user, NULL); - } else if (!nmc->complete) { - g_printerr (_("Unknown parameter: %s\n"), *argv); + /* We'll switch this to NMC_RESULT_SUCCESS if we find an access point. */ + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + break; + case 4: /* --rescan */ + argc--; + argv++; + if (!argc) { + g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); + return NMC_RESULT_ERROR_USER_INPUT; + } + rescan = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_strings (rescan, "auto", "no", "yes", NULL); + break; + default: + g_assert_not_reached(); + break; } - - next_arg (nmc, &argc, &argv, NULL); } if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) @@ -2707,55 +2891,29 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (nmc->complete) return nmc->return_value; - if (ifname) { + if (argc) + g_printerr (_("Unknown parameter: %s\n"), *argv); + + if (rescan == NULL || strcmp (rescan, "auto") == 0) { + rescan_cutoff = NM_MAX (nm_utils_get_timestamp_msec () - 30 * NM_UTILS_MSEC_PER_SECOND, 0); + } else if (strcmp (rescan, "no") == 0) { + rescan_cutoff = 0; + } else if (strcmp (rescan, "yes") == 0) { + rescan_cutoff = -1; + } else { + g_string_printf (nmc->return_text, _("Error: invalid rescan argument: '%s' not among [auto, no, yes]"), rescan); + return NMC_RESULT_ERROR_USER_INPUT; + } + if (ifname) { device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); return NMC_RESULT_ERROR_NOT_FOUND; } - /* Main header name */ - header_name = construct_header_name (base_hdr, ifname); if (NM_IS_DEVICE_WIFI (device)) { - if (bssid_user) { - /* Specific AP requested - list only that */ - aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); - for (j = 0; j < aps->len; j++) { - char *bssid_up; - NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); - const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); - - bssid_up = g_ascii_strup (bssid_user, -1); - if (!strcmp (bssid_up, candidate_bssid)) - ap = candidate_ap; - g_free (bssid_up); - } - if (!ap) { - g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), - bssid_user); - return NMC_RESULT_ERROR_NOT_FOUND; - } - /* Add headers (field names) */ - arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); - g_ptr_array_add (out.output_data, arr); - - info = g_malloc0 (sizeof (APInfo)); - info->nmc = nmc; - info->index = 1; - info->output_flags = 0; - info->active_bssid = NULL; - info->device = nm_device_get_iface (device); - - fill_output_access_point (ap, info); - - print_data_prepare_width (out.output_data); - print_data (&nmc->nmc_config, out_indices, header_name, 0, &out); - g_free (info); - } else { - show_access_point_info (device, nmc, &out); - print_data (&nmc->nmc_config, out_indices, NULL, 0, &out); - } + wifi_list_aps (NM_DEVICE_WIFI (device), nmc, out_indices, tmpl, bssid_user, rescan_cutoff); } else { if ( nm_device_get_device_type (device) == NM_DEVICE_TYPE_GENERIC && g_strcmp0 (nm_device_get_type_description (device), "wifi") == 0) { @@ -2770,76 +2928,11 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) return NMC_RESULT_ERROR_UNKNOWN; } } else { - gboolean empty_line = FALSE; - - /* List APs for all devices */ - if (bssid_user) { - /* Specific AP requested - list only that */ - for (i = 0; devices[i]; i++) { - NMDevice *dev = devices[i]; - NMC_OUTPUT_DATA_DEFINE_SCOPED (out2); - gs_free char *header_name2 = NULL; - - if (!NM_IS_DEVICE_WIFI (dev)) - continue; - - /* Main header name */ - header_name2 = construct_header_name (base_hdr, nm_device_get_iface (dev)); - out2_indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); - - arr = nmc_dup_fields_array (tmpl, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); - g_ptr_array_add (out2.output_data, arr); - - aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (dev)); - for (j = 0; j < aps->len; j++) { - char *bssid_up; - NMAccessPoint *candidate_ap = g_ptr_array_index (aps, j); - const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); - - bssid_up = g_ascii_strup (bssid_user, -1); - if (!strcmp (bssid_up, candidate_bssid)) { - ap = candidate_ap; - - info = g_malloc0 (sizeof (APInfo)); - info->nmc = nmc; - info->index = 1; - info->output_flags = 0; - info->active_bssid = NULL; - info->device = nm_device_get_iface (dev); - fill_output_access_point (ap, info); - g_free (info); - } - g_free (bssid_up); - } - if (empty_line) - g_print ("\n"); /* Empty line between devices' APs */ - print_data_prepare_width (out2.output_data); - print_data (&nmc->nmc_config, out2_indices, header_name2, 0, &out2); - empty_line = TRUE; - } - if (!ap) { - g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), - bssid_user); - return NMC_RESULT_ERROR_NOT_FOUND; - } - } else { - for (i = 0; devices[i]; i++) { - NMDevice *dev = devices[i]; - NMC_OUTPUT_DATA_DEFINE_SCOPED (out2); - gs_free char *header_name2 = NULL; - - /* Main header name */ - header_name2 = construct_header_name (base_hdr, - nm_device_get_iface (dev)); - out2_indices = parse_output_fields (fields_str, tmpl, FALSE, NULL, NULL); - - if (NM_IS_DEVICE_WIFI (dev)) { - if (empty_line) - g_print ("\n"); /* Empty line between devices' APs */ - show_access_point_info (dev, nmc, &out2); - print_data (&nmc->nmc_config, out2_indices, header_name2, 0, &out2); - empty_line = TRUE; - } + for (i = 0; devices[i]; i++) { + NMDevice *dev = devices[i]; + + if (NM_IS_DEVICE_WIFI (dev)) { + wifi_list_aps (NM_DEVICE_WIFI (dev), nmc, out_indices, tmpl, bssid_user, rescan_cutoff); } } } @@ -2852,9 +2945,9 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) { NMDevice *device = NULL; NMAccessPoint *ap = NULL; - NM80211ApFlags ap_flags = NM_802_11_AP_FLAGS_NONE; - NM80211ApSecurityFlags ap_wpa_flags = NM_802_11_AP_SEC_NONE; - NM80211ApSecurityFlags ap_rsn_flags = NM_802_11_AP_SEC_NONE; + NM80211ApFlags ap_flags; + NM80211ApSecurityFlags ap_wpa_flags; + NM80211ApSecurityFlags ap_rsn_flags; NMConnection *connection = NULL; NMSettingConnection *s_con; NMSettingWireless *s_wifi; @@ -3151,7 +3244,9 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) ap_rsn_flags = nm_access_point_get_rsn_flags (ap); /* Set password for WEP or WPA-PSK. */ - 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_rsn_flags != NM_802_11_AP_SEC_NONE) { /* Ask for missing password when one is expected and '--ask' is used */ if (!password && nmc->ask) password = passwd_ask = nmc_readline_echo (nmc->nmc_config.show_secrets, _("Password: ")); diff --git a/clients/cli/general.c b/clients/cli/general.c index 841df8bd..de743970 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -560,6 +560,9 @@ print_permissions (void *user_data) permissions[i++] = GINT_TO_POINTER (perm); permissions[i++] = NULL; + /* Optionally start paging the output. */ + nmc_terminal_spawn_pager (&nmc->nmc_config); + if (!nmc_print (&nmc->nmc_config, permissions, _("NetworkManager permissions"), diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index e9752952..6407f50b 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -188,8 +188,8 @@ complete_fields (const char *option, const char *prefix) complete_field (h, metagen_ip4_config); complete_field (h, nmc_fields_dhcp_config); complete_field (h, nmc_fields_ip6_config); - complete_field (h, nmc_fields_con_show); - complete_field (h, nmc_fields_con_active_details_general); + complete_field (h, metagen_con_show); + complete_field (h, metagen_con_active_general); complete_field (h, nmc_fields_con_active_details_vpn); complete_field (h, nmc_fields_con_active_details_groups); complete_field (h, nmc_fields_dev_status); diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 61bf86de..bcf1c01b 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -72,6 +72,14 @@ typedef enum { NMC_PRINT_PRETTY = 2 } NMCPrintOutput; +static inline NMMetaAccessorGetType +nmc_print_output_to_accessor_get_type (NMCPrintOutput print_output) +{ + return (print_output != NMC_PRINT_TERSE) + ? NM_META_ACCESSOR_GET_TYPE_PRETTY + : NM_META_ACCESSOR_GET_TYPE_PARSABLE; +} + /* === Output fields === */ typedef enum { diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 32c44e39..365a0303 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -199,10 +199,18 @@ next_arg (NmCli *nmc, int *argc, char ***argv, ...) /* Check command dependent options first */ while ((cmd_option = va_arg (args, const char *))) { - /* strip heading "--" form cmd_option */ - if (nmc_arg_is_option (**argv, cmd_option + 2)) { - va_end (args); - return cmd_option_pos; + if (cmd_option[0] == '-' && cmd_option[1] == '-') { + /* Match as an option (leading "--" stripped) */ + if (nmc_arg_is_option (**argv, cmd_option + 2)) { + va_end (args); + return cmd_option_pos; + } + } else { + /* Match literally. */ + if (strcmp (**argv, cmd_option) == 0) { + va_end (args); + return cmd_option_pos; + } } cmd_option_pos++; } @@ -1004,9 +1012,7 @@ _print_fill (const NmcConfig *nmc_config, g_array_set_clear_func (cells, _print_data_cell_clear); g_array_set_size (cells, targets_len * header_row->len); - text_get_type = pretty - ? NM_META_ACCESSOR_GET_TYPE_PRETTY - : NM_META_ACCESSOR_GET_TYPE_PARSABLE; + text_get_type = nmc_print_output_to_accessor_get_type (nmc_config->print_output); text_get_flags = NM_META_ACCESSOR_GET_FLAGS_ACCEPT_STRV; if (nmc_config->show_secrets) text_get_flags |= NM_META_ACCESSOR_GET_FLAGS_SHOW_SECRETS; @@ -1040,20 +1046,22 @@ _print_fill (const NmcConfig *nmc_config, &is_default, (gpointer *) &to_free); + nm_assert (!to_free || value == to_free); + header_cell->skip = nmc_config->overview && is_default; if (NM_FLAGS_HAS (text_out_flags, NM_META_ACCESSOR_GET_OUT_FLAGS_STRV)) { - if (value) { - if (nmc_config->multiline_output) { - cell->text_format = PRINT_DATA_CELL_FORMAT_TYPE_STRV; - cell->text.strv = value; - cell->text_to_free = !!to_free; - } else { + if (nmc_config->multiline_output) { + cell->text_format = PRINT_DATA_CELL_FORMAT_TYPE_STRV; + cell->text.strv = value; + cell->text_to_free = !!to_free; + } else { + if (value && ((const char *const*) value)[0]) { cell->text.plain = g_strjoinv (" | ", (char **) value); cell->text_to_free = TRUE; - if (to_free) - g_strfreev ((char **) to_free); } + if (to_free) + g_strfreev ((char **) to_free); } } else { cell->text.plain = value; @@ -1165,7 +1173,7 @@ _print_do (const NmcConfig *nmc_config, guint i_row, i_col; nm_auto_free_gstring GString *str = NULL; - g_assert (col_len && row_len); + g_assert (col_len); /* Main header */ if (pretty && header_name_no_l10n) { diff --git a/clients/cli/utils.h b/clients/cli/utils.h index dc0ce083..19694d11 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -121,6 +121,37 @@ typedef enum { NMC_GENERIC_INFO_TYPE_IP6_CONFIG_DOMAIN, _NMC_GENERIC_INFO_TYPE_IP6_CONFIG_NUM, + NMC_GENERIC_INFO_TYPE_CON_SHOW_NAME = 0, + NMC_GENERIC_INFO_TYPE_CON_SHOW_UUID, + NMC_GENERIC_INFO_TYPE_CON_SHOW_TYPE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP, + NMC_GENERIC_INFO_TYPE_CON_SHOW_TIMESTAMP_REAL, + NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT, + NMC_GENERIC_INFO_TYPE_CON_SHOW_AUTOCONNECT_PRIORITY, + NMC_GENERIC_INFO_TYPE_CON_SHOW_READONLY, + NMC_GENERIC_INFO_TYPE_CON_SHOW_DBUS_PATH, + NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_DEVICE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_STATE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_ACTIVE_PATH, + NMC_GENERIC_INFO_TYPE_CON_SHOW_SLAVE, + NMC_GENERIC_INFO_TYPE_CON_SHOW_FILENAME, + _NMC_GENERIC_INFO_TYPE_CON_SHOW_NUM, + + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NAME = 0, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_UUID, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEVICES, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_STATE, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DEFAULT6, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_SPEC_OBJECT, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_VPN, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_DBUS_PATH, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_CON_PATH, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_ZONE, + NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_MASTER_PATH, + _NMC_GENERIC_INFO_TYPE_CON_ACTIVE_GENERAL_NUM, + } NmcGenericInfoType; #define NMC_HANDLE_COLOR(color) \ |