From 77a7e913a20f44f417c8f96de3086d48beec5fe7 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 (cherry picked from commit bbbf5229413f364dba8646ca582f41e7eef513d5) --- introspection/nm-device.xml | 34 ++++++++++++++++++++++++++++++ libnm-core/nm-dbus-interface.h | 21 ++++++++++++++++++- libnm/libnm.ver | 6 ++++++ 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, 138 insertions(+), 2 deletions(-) diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index e405a384ba..b2e3827e2a 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -139,6 +139,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. + + @@ -654,6 +660,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..8c5cd3c4c1 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,26 @@ 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.0.6 + **/ +NM_AVAILABLE_IN_1_0_6 +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 294bab4109..b33f11812b 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -851,3 +851,9 @@ global: nm_setting_connection_get_autoconnect_slaves; } libnm_1_0_0; +libnm_1_0_6 { +global: + nm_device_get_metered; + nm_metered_get_type; +} libnm_1_0_4; + diff --git a/libnm/nm-device.c b/libnm/nm-device.c index cd14c0e902..d3ac705c1c 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; @@ -130,6 +131,7 @@ enum { PROP_AVAILABLE_CONNECTIONS, PROP_PHYSICAL_PORT_ID, PROP_MTU, + PROP_METERED, LAST_PROP }; @@ -193,6 +195,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 }, @@ -449,6 +452,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; @@ -793,6 +799,20 @@ nm_device_class_init (NMDeviceClass *device_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the device is metered. + * + * Since: 1.0.6 + **/ + 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 */ /** @@ -1883,6 +1903,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.0.6 + **/ +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 5cc3735695..a61dcf3234 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -60,6 +60,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; @@ -119,6 +120,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_0_6 +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 75f4a2972d..4c4e04108d 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -128,6 +128,7 @@ enum { PROP_MASTER, PROP_HW_ADDRESS, PROP_HAS_PENDING_ACTION, + PROP_METERED, LAST_PROP }; @@ -338,6 +339,8 @@ typedef struct { gboolean is_master; GSList * slaves; /* list of SlaveInfo */ + NMMetered metered; + NMConnectionProvider *con_provider; } NMDevicePrivate; @@ -687,6 +690,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.0.6 + **/ +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(): @@ -9298,6 +9316,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; @@ -9561,6 +9582,20 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMDevice:metered: + * + * Whether the connection is metered. + * + * Since: 1.0.6 + **/ + 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 f1dab9bf21..e9d5b9488d 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -58,6 +58,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 */ @@ -74,7 +75,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 ()) @@ -281,6 +281,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 e16c0682898bfa899d440b6325f13e9546981614 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 (cherry picked from commit f208e7030f9b305bee4063d541163e47e2f51c06) --- clients/cli/common.c | 17 +++++++++++++++++ clients/cli/common.h | 1 + clients/cli/devices.c | 5 ++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index 97d0bb940e..35676d0f2b 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 979c590635..41564a5ab7 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -78,11 +78,12 @@ static NmcOutputField nmc_fields_dev_show_general[] = { {"CONNECTION", N_("CONNECTION"), 20}, /* 20 */ {"CON-UUID", N_("CON-UUID"), 38}, /* 21 */ {"CON-PATH", N_("CON-PATH"), 51}, /* 22 */ + {"METERED", N_("METERED"), 10}, /* 23 */ {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,PHYS-PORT-ID,"\ - "CONNECTION,CON-UUID,CON-PATH" + "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 */ @@ -858,6 +859,8 @@ show_device_info (NMDevice *device, NmCli *nmc) set_val_strc (arr, 20, get_active_connection_id (device)); set_val_strc (arr, 21, acon ? nm_active_connection_get_uuid (acon) : NULL); set_val_strc (arr, 22, acon ? nm_object_get_path (NM_OBJECT (acon)) : NULL); + set_val_strc (arr, 23, 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 4eb05767ee125dd388881a3154a0f6366f40fbf0 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. (cherry picked from commit 6f647fe689ddc5102c7a4492740a96f043d4a478) --- 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 07b3401d1e..b73d2289f0 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -76,6 +76,7 @@ typedef struct { char *zone; GSList *secondaries; /* secondary connections to activate with the base connection */ guint gateway_ping_timeout; + NMMetered metered; } NMSettingConnectionPrivate; enum { @@ -95,6 +96,7 @@ enum { PROP_AUTOCONNECT_SLAVES, PROP_SECONDARIES, PROP_GATEWAY_PING_TIMEOUT, + PROP_METERED, LAST_PROP }; @@ -760,6 +762,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.0.6 + **/ +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) { @@ -921,6 +940,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) { @@ -1154,6 +1185,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; @@ -1227,6 +1261,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; @@ -1626,4 +1663,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.0.6 + **/ + 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 ff89d20081..24b5f6accc 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -59,6 +59,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES "autoconnect-slaves" #define NM_SETTING_CONNECTION_SECONDARIES "secondaries" #define NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT "gateway-ping-timeout" +#define NM_SETTING_CONNECTION_METERED "metered" /* Types for property values */ /** @@ -142,6 +143,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_0_6 +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 bd144f384b..a077740dc9 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -1969,6 +1969,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, 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 b33f11812b..ecdfc3416c 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -855,5 +855,6 @@ libnm_1_0_6 { global: nm_device_get_metered; nm_metered_get_type; + nm_setting_connection_get_metered; } libnm_1_0_4; From 5496a915751a95ea2a5b61844364f473d5a38b3b 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() (cherry picked from commit 609f4f37c0db111e7f9e2f452e0d442b41aa0298) --- 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 dfd002edce..43d69b63ba 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -424,6 +424,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 77e697af66..ba649f8fae 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 a4cdf4a63c29d0e442fe4cac7e4294a554f1c9a2 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 (cherry picked from commit f0aebfd7462dea9b3ebc579a49248027f6d23905) --- 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 66759dfc56..6ede34c42c 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -60,6 +60,7 @@ NmcOutputField nmc_fields_setting_connection[] = { SETTING_FIELD (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, 13), /* 13 */ SETTING_FIELD (NM_SETTING_CONNECTION_SECONDARIES, 40), /* 14 */ SETTING_FIELD (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, 30), /* 15 */ + SETTING_FIELD (NM_SETTING_CONNECTION_METERED, 10), /* 16 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_CONNECTION_ALL "name"","\ @@ -77,7 +78,8 @@ NmcOutputField nmc_fields_setting_connection[] = { NM_SETTING_CONNECTION_SLAVE_TYPE","\ NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES","\ 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 */ @@ -2744,6 +2746,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 \ @@ -5324,6 +5371,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), @@ -6684,6 +6738,7 @@ setting_connection_details (NMSetting *setting, NmCli *nmc, const char *one_pro set_val_str (arr, 13, nmc_property_connection_get_autoconnect_slaves (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 14, nmc_property_connection_get_secondaries (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 15, nmc_property_connection_get_gateway_ping_timeout (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 16, 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 54ebd11026ad63bba454f1c5847ba85f14a92935 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. (cherry picked from commit 68db65b727bdfb7e560a82566348a256b848bbde) --- 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 db8c4b43e6..4c3ea8bd27 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -56,6 +56,7 @@ typedef struct { GArray *wins; guint32 mtu; NMIPConfigSource mtu_source; + gboolean metered; } NMIP4ConfigPrivate; /* internal guint32 are assigned to gobject properties of type uint. Ensure, that uint is large enough */ @@ -574,6 +575,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)); } @@ -1028,6 +1033,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); @@ -1098,6 +1109,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 @@ -1744,6 +1756,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 463675244e..62a309fa08 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -146,6 +146,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 75f0c7949420d913e2f14c9b71a4342a2c17dcd0 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. (cherry picked from commit 3c2f4a17f9255e2c6da6e6122159a9f993b80480) --- .../libsystemd-network/dhcp-lease-internal.h | 2 + .../src/libsystemd-network/dhcp-protocol.h | 1 + .../src/libsystemd-network/sd-dhcp-lease.c | 52 +++++++++++++++++++ .../systemd-dhcp/src/systemd/sd-dhcp-lease.h | 1 + 4 files changed, 56 insertions(+) diff --git a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h index 9e184ac4b5..71f7f143b3 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-lease-internal.h @@ -72,6 +72,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/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h index abca9422c5..aa37e9b0b5 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/dhcp-protocol.h @@ -125,6 +125,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/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c index 2d13d503e1..0b5048a663 100644 --- a/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/dhcp-manager/systemd-dhcp/src/libsystemd-network/sd-dhcp-lease.c @@ -191,6 +191,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); @@ -286,6 +299,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); @@ -568,6 +599,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; @@ -595,6 +634,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; @@ -667,6 +707,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/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h index 4296b91d8a..079a1bb347 100644 --- a/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h +++ b/src/dhcp-manager/systemd-dhcp/src/systemd/sd-dhcp-lease.h @@ -45,6 +45,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 c41fe4c659c06aba39dd31c4fe7e39cc7d1ddc3d 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. (cherry picked from commit 1e39b2320dfe42ca33dfbf12746d63935818ac5a) --- 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 12dc03cc84..2bd0d72ffc 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -224,6 +224,8 @@ lease_to_ip4_config (sd_dhcp_lease *lease, guint16 mtu; int r, num; guint64 end_time; + uint8_t *data; + gboolean metered = FALSE; g_return_val_if_fail (lease != NULL, NULL); @@ -355,6 +357,11 @@ lease_to_ip4_config (sd_dhcp_lease *lease, 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 8cd4359abb..ab7f26d5fa 100644 --- a/src/dhcp-manager/nm-dhcp-utils.c +++ b/src/dhcp-manager/nm-dhcp-utils.c @@ -575,6 +575,9 @@ nm_dhcp_utils_ip4_config_from_options (const char *iface, 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 53688a5b1b..e022a1ae74 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 ("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 ("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 afd1bf564251f7d72899675b4e44e2f4b6f54e90 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'. (cherry picked from commit a86255a0432b2ba88f5d629bffb79dd05bd7590a) --- 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 4c4e04108d..bf4ca8e6e0 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 ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data); +static void nm_device_update_metered (NMDevice *self); #include "nm-device-glue.h" @@ -3468,8 +3469,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); @@ -7515,6 +7518,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 @@ -7972,6 +8028,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean nm_platform_address_flush (NM_PLATFORM_GET, ifindex); } + nm_device_update_metered (self); _cleanup_generic_post (self, cleanup_type); } @@ -8444,6 +8501,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 1ab766b61bfe1ff3ffcd7585c7ac243c0d7f7a43 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. (cherry picked from commit 862fd91df06087257dfa2b647172bafc60328487) --- 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 975afac6a461e26b027c938deedb587ce9ec3158 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 (cherry picked from commit 7e5e624dafc9ad4e6ec2dc32cc0d7013476ca2c6) --- 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 b73d2289f0..28e590df37 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -1671,6 +1671,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * * Since: 1.0.6 **/ + /* ---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 ec87416775..170b4b2905 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -250,6 +250,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 52bf51d18b..146cd34096 100644 --- a/src/settings/plugins/ifcfg-rh/writer.c +++ b/src/settings/plugins/ifcfg-rh/writer.c @@ -1764,6 +1764,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 83aba66a85ae41a746aab3f428c0fe68adfb8b65 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(). (cherry picked from commit bd4e1f37f88baddc743b79b68886112882deca9a) --- 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 43d69b63ba..3cc836d83e 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -407,7 +407,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; } @@ -435,7 +438,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 5c11d26a2e323ec6c102e9d22e6f6a5b4fb667d9 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. (cherry picked from commit 04d5804dd5826cc36398027a023b0e639a54bd26) --- 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..2cdea31336 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.0.6 + **/ + 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..2a61a0109a 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.0.6 + **/ + 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 d40d571e8a..b0bdd77a81 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); @@ -838,6 +853,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) { @@ -1043,6 +1059,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 080cdb8b24..34078a0094 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -161,6 +161,7 @@ typedef struct { guint ac_cleanup_id; NMActiveConnection *primary_connection; NMActiveConnection *activating_connection; + NMMetered metered; GSList *devices; NMState state; @@ -233,6 +234,7 @@ enum { PROP_PRIMARY_CONNECTION_TYPE, PROP_ACTIVATING_CONNECTION, PROP_DEVICES, + PROP_METERED, /* Not exported */ PROP_HOSTNAME, @@ -657,6 +659,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) { @@ -4259,6 +4285,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) { @@ -4281,11 +4315,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); } } @@ -4879,6 +4925,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 @@ -4963,6 +5011,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; @@ -5238,6 +5289,20 @@ nm_manager_class_init (NMManagerClass *manager_class) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NMManager:metered: + * + * Whether the connectivity is metered. + * + * Since: 1.0.6 + **/ + 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"