From bbbf5229413f364dba8646ca582f41e7eef513d5 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 29 Apr 2015 16:34:38 +0200 Subject: [PATCH 01/13] core,libnm: add 'metered' property to NMDevice --- introspection/nm-device.xml | 34 ++++++++++++++++++++++++++++++ libnm-core/nm-dbus-interface.h | 20 +++++++++++++++++- libnm/libnm.ver | 2 ++ libnm/nm-device.c | 38 ++++++++++++++++++++++++++++++++++ libnm/nm-device.h | 3 +++ src/devices/nm-device.c | 35 +++++++++++++++++++++++++++++++ src/devices/nm-device.h | 3 ++- 7 files changed, 133 insertions(+), 2 deletions(-) diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index 71b830f108..239358a1e7 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -145,6 +145,12 @@ The device MTU (maximum transmission unit). + + + Whether the amount of traffic flowing through the device is + subject to limitations, for example set by service providers. + + @@ -660,6 +666,34 @@ + + + + The device metered status is unknown. + + + + + The device is metered and the value was statically set. + + + + + The device is not metered and the value was statically set. + + + + + The device is metered and the value was guessed. + + + + + The device is not metered and the value was guessed. + + + + diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index ccc3b3a9e9..0934add6fd 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -403,7 +403,6 @@ typedef enum { NM_DEVICE_STATE_FAILED = 120 } NMDeviceState; - /** * NMDeviceStateReason: * @NM_DEVICE_STATE_REASON_NONE: No reason given @@ -540,6 +539,25 @@ typedef enum { NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED = 62, } NMDeviceStateReason; +/** + * NMMetered: + * @NM_METERED_UNKNOWN: The metered status is unknown + * @NM_METERED_YES: Metered, the value was statically set + * @NM_METERED_NO: Not metered, the value was statically set + * @NM_METERED_GUESS_YES: Metered, the value was guessed + * @NM_METERED_GUESS_NO: Not metered, the value was guessed + * + * (Corresponds to the NM_METERED type in nm-device.xml.) + * + * Since: 1.2 + **/ +typedef enum { + NM_METERED_UNKNOWN = 0, + NM_METERED_YES = 1, + NM_METERED_NO = 2, + NM_METERED_GUESS_YES = 3, + NM_METERED_GUESS_NO = 4, +} NMMetered; /** * NMActiveConnectionState: diff --git a/libnm/libnm.ver b/libnm/libnm.ver index c15f0381c0..c77069bc3c 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -848,7 +848,9 @@ local: libnm_1_2_0 { global: nm_access_point_get_last_seen; + nm_device_get_metered; nm_device_get_nm_plugin_missing; + nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; nm_setting_ip_config_add_dns_option; diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 44cdc89153..9781095dbe 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -81,6 +81,7 @@ typedef struct { char *driver_version; char *firmware_version; char *type_description; + NMMetered metered; NMDeviceCapabilities capabilities; gboolean managed; gboolean firmware_missing; @@ -132,6 +133,7 @@ enum { PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, PROP_MTU, + PROP_METERED, LAST_PROP }; @@ -196,6 +198,7 @@ init_dbus (NMObject *object) { NM_DEVICE_AVAILABLE_CONNECTIONS, &priv->available_connections, NULL, NM_TYPE_REMOTE_CONNECTION }, { NM_DEVICE_PHYSICAL_PORT_ID, &priv->physical_port_id }, { NM_DEVICE_MTU, &priv->mtu }, + { NM_DEVICE_METERED, &priv->metered }, /* Properties that exist in D-Bus but that we don't track */ { "ip4-address", NULL }, @@ -455,6 +458,9 @@ get_property (GObject *object, case PROP_MTU: g_value_set_uint (value, nm_device_get_mtu (device)); break; + case PROP_METERED: + g_value_set_uint (value, nm_device_get_metered (device)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -814,6 +820,20 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the device is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_DEVICE_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** @@ -1941,6 +1961,24 @@ nm_device_get_mtu (NMDevice *device) return NM_DEVICE_GET_PRIVATE (device)->mtu; } +/** + * nm_device_get_metered: + * @device: a #NMDevice + * + * Gets the metered setting of a #NMDevice. + * + * Returns: the metered setting. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered (NMDevice *device) +{ + g_return_val_if_fail (NM_IS_DEVICE (device), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (device)->metered; +} + /** * nm_device_is_software: * @device: a #NMDevice diff --git a/libnm/nm-device.h b/libnm/nm-device.h index 91cf13b1a0..66e1d5780c 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -61,6 +61,7 @@ G_BEGIN_DECLS #define NM_DEVICE_PRODUCT "product" #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" +#define NM_DEVICE_METERED "metered" struct _NMDevice { NMObject parent; @@ -122,6 +123,8 @@ gboolean nm_device_is_software (NMDevice *device); const char * nm_device_get_product (NMDevice *device); const char * nm_device_get_vendor (NMDevice *device); const char * nm_device_get_description (NMDevice *device); +NM_AVAILABLE_IN_1_2 +NMMetered nm_device_get_metered (NMDevice *device); char ** nm_device_disambiguate_names (NMDevice **devices, int num_devices); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index dec33d4086..f867ed48a9 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -129,6 +129,7 @@ enum { PROP_MASTER, PROP_HW_ADDRESS, PROP_HAS_PENDING_ACTION, + PROP_METERED, LAST_PROP }; @@ -326,6 +327,8 @@ typedef struct { gboolean is_master; GSList * slaves; /* list of SlaveInfo */ + NMMetered metered; + NMConnectionProvider *con_provider; } NMDevicePrivate; @@ -673,6 +676,21 @@ nm_device_get_device_type (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type; } +/** + * nm_device_get_metered: + * @setting: the #NMDevice + * + * Returns: the #NMDevice:metered property of the device. + * + * Since: 1.2 + **/ +NMMetered +nm_device_get_metered (NMDevice *self) +{ + g_return_val_if_fail (NM_IS_DEVICE (self), NM_METERED_UNKNOWN); + + return NM_DEVICE_GET_PRIVATE (self)->metered; +} /** * nm_device_get_priority(): @@ -9092,6 +9110,9 @@ get_property (GObject *object, guint prop_id, case PROP_HAS_PENDING_ACTION: g_value_set_boolean (value, nm_device_has_pending_action (self)); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -9362,6 +9383,20 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the connection is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_DEVICE_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[STATE_CHANGED] = g_signal_new ("state-changed", diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 424b096528..9cdab4c3fc 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -59,6 +59,7 @@ #define NM_DEVICE_PHYSICAL_PORT_ID "physical-port-id" #define NM_DEVICE_MTU "mtu" #define NM_DEVICE_HW_ADDRESS "hw-address" +#define NM_DEVICE_METERED "metered" #define NM_DEVICE_TYPE_DESC "type-desc" /* Internal only */ #define NM_DEVICE_RFKILL_TYPE "rfkill-type" /* Internal only */ @@ -75,7 +76,6 @@ #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" #define NM_DEVICE_RECHECK_ASSUME "recheck-assume" - G_BEGIN_DECLS #define NM_TYPE_DEVICE (nm_device_get_type ()) @@ -282,6 +282,7 @@ const char * nm_device_get_driver_version (NMDevice *dev); const char * nm_device_get_type_desc (NMDevice *dev); const char * nm_device_get_type_description (NMDevice *dev); NMDeviceType nm_device_get_device_type (NMDevice *dev); +NMMetered nm_device_get_metered (NMDevice *dev); int nm_device_get_priority (NMDevice *dev); guint32 nm_device_get_ip4_route_metric (NMDevice *dev); From f208e7030f9b305bee4063d541163e47e2f51c06 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 30 Apr 2015 11:52:04 +0200 Subject: [PATCH 02/13] cli: add 'metered' property to device --- clients/cli/common.c | 17 +++++++++++++++++ clients/cli/common.h | 1 + clients/cli/devices.c | 4 +++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index 5b07360d74..d92aeaf263 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -521,6 +521,23 @@ nmc_device_state_to_string (NMDeviceState state) } } +const char * +nmc_device_metered_to_string (NMMetered value) +{ + switch (value) { + case NM_METERED_YES: + return _("yes"); + case NM_METERED_NO: + return _("no"); + case NM_METERED_GUESS_YES: + return _("yes (guessed)"); + case NM_METERED_GUESS_NO: + return _("no (guessed)"); + default: + return _("unknown"); + } +} + const char * nmc_device_reason_to_string (NMDeviceStateReason reason) { diff --git a/clients/cli/common.h b/clients/cli/common.h index 8bb043f4d7..bb3f44e851 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -35,6 +35,7 @@ NMIPRoute *nmc_parse_and_build_route (int family, const char *first, const char const char * nmc_device_state_to_string (NMDeviceState state); const char * nmc_device_reason_to_string (NMDeviceStateReason reason); +const char * nmc_device_metered_to_string (NMMetered value); char ** nmc_vlan_parse_priority_maps (const char *priority_map, diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 1c9f5881d6..fd2f7b6167 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -79,11 +79,12 @@ static NmcOutputField nmc_fields_dev_show_general[] = { {"CONNECTION", N_("CONNECTION"), 20}, /* 21 */ {"CON-UUID", N_("CON-UUID"), 38}, /* 22 */ {"CON-PATH", N_("CON-PATH"), 51}, /* 23 */ + {"METERED", N_("METERED"), 10}, /* 24 */ {NULL, NULL, 0} }; #define NMC_FIELDS_DEV_SHOW_GENERAL_ALL "NAME,DEVICE,TYPE,NM-TYPE,VENDOR,PRODUCT,DRIVER,DRIVER-VERSION,FIRMWARE-VERSION,HWADDR,MTU,"\ "STATE,REASON,UDI,IP-IFACE,IS-SOFTWARE,NM-MANAGED,AUTOCONNECT,FIRMWARE-MISSING,NM-PLUGIN-MISSING,"\ - "PHYS-PORT-ID,CONNECTION,CON-UUID,CON-PATH" + "PHYS-PORT-ID,CONNECTION,CON-UUID,CON-PATH,METERED" #define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" /* Available fields for 'device show' - CONNECTIONS part */ @@ -845,6 +846,7 @@ show_device_info (NMDevice *device, NmCli *nmc) set_val_strc (arr, 21, get_active_connection_id (device)); set_val_strc (arr, 22, acon ? nm_active_connection_get_uuid (acon) : NULL); set_val_strc (arr, 23, acon ? nm_object_get_path (NM_OBJECT (acon)) : NULL); + set_val_strc (arr, 24, nmc_device_metered_to_string (nm_device_get_metered (device))); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ From 6f647fe689ddc5102c7a4492740a96f043d4a478 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 27 Apr 2015 17:45:53 +0200 Subject: [PATCH 03/13] libnm-core: add 'metered' property to NMSettingConnection Add a 'metered' enum property to NMSettingConnection with possible values: unknown,yes,no. The value indicates the presence of limitations in the amount of traffic flowing through the connection. --- libnm-core/nm-setting-connection.c | 52 ++++++++++++++++++++++++++++++ libnm-core/nm-setting-connection.h | 3 ++ libnm-core/tests/test-general.c | 1 + libnm/libnm.ver | 1 + 4 files changed, 57 insertions(+) diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index 7bc927bc1d..b2274d36b0 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -74,6 +74,7 @@ typedef struct { char *zone; GSList *secondaries; /* secondary connections to activate with the base connection */ guint gateway_ping_timeout; + NMMetered metered; } NMSettingConnectionPrivate; enum { @@ -92,6 +93,7 @@ enum { PROP_SLAVE_TYPE, PROP_SECONDARIES, PROP_GATEWAY_PING_TIMEOUT, + PROP_METERED, LAST_PROP }; @@ -738,6 +740,23 @@ nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting) return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->gateway_ping_timeout; } +/** + * nm_setting_connection_get_metered: + * @setting: the #NMSettingConnection + * + * Returns: the #NMSettingConnection:metered property of the setting. + * + * Since: 1.2 + **/ +NMMetered +nm_setting_connection_get_metered (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), + NM_METERED_UNKNOWN); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->metered; +} + static void _set_error_missing_base_setting (GError **error, const char *type) { @@ -899,6 +918,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (priv->metered != NM_METERED_UNKNOWN && + priv->metered != NM_METERED_YES && + priv->metered != NM_METERED_NO) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("metered value %d is not valid"), priv->metered); + g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_METERED); + return FALSE; + } + /* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */ if (!priv->uuid) { @@ -1129,6 +1160,9 @@ set_property (GObject *object, guint prop_id, case PROP_GATEWAY_PING_TIMEOUT: priv->gateway_ping_timeout = g_value_get_uint (value); break; + case PROP_METERED: + priv->metered = g_value_get_enum (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1199,6 +1233,9 @@ get_property (GObject *object, guint prop_id, case PROP_GATEWAY_PING_TIMEOUT: g_value_set_uint (value, priv->gateway_ping_timeout); break; + case PROP_METERED: + g_value_set_enum (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1567,4 +1604,19 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingConnection:metered: + * + * Whether the connection is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_enum (NM_SETTING_CONNECTION_METERED, "", "", + NM_TYPE_METERED, + NM_METERED_UNKNOWN, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); } diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index d1ad0fe1e5..1692fe2c53 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -58,6 +58,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_SLAVE_TYPE "slave-type" #define NM_SETTING_CONNECTION_SECONDARIES "secondaries" #define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout" +#define NM_SETTING_CONNECTION_METERED "metered" /** * NMSettingConnection: @@ -119,6 +120,8 @@ void nm_setting_connection_remove_secondary (NMSettingConnection *set gboolean nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting, const char *sec_uuid); guint32 nm_setting_connection_get_gateway_ping_timeout (NMSettingConnection *setting); +NM_AVAILABLE_IN_1_2 +NMMetered nm_setting_connection_get_metered (NMSettingConnection *setting); G_END_DECLS diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 55e905d5e8..1aca515a88 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1967,6 +1967,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_SLAVE_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_SECONDARIES, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_METERED, NM_SETTING_DIFF_RESULT_IN_A }, { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN } } }, { NM_SETTING_WIRED_SETTING_NAME, { diff --git a/libnm/libnm.ver b/libnm/libnm.ver index c77069bc3c..34890414c3 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -853,6 +853,7 @@ global: nm_metered_get_type; nm_setting_802_1x_check_cert_scheme; nm_setting_bridge_get_multicast_snooping; + nm_setting_connection_get_metered; nm_setting_ip_config_add_dns_option; nm_setting_ip_config_clear_dns_options; nm_setting_ip_config_get_dns_option; From 609f4f37c0db111e7f9e2f452e0d442b41aa0298 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 1 Jun 2015 16:45:25 +0200 Subject: [PATCH 04/13] cli: add nmc_string_to_tristate() --- clients/cli/utils.c | 30 ++++++++++++++++++++++++++++++ clients/cli/utils.h | 7 +++++++ 2 files changed, 37 insertions(+) diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 727e309d1c..ce4749bfd1 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -517,6 +517,36 @@ nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) return TRUE; } +gboolean +nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) +{ + const char *s_true[] = { "true", "yes", "on", NULL }; + const char *s_false[] = { "false", "no", "off", NULL }; + const char *s_unknown[] = { "unknown", NULL }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (str, "o") == 0) { + g_set_error (error, 1, 0, + _("'%s' is ambiguous (on x off)"), str); + return FALSE; + } + + if (nmc_string_is_valid (str, s_true, NULL)) + *val = NMC_TRI_STATE_YES; + else if (nmc_string_is_valid (str, s_false, NULL)) + *val = NMC_TRI_STATE_NO; + else if (nmc_string_is_valid (str, s_unknown, NULL)) + *val = NMC_TRI_STATE_UNKNOWN; + else { + g_set_error (error, 1, 0, + _("'%s' is not valid; use [%s], [%s] or [%s]"), + str, "true, yes, on", "false, no, off", "unknown"); + return FALSE; + } + return TRUE; +} + /* * Ask user for input and return the string. * The caller is responsible for freeing the returned string. diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 6defb1de14..54f76b071a 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -32,6 +32,12 @@ typedef struct { gboolean found; } nmc_arg_t; +typedef enum { + NMC_TRI_STATE_NO, + NMC_TRI_STATE_YES, + NMC_TRI_STATE_UNKNOWN, +} NMCTriStateValue; + /* === Functions === */ int matches (const char *cmd, const char *pattern); int next_arg (int *argc, char ***argv); @@ -62,6 +68,7 @@ gboolean nmc_string_to_uint (const char *str, unsigned long int max, unsigned long int *value); gboolean nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error); +gboolean nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error); char *nmc_ip4_address_as_string (guint32 ip, GError **error); char *nmc_ip6_address_as_string (const struct in6_addr *ip, GError **error); void nmc_terminal_erase_line (void); From f0aebfd7462dea9b3ebc579a49248027f6d23905 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 30 Apr 2015 09:58:20 +0200 Subject: [PATCH 05/13] cli: add support for 'metered' connection property --- clients/cli/settings.c | 57 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index e0ae53904f..58149a37c4 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -59,6 +59,7 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SLAVE_TYPE, 20), /* 12 */ SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 13 */ SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 14 */ + SETTING_FIELD (NM_SETTING_CONNECTION_METERED, 10), /* 15 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ @@ -75,7 +76,8 @@ NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_MASTER","\ NM_SETTING_CONNECTION_SLAVE_TYPE","\ NM_SETTING_CONNECTION_SECONDARIES","\ - NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT + NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT","\ + NM_SETTING_CONNECTION_METERED #define NMC_FIELDS_SETTING_CONNECTION_COMMON NMC_FIELDS_SETTING_CONNECTION_ALL /* Available fields for NM_SETTING_WIRED_SETTING_NAME */ @@ -2691,6 +2693,51 @@ nmc_property_connection_describe_secondaries (NMSetting *setting, const char *pr "Example: private-openvpn, fe6ba5d8-c2fc-4aae-b2e3-97efddd8d9a7\n"); } +/* 'metered' */ +static char * +nmc_property_connection_get_metered (NMSetting *setting, NmcPropertyGetType get_type) +{ + NMSettingConnection *s_conn = NM_SETTING_CONNECTION (setting); + + switch (nm_setting_connection_get_metered (s_conn)) { + case NM_METERED_YES: + return g_strdup (_("yes")); + case NM_METERED_NO: + return g_strdup (_("no")); + case NM_METERED_UNKNOWN: + default: + return g_strdup (_("unknown")); + } +} + +static gboolean +nmc_property_connection_set_metered (NMSetting *setting, const char *prop, + const char *val, GError **error) +{ + NMMetered metered; + NMCTriStateValue ts_val; + + if (!nmc_string_to_tristate (val, &ts_val, error)) + return FALSE; + + switch (ts_val) { + case NMC_TRI_STATE_YES: + metered = NM_METERED_YES; + break; + case NMC_TRI_STATE_NO: + metered = NM_METERED_NO; + break; + case NMC_TRI_STATE_UNKNOWN: + metered = NM_METERED_UNKNOWN; + break; + default: + g_assert_not_reached(); + } + + g_object_set (setting, prop, metered, NULL); + return TRUE; +} + /* --- NM_SETTING_802_1X_SETTING_NAME property setter functions --- */ #define DEFINE_SETTER_STR_LIST(def_func, set_func) \ static gboolean \ @@ -5372,6 +5419,13 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (CONNECTION, METERED), + nmc_property_connection_get_metered, + nmc_property_connection_set_metered, + NULL, + NULL, + NULL, + NULL); /* Add editable properties for NM_SETTING_DCB_SETTING_NAME */ nmc_add_prop_funcs (GLUE (DCB, APP_FCOE_FLAGS), @@ -6752,6 +6806,7 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 12, nmc_property_connection_get_slave_type (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 13, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 14, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 15, nmc_property_connection_get_metered (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ From 68db65b727bdfb7e560a82566348a256b848bbde Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 30 Apr 2015 18:11:16 +0200 Subject: [PATCH 06/13] core: add 'metered' flag to NMIP4Config Some DHCP servers send specific options to give a hint that clients should avoid unneeded data usage. Add a metered flag to NMIP4Config to keep track of this information. --- src/nm-ip4-config.c | 30 ++++++++++++++++++++++++++++++ src/nm-ip4-config.h | 4 ++++ 2 files changed, 34 insertions(+) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 5426e23019..1e24b2ec67 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -59,6 +59,7 @@ typedef struct { NMIPConfigSource mtu_source; int ifindex; gint64 route_metric; + gboolean metered; } NMIP4ConfigPrivate; /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */ @@ -607,6 +608,10 @@ nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src) for (i = 0; i < nm_ip4_config_get_num_wins (src); i++) nm_ip4_config_add_wins (dst, nm_ip4_config_get_wins (src, i)); + /* metered flag */ + nm_ip4_config_set_metered (dst, nm_ip4_config_get_metered (dst) || + nm_ip4_config_get_metered (src)); + g_object_thaw_notify (G_OBJECT (dst)); } @@ -1117,6 +1122,12 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev has_minor_changes = TRUE; } + /* metered */ + if (src_priv->metered != dst_priv->metered) { + dst_priv->metered = src_priv->metered; + has_minor_changes = TRUE; + } + /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */ g_assert (config_equal == !has_relevant_changes); @@ -1192,6 +1203,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail) } g_message (" n-dflt: %d", nm_ip4_config_get_never_default (config)); + g_message (" mtrd: %d", (int) nm_ip4_config_get_metered (config)); } gboolean @@ -1906,6 +1918,24 @@ nm_ip4_config_get_mtu_source (const NMIP4Config *config) /******************************************************************/ +void +nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + priv->metered = !!metered; +} + +gboolean +nm_ip4_config_get_metered (const NMIP4Config *config) +{ + NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); + + return priv->metered; +} + +/******************************************************************/ + static inline void hash_u32 (GChecksum *sum, guint32 n) { diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 991725b5a1..69880c1322 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -161,6 +161,10 @@ void nm_ip4_config_set_mtu (NMIP4Config *config, guint32 mtu, NMIPConfigSource s guint32 nm_ip4_config_get_mtu (const NMIP4Config *config); NMIPConfigSource nm_ip4_config_get_mtu_source (const NMIP4Config *config); +/* Metered */ +void nm_ip4_config_set_metered (NMIP4Config *config, gboolean metered); +gboolean nm_ip4_config_get_metered (const NMIP4Config *config); + void nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only); gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b); From 3c2f4a17f9255e2c6da6e6122159a9f993b80480 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 21 May 2015 08:40:02 +0200 Subject: [PATCH 07/13] systemd/dhcp: add support for vendor specific DHCP option This adds support for DHCP option 43 (Vendor Specific Information) to the internal DHCP client. The option carries an opaque object of n octets, interpreted by vendor-specific code on the clients and servers. --- .../libsystemd-network/dhcp-lease-internal.h | 2 + .../src/libsystemd-network/dhcp-protocol.h | 1 + .../src/libsystemd-network/sd-dhcp-lease.c | 52 +++++++++++++++++++ src/systemd/src/systemd/sd-dhcp-lease.h | 1 + 4 files changed, 56 insertions(+) diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h index 2d7bf03bab..91843beae5 100644 --- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h @@ -74,6 +74,8 @@ struct sd_dhcp_lease { char *root_path; uint8_t *client_id; size_t client_id_len; + uint8_t *vendor_specific; + size_t vendor_specific_size; }; int dhcp_lease_new(sd_dhcp_lease **ret); diff --git a/src/systemd/src/libsystemd-network/dhcp-protocol.h b/src/systemd/src/libsystemd-network/dhcp-protocol.h index da483feadf..3a1e473afa 100644 --- a/src/systemd/src/libsystemd-network/dhcp-protocol.h +++ b/src/systemd/src/libsystemd-network/dhcp-protocol.h @@ -127,6 +127,7 @@ enum { DHCP_OPTION_BROADCAST = 28, DHCP_OPTION_STATIC_ROUTE = 33, DHCP_OPTION_NTP_SERVER = 42, + DHCP_OPTION_VENDOR_SPECIFIC = 43, DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, DHCP_OPTION_OVERLOAD = 52, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index ee6d72dffc..ae44415df2 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -180,6 +180,19 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes return 0; } +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data) { + assert_return(lease, -EINVAL); + assert_return(data, -EINVAL); + + if (lease->vendor_specific) { + *data = lease->vendor_specific; + return lease->vendor_specific_size; + } else + return -ENOENT; + + return 0; +} + sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -275,6 +288,24 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { return 0; } +static int lease_parse_binary(const uint8_t *option, size_t len, uint8_t **ret) { + assert (option); + assert (ret); + + if (len >= 1) { + uint8_t *data; + + data = memdup(option, len); + if (!data) + return -errno; + + free(*ret); + *ret = data; + } + + return 0; +} + static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) { assert(option); assert(ret); @@ -571,6 +602,14 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, return r; break; + + case DHCP_OPTION_VENDOR_SPECIFIC: + r = lease_parse_binary(option, len, &lease->vendor_specific); + if (r < 0) + return r; + lease->vendor_specific_size = len; + + break; } return 0; @@ -598,6 +637,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const uint8_t *client_id; size_t client_id_len; const char *string; + uint8_t *data; uint16_t mtu; struct sd_dhcp_route *routes; int r; @@ -670,6 +710,18 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) serialize_dhcp_routes(f, "ROUTES", routes, r); + r = sd_dhcp_lease_get_vendor_specific(lease, &data); + if (r >= 0) { + _cleanup_free_ char *option_hex = NULL; + + option_hex = hexmem(data, r); + if (!option_hex) { + r = -ENOMEM; + goto finish; + } + fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); + } + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { _cleanup_free_ char *client_id_hex = NULL; diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h index 80d32134b1..28ee120bcb 100644 --- a/src/systemd/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/src/systemd/sd-dhcp-lease.h @@ -47,6 +47,7 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn); +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, uint8_t **data); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, size_t *client_id_len); From 1e39b2320dfe42ca33dfbf12746d63935818ac5a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 6 May 2015 14:07:21 +0200 Subject: [PATCH 08/13] dhcp: detect NMIP4Config 'metered' flag based on ANDROID_METERED DHCP option Some versions of Android's DHCP server send option 43 (Vendor specific information) with value "ANDROID_METERED" in Wi-Fi hotspot mode. Mark the NMIP4Config as metered when such option is received. --- src/dhcp-manager/nm-dhcp-systemd.c | 7 +++++++ src/dhcp-manager/nm-dhcp-utils.c | 3 +++ src/dhcp-manager/tests/test-dhcp-utils.c | 25 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index de5bf2a9be..05de2b340b 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -226,6 +226,8 @@ lease_to_ip4_config (const char *iface, guint16 mtu; int r, num; guint64 end_time; + uint8_t *data; + gboolean metered = FALSE; g_return_val_if_fail (lease != NULL, NULL); @@ -357,6 +359,11 @@ lease_to_ip4_config (const char *iface, g_string_free (l, TRUE); } + num = sd_dhcp_lease_get_vendor_specific (lease, &data); + if (num > 0) + metered = !!memmem (data, num, "ANDROID_METERED", STRLEN ("ANDROID_METERED")); + nm_ip4_config_set_metered (ip4_config, metered); + return ip4_config; } diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c index e50b4e3b8b..4dde1a4454 100644 --- a/src/dhcp-manager/nm-dhcp-utils.c +++ b/src/dhcp-manager/nm-dhcp-utils.c @@ -576,6 +576,9 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, g_strfreev (nis); } + str = g_hash_table_lookup (options, "vendor_encapsulated_options"); + nm_ip4_config_set_metered (ip4_config, str && strstr (str, "ANDROID_METERED")); + return ip4_config; error: diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c index 286c1d7476..3cdae732a7 100644 --- a/src/dhcp-manager/tests/test-dhcp-utils.c +++ b/src/dhcp-manager/tests/test-dhcp-utils.c @@ -175,6 +175,30 @@ test_wins_options (void) g_hash_table_destroy (options); } +static void +test_vendor_option_metered (void) +{ + GHashTable *options; + NMIP4Config *ip4_config; + static const Option data[] = { + { "vendor_encapsulated_options", "ANDROID_METERED" }, + { NULL, NULL } + }; + + options = fill_table (generic_options, NULL); + ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); + g_assert (ip4_config); + g_assert (nm_ip4_config_get_metered (ip4_config) == FALSE); + g_hash_table_destroy (options); + + options = fill_table (generic_options, NULL); + options = fill_table (data, options); + ip4_config = nm_dhcp_utils_ip4_config_from_options (1, "eth0", options, 0); + g_assert (ip4_config); + g_assert (nm_ip4_config_get_metered (ip4_config) == TRUE); + g_hash_table_destroy (options); +} + static void ip4_test_route (NMIP4Config *ip4_config, guint route_num, @@ -716,6 +740,7 @@ int main (int argc, char **argv) g_test_add_func ("/dhcp/ip4-missing-prefix-8", test_ip4_missing_prefix_8); g_test_add_func ("/dhcp/ip4-prefix-classless", test_ip4_prefix_classless); g_test_add_func ("/dhcp/client-id-from-string", test_client_id_from_string); + g_test_add_func ("/dhcp/vendor-option-metered", test_vendor_option_metered); return g_test_run (); } From a86255a0432b2ba88f5d629bffb79dd05bd7590a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 30 Apr 2015 14:18:14 +0200 Subject: [PATCH 09/13] core: update device 'metered' property on connection state change The metered property of a NMDevice that reaches the activated state is copied from the active connection and if its value is 'unknown' some heuristics are used to guess the actual value. When the connection is torn down the metered property is reset to 'unknown'. --- src/devices/nm-device.c | 60 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index f867ed48a9..63baa63270 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -75,6 +75,7 @@ _LOG_DECLARE_SELF (NMDevice); static void impl_device_disconnect (NMDevice *self, DBusGMethodInvocation *context); static void impl_device_delete (NMDevice *self, DBusGMethodInvocation *context); +static void nm_device_update_metered (NMDevice *self); #include "nm-device-glue.h" @@ -3362,8 +3363,10 @@ dhcp4_state_changed (NMDhcpClient *client, if (priv->ip4_state == IP_CONF) nm_device_activate_schedule_ip4_config_result (self, ip4_config); - else if (priv->ip4_state == IP_DONE) + else if (priv->ip4_state == IP_DONE) { dhcp4_lease_change (self, ip4_config); + nm_device_update_metered (self); + } break; case NM_DHCP_STATE_TIMEOUT: dhcp4_fail (self, TRUE); @@ -7292,6 +7295,59 @@ nm_device_set_dhcp_anycast_address (NMDevice *self, const char *addr) priv->dhcp_anycast_address = g_strdup (addr); } +static void +nm_device_update_metered (NMDevice *self) +{ +#define NM_METERED_INVALID ((NMMetered) -1) + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMSettingConnection *setting; + NMMetered conn_value, value = NM_METERED_INVALID; + NMConnection *connection = NULL; + NMDeviceState state; + + g_return_if_fail (NM_IS_DEVICE (self)); + + state = nm_device_get_state (self); + if ( state <= NM_DEVICE_STATE_DISCONNECTED + || state > NM_DEVICE_STATE_ACTIVATED) + value = NM_METERED_UNKNOWN; + + if (value == NM_METERED_INVALID) { + connection = nm_device_get_connection (self); + if (connection) { + setting = nm_connection_get_setting_connection (connection); + if (setting) { + conn_value = nm_setting_connection_get_metered (setting); + if (conn_value != NM_METERED_UNKNOWN) + value = conn_value; + } + } + } + + /* Try to guess a value using the metered flag in IP configuration */ + if (value == NM_METERED_INVALID) { + if ( priv->ip4_config + && priv->ip4_state == IP_DONE + && nm_ip4_config_get_metered (priv->ip4_config)) + value = NM_METERED_GUESS_YES; + } + + /* Otherwise look at connection type */ + if (value == NM_METERED_INVALID) { + if ( nm_connection_is_type (connection, NM_SETTING_GSM_SETTING_NAME) + || nm_connection_is_type (connection, NM_SETTING_CDMA_SETTING_NAME)) + value = NM_METERED_GUESS_YES; + else + value = NM_METERED_GUESS_NO; + } + + if (value != priv->metered) { + _LOGD (LOGD_DEVICE, "set metered value %d", value); + priv->metered = value; + g_object_notify (G_OBJECT (self), NM_DEVICE_METERED); + } +} + /** * nm_device_check_connection_available(): * @self: the #NMDevice @@ -7749,6 +7805,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, gboolean deconfig nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } + nm_device_update_metered (self); _cleanup_generic_post (self, deconfigure); } @@ -8221,6 +8278,7 @@ _set_state_full (NMDevice *self, break; case NM_DEVICE_STATE_ACTIVATED: _LOGI (LOGD_DEVICE, "Activation: successful, device activated."); + nm_device_update_metered (self); nm_dispatcher_call (DISPATCHER_ACTION_UP, nm_act_request_get_connection (req), self, NULL, NULL, NULL); break; case NM_DEVICE_STATE_FAILED: From 862fd91df06087257dfa2b647172bafc60328487 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 29 May 2015 17:37:26 +0200 Subject: [PATCH 10/13] ifcfg-rh: change type of svTrueValue() return value and argument Change type of return value and 'def' argument of svTrueValue() to gint to make clear that it can be something different from TRUE and FALSE. --- src/settings/plugins/ifcfg-rh/shvar.c | 6 +++--- src/settings/plugins/ifcfg-rh/shvar.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index 4a5ca1d186..283aa82611 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -303,11 +303,11 @@ svGetValueFull (shvarFile *s, const char *key, gboolean verbatim) * return FALSE if resolves to any non-truth value (e.g. "no", "n", "false") * return otherwise */ -gboolean -svTrueValue (shvarFile *s, const char *key, gboolean def) +gint +svTrueValue (shvarFile *s, const char *key, gint def) { char *tmp; - gboolean returnValue = def; + gint returnValue = def; tmp = svGetValue (s, key, FALSE); if (!tmp) diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 4902541b00..de7a358556 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -62,7 +62,7 @@ char *svGetValueFull (shvarFile *s, const char *key, gboolean verbatim); * return FALSE if resolves to any non-truth value (e.g. "no", "n", "false") * return otherwise */ -gboolean svTrueValue (shvarFile *s, const char *key, gboolean def); +gint svTrueValue (shvarFile *s, const char *key, gint def); gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback); From 7e5e624dafc9ad4e6ec2dc32cc0d7013476ca2c6 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 29 May 2015 17:41:21 +0200 Subject: [PATCH 11/13] ifcfg-rh: add support for CONNECTION_METERED --- libnm-core/nm-setting-connection.c | 8 ++++++++ src/settings/plugins/ifcfg-rh/reader.c | 9 +++++++++ src/settings/plugins/ifcfg-rh/writer.c | 11 +++++++++++ 3 files changed, 28 insertions(+) diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index b2274d36b0..5812a0a442 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -1612,6 +1612,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * * Since: 1.2 **/ + /* ---ifcfg-rh--- + * property: metered + * variable: CONNECTION_METERED + * values: yes,no,unknown + * description: Whether the device is metered + * example: CONNECTION_METERED=yes + * ---end--- + */ g_object_class_install_property (object_class, PROP_METERED, g_param_spec_enum (NM_SETTING_CONNECTION_METERED, "", "", diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 71bb2b942d..2387be3fcd 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -248,6 +248,15 @@ make_connection_setting (const char *file, g_free (value); } + switch (svTrueValue (ifcfg, "CONNECTION_METERED", -1)) { + case TRUE: + g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_YES, NULL); + break; + case FALSE: + g_object_set (s_con, NM_SETTING_CONNECTION_METERED, NM_METERED_NO, NULL); + break; + } + return NM_SETTING (s_con); } diff --git a/src/settings/plugins/ifcfg-rh/writer.c b/src/settings/plugins/ifcfg-rh/writer.c index e432d1b95e..df2308e405 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1777,6 +1777,17 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg) svSetValue (ifcfg, "GATEWAY_PING_TIMEOUT", tmp, FALSE); g_free (tmp); } + + switch (nm_setting_connection_get_metered (s_con)) { + case NM_METERED_YES: + svSetValue (ifcfg, "CONNECTION_METERED", "yes", FALSE); + break; + case NM_METERED_NO: + svSetValue (ifcfg, "CONNECTION_METERED", "no", FALSE); + break; + default: + svSetValue (ifcfg, "CONNECTION_METERED", NULL, FALSE); + } } static gboolean From bd4e1f37f88baddc743b79b68886112882deca9a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Mon, 1 Jun 2015 17:35:47 +0200 Subject: [PATCH 12/13] cli: fix error message localization Valid values must not be translated in error messages generated in nmc_string_to_bool() and nmc_string_to_tristate(). --- clients/cli/utils.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/clients/cli/utils.c b/clients/cli/utils.c index ce4749bfd1..575ece7c0c 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -500,7 +500,10 @@ nmc_string_to_bool (const char *str, gboolean *val_bool, GError **error) if (g_strcmp0 (str, "o") == 0) { g_set_error (error, 1, 0, - _("'%s' is ambiguous (on x off)"), str); + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); return FALSE; } @@ -528,7 +531,10 @@ nmc_string_to_tristate (const char *str, NMCTriStateValue *val, GError **error) if (g_strcmp0 (str, "o") == 0) { g_set_error (error, 1, 0, - _("'%s' is ambiguous (on x off)"), str); + /* Translators: the first %s is the partial value entered by + * the user, the second %s a list of compatible values. + */ + _("'%s' is ambiguous (%s)"), str, "on x off"); return FALSE; } From 04d5804dd5826cc36398027a023b0e639a54bd26 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 3 Jun 2015 09:15:24 +0200 Subject: [PATCH 13/13] nm-manager: add 'metered' property This introduces a global metered property which makes easier for clients to obtain the metered status of the current primary connection. --- introspection/nm-manager.xml | 8 +++++ libnm/nm-client.c | 16 +++++++++ libnm/nm-client.h | 1 + libnm/nm-manager.c | 19 ++++++++++ libnm/nm-manager.h | 1 + src/nm-active-connection.c | 24 +++++++++++++ src/nm-active-connection.h | 6 +++- src/nm-manager.c | 67 +++++++++++++++++++++++++++++++++++- src/nm-manager.h | 1 + 9 files changed, 141 insertions(+), 2 deletions(-) diff --git a/introspection/nm-manager.xml b/introspection/nm-manager.xml index a9bb6dcf14..f0d04438d5 100644 --- a/introspection/nm-manager.xml +++ b/introspection/nm-manager.xml @@ -335,6 +335,14 @@ + + + Wheter the connectivity is metered. This is equivalent to the + metered property of the device associated with the primary + connection. + + + The object path of an active connection that is currently diff --git a/libnm/nm-client.c b/libnm/nm-client.c index e0a2a7b4f4..3d683a1c67 100644 --- a/libnm/nm-client.c +++ b/libnm/nm-client.c @@ -76,6 +76,7 @@ enum { PROP_CONNECTIONS, PROP_HOSTNAME, PROP_CAN_MODIFY, + PROP_METERED, LAST_PROP }; @@ -1871,6 +1872,7 @@ get_property (GObject *object, guint prop_id, case PROP_PRIMARY_CONNECTION: case PROP_ACTIVATING_CONNECTION: case PROP_DEVICES: + case PROP_METERED: g_object_get_property (G_OBJECT (NM_CLIENT_GET_PRIVATE (object)->manager), pspec->name, value); break; @@ -2143,6 +2145,20 @@ nm_client_class_init (NMClientClass *client_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMClient:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_CLIENT_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ /** diff --git a/libnm/nm-client.h b/libnm/nm-client.h index 619ad8585f..fc309fa1a0 100644 --- a/libnm/nm-client.h +++ b/libnm/nm-client.h @@ -56,6 +56,7 @@ G_BEGIN_DECLS #define NM_CLIENT_CONNECTIONS "connections" #define NM_CLIENT_HOSTNAME "hostname" #define NM_CLIENT_CAN_MODIFY "can-modify" +#define NM_CLIENT_METERED "metered" #define NM_CLIENT_DEVICE_ADDED "device-added" #define NM_CLIENT_DEVICE_REMOVED "device-removed" diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c index 003d6b8755..2fd40273a3 100644 --- a/libnm/nm-manager.c +++ b/libnm/nm-manager.c @@ -64,6 +64,7 @@ typedef struct { NMConnectivityState connectivity; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; + NMMetered metered; GCancellable *perm_call_cancellable; GHashTable *permissions; @@ -102,6 +103,7 @@ enum { PROP_PRIMARY_CONNECTION, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_METERED, LAST_PROP }; @@ -179,6 +181,7 @@ init_dbus (NMObject *object) { NM_MANAGER_PRIMARY_CONNECTION, &priv->primary_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_MANAGER_ACTIVATING_CONNECTION, &priv->activating_connection, NULL, NM_TYPE_ACTIVE_CONNECTION }, { NM_MANAGER_DEVICES, &priv->devices, NULL, NM_TYPE_DEVICE, "device" }, + { NM_MANAGER_METERED, &priv->metered }, { NULL }, }; @@ -1537,6 +1540,9 @@ get_property (GObject *object, case PROP_DEVICES: g_value_take_boxed (value, _nm_utils_copy_object_array (nm_manager_get_devices (self))); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1669,6 +1675,19 @@ nm_manager_class_init (NMManagerClass *manager_class) G_TYPE_PTR_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_MANAGER_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); /* signals */ diff --git a/libnm/nm-manager.h b/libnm/nm-manager.h index ca9f7dd60b..8d04a060f6 100644 --- a/libnm/nm-manager.h +++ b/libnm/nm-manager.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_MANAGER_PRIMARY_CONNECTION "primary-connection" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" #define NM_MANAGER_DEVICES "devices" +#define NM_MANAGER_METERED "metered" typedef struct { NMObject parent; diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index d0b5b8753f..2b7296196d 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -100,6 +100,7 @@ enum { enum { DEVICE_CHANGED, + DEVICE_METERED_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -401,6 +402,18 @@ device_master_changed (GObject *object, } } +static void +device_metered_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMActiveConnection *self = (NMActiveConnection *) user_data; + NMDevice *device = NM_DEVICE (object); + + g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self)); + g_signal_emit (self, signals[DEVICE_METERED_CHANGED], 0, nm_device_get_metered (device)); +} + gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) { @@ -427,6 +440,8 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) G_CALLBACK (device_state_changed), self); g_signal_connect (device, "notify::master", G_CALLBACK (device_master_changed), self); + g_signal_connect (device, "notify::" NM_DEVICE_METERED, + G_CALLBACK (device_metered_changed), self); if (!priv->assumed) { priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self); @@ -837,6 +852,7 @@ _device_cleanup (NMActiveConnection *self) if (priv->device) { g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_state_changed), self); g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_master_changed), self); + g_signal_handlers_disconnect_by_func (priv->device, G_CALLBACK (device_metered_changed), self); } if (priv->pending_activation_id) { @@ -1042,6 +1058,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) NULL, NULL, NULL, G_TYPE_NONE, 2, NM_TYPE_DEVICE, NM_TYPE_DEVICE); + signals[DEVICE_METERED_CHANGED] = + g_signal_new (NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMActiveConnectionClass, device_metered_changed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_UINT); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (ac_class), &dbus_glib_nm_active_connection_object_info); diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h index 710cfeed9f..4db5e6ff56 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -57,7 +57,8 @@ #define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" /* Internal signals*/ -#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED "device-changed" +#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED "device-changed" +#define NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED "device-metered-changed" struct _NMActiveConnection { GObject parent; @@ -78,6 +79,9 @@ typedef struct { void (*device_changed) (NMActiveConnection *connection, NMDevice *new_device, NMDevice *old_device); + + void (*device_metered_changed) (NMActiveConnection *connection, + NMMetered new_value); } NMActiveConnectionClass; GType nm_active_connection_get_type (void); diff --git a/src/nm-manager.c b/src/nm-manager.c index 5e2aff8c65..bf9e69a368 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -157,6 +157,7 @@ typedef struct { guint ac_cleanup_id; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; + NMMetered metered; GSList *devices; NMState state; @@ -230,6 +231,7 @@ enum { PROP_PRIMARY_CONNECTION_TYPE, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_METERED, /* Not exported */ PROP_HOSTNAME, @@ -654,6 +656,30 @@ find_best_device_state (NMManager *manager) return best_state; } +static void +nm_manager_update_metered (NMManager *manager) +{ + NMManagerPrivate *priv; + NMDevice *device; + NMMetered value = NM_METERED_UNKNOWN; + + g_return_if_fail (NM_IS_MANAGER (manager)); + priv = NM_MANAGER_GET_PRIVATE (manager); + + if (priv->primary_connection) { + device = nm_active_connection_get_device (priv->primary_connection); + if (device) + value = nm_device_get_metered (device); + } + + if (value != priv->metered) { + priv->metered = value; + nm_log_dbg (LOGD_CORE, "New manager metered value: %d", + (int) priv->metered); + g_object_notify (G_OBJECT (manager), NM_MANAGER_METERED); + } +} + static void nm_manager_update_state (NMManager *manager) { @@ -4055,6 +4081,14 @@ firmware_dir_changed (GFileMonitor *monitor, } } +static void +connection_metered_changed (GObject *object, + NMMetered metered, + gpointer user_data) +{ + nm_manager_update_metered (NM_MANAGER (user_data)); +} + static void policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { @@ -4077,11 +4111,23 @@ policy_default_device_changed (GObject *object, GParamSpec *pspec, gpointer user ac = NULL; if (ac != priv->primary_connection) { - g_clear_object (&priv->primary_connection); + if (priv->primary_connection) { + g_signal_handlers_disconnect_by_func (priv->primary_connection, + G_CALLBACK (connection_metered_changed), + self); + g_clear_object (&priv->primary_connection); + } + priv->primary_connection = ac ? g_object_ref (ac) : NULL; + + if (priv->primary_connection) { + g_signal_connect (priv->primary_connection, NM_ACTIVE_CONNECTION_DEVICE_METERED_CHANGED, + G_CALLBACK (connection_metered_changed), self); + } nm_log_dbg (LOGD_CORE, "PrimaryConnection now %s", ac ? nm_active_connection_get_id (ac) : "(none)"); g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION); g_object_notify (G_OBJECT (self), NM_MANAGER_PRIMARY_CONNECTION_TYPE); + nm_manager_update_metered (self); } } @@ -4649,6 +4695,8 @@ nm_manager_init (NMManager *manager) /* Update timestamps in active connections */ priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager); + + priv->metered = NM_METERED_UNKNOWN; } static void @@ -4733,6 +4781,9 @@ get_property (GObject *object, guint prop_id, } g_value_take_boxed (value, array); break; + case PROP_METERED: + g_value_set_uint (value, priv->metered); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5011,6 +5062,20 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_METERED, + g_param_spec_uint (NM_MANAGER_METERED, "", "", + 0, G_MAXUINT32, NM_METERED_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /* signals */ signals[DEVICE_ADDED] = g_signal_new ("device-added", diff --git a/src/nm-manager.h b/src/nm-manager.h index 3b00e8053a..a8e7d7ad32 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -51,6 +51,7 @@ #define NM_MANAGER_PRIMARY_CONNECTION_TYPE "primary-connection-type" #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection" #define NM_MANAGER_DEVICES "devices" +#define NM_MANAGER_METERED "metered" /* Not exported */ #define NM_MANAGER_HOSTNAME "hostname"