diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 3983cd7c1b..6c2b9fc71e 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -290,6 +290,13 @@ global: nm_setting_wired_get_mac_address; nm_setting_wired_get_cloned_mac_address; nm_setting_wired_get_mtu; + nm_setting_wired_get_num_s390_options; + nm_setting_wired_get_s390_nettype; + nm_setting_wired_get_s390_option; + nm_setting_wired_get_s390_option_by_key; + nm_setting_wired_add_s390_option; + nm_setting_wired_get_s390_subchannels; + nm_setting_wired_remove_s390_option; nm_setting_wireless_ap_security_compatible; nm_setting_wireless_error_get_type; nm_setting_wireless_error_quark; diff --git a/libnm-util/nm-setting-wired.c b/libnm-util/nm-setting-wired.c index 0f01832b50..b18f691f21 100644 --- a/libnm-util/nm-setting-wired.c +++ b/libnm-util/nm-setting-wired.c @@ -23,12 +23,16 @@ * (C) Copyright 2007 - 2008 Novell, Inc. */ +#include +#include #include #include + #include "nm-setting-wired.h" #include "nm-param-spec-specialized.h" #include "nm-utils.h" #include "nm-utils-private.h" +#include "nm-dbus-glib-types.h" GQuark nm_setting_wired_error_quark (void) @@ -76,6 +80,9 @@ typedef struct { GByteArray *device_mac_address; GByteArray *cloned_mac_address; guint32 mtu; + GPtrArray *s390_subchannels; + char *s390_nettype; + GHashTable *s390_options; } NMSettingWiredPrivate; enum { @@ -87,10 +94,23 @@ enum { PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, PROP_MTU, + PROP_S390_SUBCHANNELS, + PROP_S390_NETTYPE, + PROP_S390_OPTIONS, LAST_PROP }; +static const char *valid_s390_opts[] = { + "portno", "layer2", "portname", "protocol", "priority_queueing", + "buffer_count", "isolation", "total", "inter", "inter_jumbo", "route4", + "route6", "fake_broadcast", "broadcast_mode", "canonical_macaddr", + "checksumming", "sniffer", "large_send", "ipato_enable", "ipato_invert4", + "ipato_add4", "ipato_invert6", "ipato_add6", "vipa_add4", "vipa_add6", + "rxip_add4", "rxip_add6", "lancmd_timeout", + NULL +}; + NMSetting * nm_setting_wired_new (void) { @@ -153,12 +173,197 @@ nm_setting_wired_get_mtu (NMSettingWired *setting) return NM_SETTING_WIRED_GET_PRIVATE (setting)->mtu; } +/** + * nm_setting_wired_get_s390_subchannels: + * @setting: the #NMSettingWired + * + * Return the list of s390 subchannels that identify the device that this + * connection is applicable to. The connection should only be used in + * conjunction with that device. + * + * Returns: a #GPtrArray of strings, each specifying one subchannel the + * s390 device uses to communicate to the host. + **/ +const GPtrArray * +nm_setting_wired_get_s390_subchannels (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_subchannels; +} + +/** + * nm_setting_wired_get_s390_nettype: + * @setting: the #NMSettingWired + * + * Returns the s390 device type this connection should apply to. Will be one + * of 'qeth', 'lcs', or 'ctcm'. + * + * Returns: the s390 device type + **/ +const char * +nm_setting_wired_get_s390_nettype (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_nettype; +} + +/** + * nm_setting_wired_get_num_s390_options: + * @setting: the #NMSettingWired + * + * Returns the number of s390-specific options that should be set for this + * device when it is activated. This can be used to retrieve each s390 + * option individually using nm_setting_wired_get_s390_option(). + * + * Returns: the number of s390-specific device options + **/ +guint32 +nm_setting_wired_get_num_s390_options (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), 0); + + return g_hash_table_size (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options); +} + +/** + * nm_setting_wired_get_s390_option: + * @setting: the #NMSettingWired + * @idx: index of the desired option, from 0 to + * nm_setting_wired_get_num_s390_options() - 1 + * @out_key: on return, the key name of the s390 specific option; this value is + * owned by the setting and should not be modified + * @out_value: on return, the value of the key of the s390 specific option; this + * value is owned by the setting and should not be modified + * + * Given an index, return the value of the s390 option at that index. indexes + * are *not* guaranteed to be static across modifications to options done by + * nm_setting_wired_add_s390_option() and nm_setting_wired_remove_s390_option(), + * and should not be used to refer to options except for short periods of time + * such as during option iteration. + * + * Returns: %TRUE on success if the index was valid and an option was found, + * %FALSE if the index was invalid (ie, greater than the number of options + * currently held by the setting) + **/ +gboolean +nm_setting_wired_get_s390_option (NMSettingWired *setting, + guint32 idx, + const char **out_key, + const char **out_value) +{ + NMSettingWiredPrivate *priv; + guint32 num_keys; + GList *keys; + const char *_key = NULL, *_value = NULL; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + + priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + + num_keys = nm_setting_wired_get_num_s390_options (setting); + g_return_val_if_fail (idx < num_keys, FALSE); + + keys = g_hash_table_get_keys (priv->s390_options); + _key = g_list_nth_data (keys, idx); + _value = g_hash_table_lookup (priv->s390_options, _key); + + if (out_key) + *out_key = _key; + if (out_value) + *out_value = _value; + return TRUE; +} + +/** + * nm_setting_wired_get_s390_option_by_key: + * @setting: the #NMSettingWired + * @key: the key for which to retrieve the value + * + * Returns the value associated with the s390-specific option specified by + * @key, if it exists. + * + * Returns: the value, or NULL if the key/value pair was never added to the + * setting; the value is owned by the setting and must not be modified + **/ +const char * +nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting, + const char *key) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + g_return_val_if_fail (key != NULL, NULL); + g_return_val_if_fail (strlen (key), NULL); + + return g_hash_table_lookup (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key); +} + +/** + * nm_setting_wired_add_s390_options: + * @setting: the #NMSettingWired + * @key: key name for the option + * @value: value for the option + * + * Add an option to the table. The option is compared to an internal list + * of allowed options. Key names may contain only alphanumeric characters + * (ie [a-zA-Z0-9]). Adding a new key replaces any existing key/value pair that + * may already exist. + * + * Returns: %TRUE if the option was valid and was added to the internal option + * list, %FALSE if it was not. + **/ +gboolean nm_setting_wired_add_s390_option (NMSettingWired *setting, + const char *key, + const char *value) +{ + size_t value_len; + + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (strlen (key), FALSE); + g_return_val_if_fail (_nm_utils_string_in_list (key, valid_s390_opts), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + value_len = strlen (value); + g_return_val_if_fail (value_len > 0 && value_len < 200, FALSE); + + g_hash_table_insert (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, + g_strdup (key), + g_strdup (value)); + return TRUE; +} + +/** + * nm_setting_wired_remove_s390_options: + * @setting: the #NMSettingWired + * @key: key name for the option to remove + * + * Remove the s390-specific option referenced by @key from the internal option + * list. + * + * Returns: %TRUE if the option was found and removed from the internal option + * list, %FALSE if it was not. + **/ +gboolean +nm_setting_wired_remove_s390_option (NMSettingWired *setting, + const char *key) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + g_return_val_if_fail (strlen (key), FALSE); + + return g_hash_table_remove (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key); +} + static gboolean verify (NMSetting *setting, GSList *all_settings, GError **error) { NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); const char *valid_ports[] = { "tp", "aui", "bnc", "mii", NULL }; const char *valid_duplex[] = { "half", "full", NULL }; + const char *valid_nettype[] = { "qeth", "lcs", "ctcm", NULL }; + GHashTableIter iter; + const char *key, *value; if (priv->port && !_nm_utils_string_in_list (priv->port, valid_ports)) { g_set_error (error, @@ -184,6 +389,36 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) return FALSE; } + if ( priv->s390_subchannels + && !(priv->s390_subchannels->len == 3 || priv->s390_subchannels->len == 2)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRED_S390_SUBCHANNELS); + return FALSE; + } + + if (priv->s390_nettype && !_nm_utils_string_in_list (priv->s390_nettype, valid_nettype)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRED_S390_NETTYPE); + return FALSE; + } + + g_hash_table_iter_init (&iter, priv->s390_options); + while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { + if ( !_nm_utils_string_in_list (key, valid_s390_opts) + || !strlen (value) + || (strlen (value) > 200)) { + g_set_error (error, + NM_SETTING_WIRED_ERROR, + NM_SETTING_WIRED_ERROR_INVALID_PROPERTY, + NM_SETTING_WIRED_S390_OPTIONS); + return FALSE; + } + } + if (priv->cloned_mac_address && priv->cloned_mac_address->len != ETH_ALEN) { g_set_error (error, NM_SETTING_WIRED_ERROR, @@ -198,7 +433,10 @@ verify (NMSetting *setting, GSList *all_settings, GError **error) static void nm_setting_wired_init (NMSettingWired *setting) { + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); + g_object_set (setting, NM_SETTING_NAME, NM_SETTING_WIRED_SETTING_NAME, NULL); + priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } static void @@ -208,6 +446,9 @@ finalize (GObject *object) g_free (priv->port); g_free (priv->duplex); + g_free (priv->s390_nettype); + + g_hash_table_destroy (priv->s390_options); if (priv->device_mac_address) g_byte_array_free (priv->device_mac_address, TRUE); @@ -218,11 +459,18 @@ finalize (GObject *object) G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object); } +static void +copy_hash (gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_insert ((GHashTable *) user_data, g_strdup (key), g_strdup (value)); +} + static void set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) + const GValue *value, GParamSpec *pspec) { NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object); + GHashTable *new_hash; switch (prop_id) { case PROP_PORT: @@ -252,6 +500,24 @@ set_property (GObject *object, guint prop_id, case PROP_MTU: priv->mtu = g_value_get_uint (value); break; + case PROP_S390_SUBCHANNELS: + if (priv->s390_subchannels) { + g_ptr_array_foreach (priv->s390_subchannels, (GFunc) g_free, NULL); + g_ptr_array_free (priv->s390_subchannels, TRUE); + } + priv->s390_subchannels = g_value_dup_boxed (value); + break; + case PROP_S390_NETTYPE: + g_free (priv->s390_nettype); + priv->s390_nettype = g_value_dup_string (value); + break; + case PROP_S390_OPTIONS: + /* Must make a deep copy of the hash table here... */ + g_hash_table_remove_all (priv->s390_options); + new_hash = g_value_get_boxed (value); + if (new_hash) + g_hash_table_foreach (new_hash, copy_hash, priv->s390_options); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -260,9 +526,10 @@ set_property (GObject *object, guint prop_id, static void get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) + GValue *value, GParamSpec *pspec) { NMSettingWired *setting = NM_SETTING_WIRED (object); + NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting); switch (prop_id) { case PROP_PORT: @@ -286,6 +553,15 @@ get_property (GObject *object, guint prop_id, case PROP_MTU: g_value_set_uint (value, nm_setting_wired_get_mtu (setting)); break; + case PROP_S390_SUBCHANNELS: + g_value_set_boxed (value, nm_setting_wired_get_s390_subchannels (setting)); + break; + case PROP_S390_NETTYPE: + g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting)); + break; + case PROP_S390_OPTIONS: + g_value_set_boxed (value, priv->s390_options); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -425,5 +701,66 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) "multiple Ethernet frames.", 0, G_MAXUINT32, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE | NM_SETTING_PARAM_FUZZY_IGNORE)); + + /** + * NMSettingWired:s390-subchannels: + * + * Identifies specific subchannels that this network device uses for + * communcation with z/VM or s390 host. Like #NMSettingWired:mac-address + * for non-z/VM devices, this property can be used to ensure this connection + * only applies to the network device that uses these subchannels. The + * list should contain exactly 3 strings, and each string may only only be + * composed of hexadecimal characters and the period (.) character. + **/ + g_object_class_install_property + (object_class, PROP_S390_SUBCHANNELS, + _nm_param_spec_specialized (NM_SETTING_WIRED_S390_SUBCHANNELS, + "z/VM Subchannels", + "Identifies specific subchannels that this " + "network device uses for communcation with z/VM " + "or s390 host. Like the 'mac-address' property " + "for non-z/VM devices, this property can be used " + "to ensure this connection only applies to the " + "network device that uses these subchannels. The " + "list should contain exactly 3 strings, and each " + "string may only only be composed of hexadecimal " + "characters and the period (.) character.", + DBUS_TYPE_G_ARRAY_OF_STRING, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + /** + * NMSettingWired:s390-nettype: + * + * s390 network device type; one of 'qeth', 'lcs', or 'ctc', representing + * the different types of virtual network devices available on s390 systems. + **/ + g_object_class_install_property + (object_class, PROP_S390_NETTYPE, + g_param_spec_string (NM_SETTING_WIRED_S390_NETTYPE, + "s390 Net Type", + "s390 network device type; one of 'qeth', 'lcs', or " + "'ctc', representing the different types of virtual " + "network devices available on s390 systems.", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + /** + * NMSettingWired:s390-options: + * + * Dictionary of key/value pairs of s390-specific device options. Both keys + * and values must be strings. Allowed keys include 'portno', 'layer2', + * 'portname', 'protocol', among others. Key names must contain only + * alphanumeric characters (ie, [a-zA-Z0-9]). + **/ + g_object_class_install_property + (object_class, PROP_S390_OPTIONS, + _nm_param_spec_specialized (NM_SETTING_WIRED_S390_OPTIONS, + "s390 Options", + "Dictionary of key/value pairs of s390-specific " + "device options. Both keys and values must be " + "strings. Allowed keys include 'portno', " + "'layer2', 'portname', 'protocol', among others.", + DBUS_TYPE_G_MAP_OF_STRING, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); } diff --git a/libnm-util/nm-setting-wired.h b/libnm-util/nm-setting-wired.h index a3f9ac86d9..32361b4bca 100644 --- a/libnm-util/nm-setting-wired.h +++ b/libnm-util/nm-setting-wired.h @@ -59,6 +59,9 @@ GQuark nm_setting_wired_error_quark (void); #define NM_SETTING_WIRED_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address" #define NM_SETTING_WIRED_MTU "mtu" +#define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels" +#define NM_SETTING_WIRED_S390_NETTYPE "s390-nettype" +#define NM_SETTING_WIRED_S390_OPTIONS "s390-options" typedef struct { NMSetting parent; @@ -76,14 +79,30 @@ typedef struct { GType nm_setting_wired_get_type (void); -NMSetting *nm_setting_wired_new (void); -const char *nm_setting_wired_get_port (NMSettingWired *setting); -guint32 nm_setting_wired_get_speed (NMSettingWired *setting); -const char *nm_setting_wired_get_duplex (NMSettingWired *setting); -gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting); -const GByteArray *nm_setting_wired_get_mac_address (NMSettingWired *setting); +NMSetting * nm_setting_wired_new (void); +const char * nm_setting_wired_get_port (NMSettingWired *setting); +guint32 nm_setting_wired_get_speed (NMSettingWired *setting); +const char * nm_setting_wired_get_duplex (NMSettingWired *setting); +gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting); +const GByteArray *nm_setting_wired_get_mac_address (NMSettingWired *setting); const GByteArray *nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting); -guint32 nm_setting_wired_get_mtu (NMSettingWired *setting); +guint32 nm_setting_wired_get_mtu (NMSettingWired *setting); + +const GPtrArray * nm_setting_wired_get_s390_subchannels (NMSettingWired *setting); +const char * nm_setting_wired_get_s390_nettype (NMSettingWired *setting); + +guint32 nm_setting_wired_get_num_s390_options (NMSettingWired *setting); +gboolean nm_setting_wired_get_s390_option (NMSettingWired *setting, + guint32 idx, + const char **out_key, + const char **out_value); +const char * nm_setting_wired_get_s390_option_by_key (NMSettingWired *setting, + const char *key); +gboolean nm_setting_wired_add_s390_option (NMSettingWired *setting, + const char *key, + const char *item); +gboolean nm_setting_wired_remove_s390_option (NMSettingWired *setting, + const char *key); G_END_DECLS diff --git a/libnm-util/nm-utils.c b/libnm-util/nm-utils.c index 7be41629bf..ce13da3c86 100644 --- a/libnm-util/nm-utils.c +++ b/libnm-util/nm-utils.c @@ -567,6 +567,24 @@ nm_utils_convert_strv_to_slist (const GValue *src_value, GValue *dest_value) g_value_take_boxed (dest_value, g_slist_reverse (list)); } +static void +nm_utils_convert_strv_to_ptrarray (const GValue *src_value, GValue *dest_value) +{ + char **str; + GPtrArray *array = NULL; + guint i = 0; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), G_TYPE_STRV)); + + str = (char **) g_value_get_boxed (src_value); + + array = g_ptr_array_sized_new (3); + while (str && str[i]) + g_ptr_array_add (array, g_strdup (str[i++])); + + g_value_take_boxed (dest_value, array); +} + static void nm_utils_convert_strv_to_string (const GValue *src_value, GValue *dest_value) { @@ -593,6 +611,32 @@ nm_utils_convert_strv_to_string (const GValue *src_value, GValue *dest_value) g_string_free (printable, FALSE); } +static void +nm_utils_convert_string_array_to_string (const GValue *src_value, GValue *dest_value) +{ + GPtrArray *strings; + GString *printable; + int i; + + g_return_if_fail (g_type_is_a (G_VALUE_TYPE (src_value), DBUS_TYPE_G_ARRAY_OF_STRING)); + + strings = (GPtrArray *) g_value_get_boxed (src_value); + + printable = g_string_new ("["); + for (i = 0; strings && i < strings->len; i++) { + if (i > 0) + g_string_append (printable, ", '"); + else + g_string_append_c (printable, '\''); + g_string_append (printable, g_ptr_array_index (strings, i)); + g_string_append_c (printable, '\''); + } + g_string_append_c (printable, ']'); + + g_value_take_string (dest_value, printable->str); + g_string_free (printable, FALSE); +} + static void nm_utils_convert_uint_array_to_string (const GValue *src_value, GValue *dest_value) { @@ -1055,9 +1099,15 @@ _nm_utils_register_value_transformations (void) g_value_register_transform_func (G_TYPE_STRV, DBUS_TYPE_G_LIST_OF_STRING, nm_utils_convert_strv_to_slist); + g_value_register_transform_func (G_TYPE_STRV, + DBUS_TYPE_G_ARRAY_OF_STRING, + nm_utils_convert_strv_to_ptrarray); g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING, G_TYPE_STRING, nm_utils_convert_strv_to_string); + g_value_register_transform_func (DBUS_TYPE_G_ARRAY_OF_STRING, + G_TYPE_STRING, + nm_utils_convert_string_array_to_string); g_value_register_transform_func (DBUS_TYPE_G_UINT_ARRAY, G_TYPE_STRING, nm_utils_convert_uint_array_to_string); diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 22cf2fa0d8..10240960b0 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "NetworkManagerUtils.h" #include "nm-utils.h" @@ -487,6 +488,101 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr) return FALSE; } +#define BUFSIZE 10 + +static gboolean +parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c) +{ + long unsigned int tmp; + char buf[BUFSIZE + 1]; + const char *p = subchannels; + int i = 0; + char *pa = NULL, *pb = NULL, *pc = NULL; + + g_return_val_if_fail (subchannels != NULL, FALSE); + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (*a == 0, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + g_return_val_if_fail (*b == 0, FALSE); + g_return_val_if_fail (c != NULL, FALSE); + g_return_val_if_fail (*c == 0, FALSE); + + /* sanity check */ + if (!isxdigit (subchannels[0])) + return FALSE; + + /* Get the first channel */ + while (*p && (*p != ',')) { + if (!isxdigit (*p) && (*p != '.')) + return FALSE; /* Invalid chars */ + if (i >= BUFSIZE) + return FALSE; /* Too long to be a subchannel */ + buf[i++] = *p++; + } + buf[i] = '\0'; + + /* and grab each of its elements, there should be 3 */ + pa = &buf[0]; + pb = strchr (buf, '.'); + if (pb) + pc = strchr (pb + 1, '.'); + if (!pa || !pb || !pc) + return FALSE; + + /* Split the string */ + *pb++ = '\0'; + *pc++ = '\0'; + + errno = 0; + tmp = strtoul (pa, NULL, 16); + if (errno) + return FALSE; + *a = (guint32) tmp; + + errno = 0; + tmp = strtoul (pb, NULL, 16); + if (errno) + return FALSE; + *b = (guint32) tmp; + + errno = 0; + tmp = strtoul (pc, NULL, 16); + if (errno) + return FALSE; + *c = (guint32) tmp; + + return TRUE; +} + +#define SUBCHAN_TAG "s390-subchannels:" + +gboolean +nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels) +{ + const GSList *iter; + guint32 a = 0, b = 0, c = 0; + guint32 spec_a = 0, spec_b = 0, spec_c = 0; + + g_return_val_if_fail (subchannels != NULL, FALSE); + + if (!parse_subchannels (subchannels, &a, &b, &c)) + return FALSE; + + for (iter = specs; iter; iter = g_slist_next (iter)) { + const char *spec = iter->data; + + if (!strncmp (spec, SUBCHAN_TAG, strlen (SUBCHAN_TAG))) { + spec += strlen (SUBCHAN_TAG); + if (parse_subchannels (spec, &spec_a, &spec_b, &spec_c)) { + if (a == spec_a && b == spec_b && c == spec_c) + return TRUE; + } + } + } + + return FALSE; +} + /*********************************/ static void diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index e3d1793b4d..72c0e532b8 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2004 - 2008 Red Hat, Inc. + * Copyright (C) 2004 - 2010 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. */ @@ -48,6 +48,7 @@ void nm_utils_call_dispatcher (const char *action, const char *vpn_iface); gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr); +gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels); GHashTable *value_hash_create (void); diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index 3bef9bc8e2..b851ea12d5 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -34,6 +34,9 @@ #include #include +#define G_UDEV_API_IS_SUBJECT_TO_CHANGE +#include + #include #include "nm-glib-compat.h" @@ -116,6 +119,12 @@ typedef struct { Supplicant supplicant; guint supplicant_timeout_id; + /* s390 */ + char * subchan1; + char * subchan2; + char * subchan3; + char * subchannels; /* Composite used for checking unmanaged specs */ + /* PPPoE */ NMPPPManager *ppp_manager; NMIP4Config *pending_ip4_config; @@ -291,6 +300,109 @@ carrier_off (NMNetlinkMonitor *monitor, } } +static void +_update_s390_subchannels (NMDeviceEthernet *self) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + const char *iface; + GUdevClient *client; + GUdevDevice *dev; + GUdevDevice *parent; + const char *parent_path, *item, *driver; + const char *subsystems[] = { "net", NULL }; + GDir *dir; + GError *error = NULL; + + iface = nm_device_get_iface (NM_DEVICE (self)); + + client = g_udev_client_new (subsystems); + if (!client) { + nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to initialize GUdev client", iface); + return; + } + + dev = g_udev_client_query_by_subsystem_and_name (client, "net", iface); + if (!dev) { + nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to find device with udev", iface); + goto out; + } + + /* Try for the "ccwgroup" parent */ + parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL); + if (!parent) { + /* FIXME: whatever 'lcs' devices' subsystem is here... */ + if (!parent) { + /* Not an s390 device */ + goto out; + } + } + + parent_path = g_udev_device_get_sysfs_path (parent); + dir = g_dir_open (parent_path, 0, &error); + if (!dir) { + nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to open directory '%s': %s", + iface, parent_path, + error && error->message ? error->message : "(unknown)"); + g_clear_error (&error); + goto out; + } + + /* FIXME: we probably care about ordering here to ensure that we map + * cdev0 -> subchan1, cdev1 -> subchan2, etc. + */ + while ((item = g_dir_read_name (dir))) { + char buf[50]; + char *cdev_path; + + if (strncmp (item, "cdev", 4)) + continue; /* Not a subchannel link */ + + cdev_path = g_strdup_printf ("%s/%s", parent_path, item); + + memset (buf, 0, sizeof (buf)); + errno = 0; + if (readlink (cdev_path, &buf[0], sizeof (buf) - 1) >= 0) { + if (!priv->subchan1) + priv->subchan1 = g_path_get_basename (buf); + else if (!priv->subchan2) + priv->subchan2 = g_path_get_basename (buf); + else if (!priv->subchan3) + priv->subchan3 = g_path_get_basename (buf); + } else { + nm_log_warn (LOGD_DEVICE | LOGD_HW, + "(%s): failed to read cdev link '%s': %s", + iface, cdev_path, errno); + } + g_free (cdev_path); + }; + + g_dir_close (dir); + + if (priv->subchan3) { + priv->subchannels = g_strdup_printf ("%s,%s,%s", + priv->subchan1, + priv->subchan2, + priv->subchan3); + } else if (priv->subchan2) { + priv->subchannels = g_strdup_printf ("%s,%s", + priv->subchan1, + priv->subchan2); + } else + priv->subchannels = g_strdup (priv->subchan1); + + driver = nm_device_get_driver (NM_DEVICE (self)); + nm_log_info (LOGD_DEVICE | LOGD_HW, + "(%s): found s390 '%s' subchannels [%s]", + iface, driver ? driver : "(unknown driver)", priv->subchannels); + +out: + if (parent) + g_object_unref (parent); + if (dev) + g_object_unref (dev); + g_object_unref (client); +} + static GObject* constructor (GType type, guint n_construct_params, @@ -314,6 +426,9 @@ constructor (GType type, nm_device_get_iface (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); + /* s390 stuff */ + _update_s390_subchannels (NM_DEVICE_ETHERNET (self)); + caps = nm_device_get_capabilities (self); if (caps & NM_DEVICE_CAP_CARRIER_DETECT) { GError *error = NULL; @@ -1587,6 +1702,41 @@ real_deactivate_quickly (NMDevice *device) _set_hw_addr (self, priv->perm_hw_addr, "reset"); } +static gboolean +match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac) +{ + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + const GPtrArray *subchans; + int i; + + *try_mac = TRUE; + + subchans = nm_setting_wired_get_s390_subchannels (s_wired); + if (!subchans) + return TRUE; + + /* connection requires subchannels but the device has none */ + if (!priv->subchannels) + return FALSE; + + /* Make sure each subchannel in the connection is a subchannel of this device */ + for (i = 0; i < subchans->len; i++) { + const char *candidate = g_ptr_array_index (subchans, i); + gboolean found = FALSE; + + if ( (priv->subchan1 && !strcmp (priv->subchan1, candidate)) + || (priv->subchan2 && !strcmp (priv->subchan2, candidate)) + || (priv->subchan3 && !strcmp (priv->subchan3, candidate))) + found = TRUE; + + if (!found) + return FALSE; + } + + *try_mac = FALSE; + return TRUE; +} + static gboolean real_check_connection_compatible (NMDevice *device, NMConnection *connection, @@ -1598,6 +1748,8 @@ real_check_connection_compatible (NMDevice *device, NMSettingWired *s_wired; const char *connection_type; gboolean is_pppoe = FALSE; + const GByteArray *mac; + gboolean try_mac = TRUE; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); g_assert (s_con); @@ -1624,10 +1776,15 @@ real_check_connection_compatible (NMDevice *device, } if (s_wired) { - const GByteArray *mac; + if (!match_subchans (self, s_wired, &try_mac)) { + g_set_error (error, + NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, + "The connection's s390 subchannels did not match this device."); + return FALSE; + } mac = nm_setting_wired_get_mac_address (s_wired); - if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) { + if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) { g_set_error (error, NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE, "The connection's MAC address did not match this device."); @@ -1651,6 +1808,9 @@ spec_match_list (NMDevice *device, const GSList *specs) matched = nm_match_spec_hwaddr (specs, hwaddr); g_free (hwaddr); + if (!matched && priv->subchannels) + matched = nm_match_spec_s390_subchannels (specs, priv->subchannels); + return matched; } @@ -1660,14 +1820,18 @@ wired_match_config (NMDevice *self, NMConnection *connection) NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); NMSettingWired *s_wired; const GByteArray *s_ether; + gboolean try_mac = TRUE; s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); if (!s_wired) return FALSE; + if (!match_subchans (NM_DEVICE_ETHERNET (self), s_wired, &try_mac)) + return FALSE; + /* MAC address check */ s_ether = nm_setting_wired_get_mac_address (s_wired); - if (s_ether && memcmp (s_ether->data, priv->perm_hw_addr, ETH_ALEN)) + if (try_mac && s_ether && memcmp (s_ether->data, priv->perm_hw_addr, ETH_ALEN)) return FALSE; return TRUE; @@ -1875,6 +2039,11 @@ dispose (GObject *object) priv->monitor = NULL; } + g_free (priv->subchan1); + g_free (priv->subchan2); + g_free (priv->subchan3); + g_free (priv->subchannels); + G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object); } diff --git a/src/system-settings/nm-system-config-interface.h b/src/system-settings/nm-system-config-interface.h index c5bbaaa917..3daceb89bd 100644 --- a/src/system-settings/nm-system-config-interface.h +++ b/src/system-settings/nm-system-config-interface.h @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2007 - 2008 Red Hat, Inc. + * Copyright (C) 2007 - 2010 Red Hat, Inc. * Copyright (C) 2008 Novell, Inc. */ @@ -107,6 +107,12 @@ struct _NMSystemConfigInterface { * * Method: mac Data: device MAC address formatted with leading zeros and * lowercase letters, like 00:0a:0b:0c:0d:0e + * + * Method: s390-subchannels Data: string of 2 or 3 s390 subchannels + * separated by commas (,) that identify the + * device, like "0.0.09a0,0.0.09a1,0.0.09a2". + * The string may contain only the following + * characters: [a-fA-F0-9,.] */ GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config); diff --git a/system-settings/plugins/ifcfg-rh/reader.c b/system-settings/plugins/ifcfg-rh/reader.c index 4787b85a47..a5d6c37db7 100644 --- a/system-settings/plugins/ifcfg-rh/reader.c +++ b/system-settings/plugins/ifcfg-rh/reader.c @@ -2941,6 +2941,7 @@ make_wired_setting (shvarFile *ifcfg, char *value = NULL; int mtu; GByteArray *mac = NULL; + char *nettype; s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); @@ -2968,15 +2969,98 @@ make_wired_setting (shvarFile *ifcfg, } g_byte_array_free (mac, TRUE); - } else if (!nm_controlled) { - /* If NM_CONTROLLED=no but there wasn't a MAC address, notify - * the user that the device cannot be unmanaged. - */ - PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: NM_CONTROLLED was false but HWADDR was missing; device will be managed"); } } else { g_object_unref (s_wired); - s_wired = NULL; + return NULL; + } + + value = svGetValue (ifcfg, "SUBCHANNELS", FALSE); + if (value) { + const char *p = value; + gboolean success = TRUE; + char **chans = NULL; + + /* basic sanity checks */ + while (*p) { + if (!isxdigit (*p) && (*p != ',') && (*p != '.')) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid SUBCHANNELS '%s'", value); + success = FALSE; + break; + } + p++; + } + + if (success) { + guint32 num_chans; + + chans = g_strsplit_set (value, ",", 0); + num_chans = g_strv_length (chans); + if (num_chans < 2 || num_chans > 3) { + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid SUBCHANNELS '%s' (%d channels, 2 or 3 expected)", + value, g_strv_length (chans)); + } else { + GPtrArray *array = g_ptr_array_sized_new (num_chans); + + g_ptr_array_add (array, chans[0]); + g_ptr_array_add (array, chans[1]); + if (num_chans == 3) + g_ptr_array_add (array, chans[2]); + + g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, array, NULL); + g_ptr_array_free (array, TRUE); + + /* set the unmanaged spec too */ + if (!nm_controlled && !*unmanaged) + *unmanaged = g_strdup_printf ("s390-subchannels:%s", value); + } + g_strfreev (chans); + } + g_free (value); + } + + value = svGetValue (ifcfg, "PORTNAME", FALSE); + if (value && strlen (value)) { + nm_setting_wired_add_s390_option (s_wired, "portname", value); + } + g_free (value); + + nettype = svGetValue (ifcfg, "NETTYPE", FALSE); + if (nettype && strlen (nettype)) { + if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc")) + g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL); + else + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: unknown s390 NETTYPE '%s'", nettype); + } + + value = svGetValue (ifcfg, "OPTIONS", FALSE); + if (value && strlen (value)) { + char **options, **iter; + + iter = options = g_strsplit_set (value, " ", 0); + while (iter && *iter) { + char *equals = strchr (*iter, '='); + gboolean valid = FALSE; + + if (equals) { + *equals = '\0'; + valid = nm_setting_wired_add_s390_option (s_wired, *iter, equals + 1); + } + if (!valid) + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: invalid s390 OPTION '%s'", *iter); + iter++; + } + g_strfreev (options); + } + g_free (value); + + g_free (nettype); + + if (!nm_controlled && !*unmanaged) { + /* If NM_CONTROLLED=no but there wasn't a MAC address or z/VM + * subchannels, notify the user that the device cannot be unmanaged. + */ + PLUGIN_WARN (IFCFG_PLUGIN_NAME, " warning: NM_CONTROLLED was false but HWADDR or SUBCHANNELS was missing; device will be managed"); } mac = NULL; diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am index 6005901920..fd2629173a 100644 --- a/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/Makefile.am @@ -64,7 +64,8 @@ EXTRA_DIST = \ ifcfg-test-wifi-wep-40-ascii \ keys-test-wifi-wep-40-ascii \ ifcfg-test-wifi-wep-104-ascii \ - keys-test-wifi-wep-104-ascii + keys-test-wifi-wep-104-ascii \ + ifcfg-test-wired-qeth-static check-local: @for f in $(EXTRA_DIST); do \ diff --git a/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static new file mode 100644 index 0000000000..4719de2175 --- /dev/null +++ b/system-settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-qeth-static @@ -0,0 +1,13 @@ +# IBM QETH +DEVICE=eth1 +BOOTPROTO=static +IPADDR=192.168.70.87 +NETMASK=255.255.255.0 +ONBOOT=yes +NETTYPE=qeth +SUBCHANNELS=0.0.0600,0.0.0601,0.0.0602 +TYPE=Ethernet +PORTNAME=OSAPORT +OPTIONS='layer2=1 portno=0' +MACADDR=02:00:00:23:65:1a + diff --git a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 1271928d9f..70b58d424c 100644 --- a/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/system-settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -5346,6 +5346,178 @@ test_read_wifi_wep_eap_ttls_chap (void) g_object_unref (connection); } +#define TEST_IFCFG_WIRED_QETH_STATIC TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-qeth-static" + +static void +test_read_wired_qeth_static (void) +{ + NMConnection *connection; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + const char *tmp; + const char *expected_id = "System test-wired-qeth-static"; + const GByteArray *array; + const char *expected_channel0 = "0.0.0600"; + const char *expected_channel1 = "0.0.0601"; + const char *expected_channel2 = "0.0.0602"; + const GPtrArray *subchannels; + + connection = connection_from_file (TEST_IFCFG_WIRED_QETH_STATIC, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + ASSERT (connection != NULL, + "wired-qeth-static-read", "failed to read %s: %s", TEST_IFCFG_WIRED_QETH_STATIC, error->message); + + ASSERT (nm_connection_verify (connection, &error), + "wired-qeth-static-verify", "failed to verify %s: %s", TEST_IFCFG_WIRED_QETH_STATIC, error->message); + + ASSERT (unmanaged == FALSE, + "wired-qeth-static-verify", "failed to verify %s: unexpected unmanaged value", TEST_IFCFG_WIRED_QETH_STATIC); + + /* ===== CONNECTION SETTING ===== */ + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + ASSERT (s_con != NULL, + "wired-qeth-static-verify-connection", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME); + + /* ID */ + tmp = nm_setting_connection_get_id (s_con); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-connection", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + ASSERT (strcmp (tmp, expected_id) == 0, + "wired-qeth-static-verify-connection", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_SETTING_CONNECTION_ID); + + /* ===== WIRED SETTING ===== */ + + s_wired = NM_SETTING_WIRED (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED)); + ASSERT (s_wired != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* MAC address */ + array = nm_setting_wired_get_mac_address (s_wired); + ASSERT (array == NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_MAC_ADDRESS); + + /* Subchannels */ + subchannels = nm_setting_wired_get_s390_subchannels (s_wired); + ASSERT (subchannels != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_SUBCHANNELS); + ASSERT (subchannels->len == 3, + "wired-qeth-static-verify-wired", "failed to verify %s: invalid %s / %s key (not 3 elements)", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_SUBCHANNELS); + + tmp = (const char *) g_ptr_array_index (subchannels, 0); + ASSERT (strcmp (tmp, expected_channel0) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #0", + TEST_IFCFG_WIRED_QETH_STATIC); + + tmp = (const char *) g_ptr_array_index (subchannels, 1); + ASSERT (strcmp (tmp, expected_channel1) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #1", + TEST_IFCFG_WIRED_QETH_STATIC); + + tmp = (const char *) g_ptr_array_index (subchannels, 2); + ASSERT (strcmp (tmp, expected_channel2) == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected subchannel #2", + TEST_IFCFG_WIRED_QETH_STATIC); + + /* Nettype */ + tmp = nm_setting_wired_get_s390_nettype (s_wired); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s / %s key", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_NETTYPE); + ASSERT (strcmp (tmp, "qeth") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_WIRED_S390_NETTYPE); + + /* port name */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "portname"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'portname'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "OSAPORT") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'portname' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* port number */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "portno"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'portno'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "0") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'portno' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* layer */ + tmp = nm_setting_wired_get_s390_option_by_key (s_wired, "layer2"); + ASSERT (tmp != NULL, + "wired-qeth-static-verify-wired", "failed to verify %s: missing %s s390 option 'layer2'", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + ASSERT (strcmp (tmp, "1") == 0, + "wired-qeth-static-verify-wired", "failed to verify %s: unexpected %s s390 option 'layer2' value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_WIRED_SETTING_NAME); + + /* ===== IPv4 SETTING ===== */ + + s_ip4 = NM_SETTING_IP4_CONFIG (nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG)); + ASSERT (s_ip4 != NULL, + "wired-qeth-static-verify-ip4", "failed to verify %s: missing %s setting", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_IP4_CONFIG_SETTING_NAME); + + /* Method */ + tmp = nm_setting_ip4_config_get_method (s_ip4); + ASSERT (strcmp (tmp, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0, + "wired-qeth-static-verify-ip4", "failed to verify %s: unexpected %s / %s key value", + TEST_IFCFG_WIRED_QETH_STATIC, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP4_CONFIG_METHOD); + + g_object_unref (connection); +} + static void test_write_wired_static (void) { @@ -8714,6 +8886,140 @@ test_read_ibft_malformed (const char *name, const char *iscsiadm_path) name, "unexpectedly able to read %s", TEST_IFCFG_IBFT_STATIC); } +static void +test_write_wired_qeth_dhcp (void) +{ + NMConnection *connection; + NMConnection *reread; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingIP4Config *s_ip4; + NMSettingIP6Config *s_ip6; + char *uuid; + GPtrArray *subchans; + gboolean success; + GError *error = NULL; + char *testfile = NULL; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + + connection = nm_connection_new (); + ASSERT (connection != NULL, + "wired-qeth-dhcp-write", "failed to allocate new connection"); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + ASSERT (s_con != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_CONNECTION_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + uuid = nm_utils_uuid_generate (); + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired qeth Static", + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + g_free (uuid); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + ASSERT (s_wired != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_WIRED_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + subchans = g_ptr_array_sized_new (3); + g_ptr_array_add (subchans, "0.0.600"); + g_ptr_array_add (subchans, "0.0.601"); + g_ptr_array_add (subchans, "0.0.602"); + g_object_set (s_wired, + NM_SETTING_WIRED_S390_SUBCHANNELS, subchans, + NM_SETTING_WIRED_S390_NETTYPE, "qeth", + NULL); + g_ptr_array_free (subchans, TRUE); + + nm_setting_wired_add_s390_option (s_wired, "portname", "FOOBAR"); + nm_setting_wired_add_s390_option (s_wired, "portno", "1"); + nm_setting_wired_add_s390_option (s_wired, "layer2", "0"); + nm_setting_wired_add_s390_option (s_wired, "protocol", "blahbalh"); + + /* IP4 setting */ + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + ASSERT (s_ip4 != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_IP4_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + ASSERT (s_ip6 != NULL, + "wired-qeth-dhcp-write", "failed to allocate new %s setting", + NM_SETTING_IP6_CONFIG_SETTING_NAME); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + /* Verify */ + ASSERT (nm_connection_verify (connection, &error) == TRUE, + "wired-qeth-dhcp-write", "failed to verify connection: %s", + (error && error->message) ? error->message : "(unknown)"); + + /* Save the ifcfg */ + success = writer_new_connection (connection, + TEST_SCRATCH_DIR "/network-scripts/", + &testfile, + &error); + ASSERT (success == TRUE, + "wired-qeth-dhcp-write", "failed to write connection to disk: %s", + (error && error->message) ? error->message : "(unknown)"); + + ASSERT (testfile != NULL, + "wired-qeth-dhcp-write", "didn't get ifcfg file path back after writing connection"); + + /* re-read the connection for comparison */ + reread = connection_from_file (testfile, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + unlink (testfile); + + ASSERT (reread != NULL, + "wired-qeth-dhcp-write-reread", "failed to read %s: %s", testfile, error->message); + + ASSERT (nm_connection_verify (reread, &error), + "wired-qeth-dhcp-write-reread-verify", "failed to verify %s: %s", testfile, error->message); + + ASSERT (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE, + "wired-qeth-dhcp-write", "written and re-read connection weren't the same."); + + if (route6file) + unlink (route6file); + + g_free (testfile); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + g_object_unref (connection); + g_object_unref (reread); +} + static void test_write_wired_pppoe (void) { @@ -9038,6 +9344,7 @@ int main (int argc, char **argv) test_read_wifi_wpa_eap_tls (); test_read_wifi_wpa_eap_ttls_tls (); test_read_wifi_wep_eap_ttls_chap (); + test_read_wired_qeth_static (); test_write_wired_static (); test_write_wired_static_ip6_only (); @@ -9087,6 +9394,7 @@ int main (int argc, char **argv) test_write_wifi_wpa_eap_tls (); test_write_wifi_wpa_eap_ttls_tls (); test_write_wifi_wpa_eap_ttls_mschapv2 (); + test_write_wired_qeth_dhcp (); /* iSCSI / ibft */ test_read_ibft_dhcp (); diff --git a/system-settings/plugins/ifcfg-rh/writer.c b/system-settings/plugins/ifcfg-rh/writer.c index 1058c71b80..8f1f74b57e 100644 --- a/system-settings/plugins/ifcfg-rh/writer.c +++ b/system-settings/plugins/ifcfg-rh/writer.c @@ -842,7 +842,10 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) NMSettingWired *s_wired; const GByteArray *device_mac, *cloned_mac; char *tmp; - guint32 mtu; + const char *nettype, *portname, *s390_key, *s390_val; + guint32 mtu, num_opts, i; + const GPtrArray *s390_subchannels; + GString *str; s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED); if (!s_wired) { @@ -851,6 +854,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) return FALSE; } + svSetValue (ifcfg, "HWADDR", NULL, FALSE); device_mac = nm_setting_wired_get_mac_address (s_wired); if (device_mac) { tmp = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", @@ -877,6 +881,53 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) g_free (tmp); } + svSetValue (ifcfg, "SUBCHANNELS", NULL, FALSE); + s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired); + if (s390_subchannels) { + if (s390_subchannels->len == 2) { + tmp = g_strdup_printf ("%s,%s", + (const char *) g_ptr_array_index (s390_subchannels, 0), + (const char *) g_ptr_array_index (s390_subchannels, 1)); + } else if (s390_subchannels->len == 3) { + tmp = g_strdup_printf ("%s,%s,%s", + (const char *) g_ptr_array_index (s390_subchannels, 0), + (const char *) g_ptr_array_index (s390_subchannels, 1), + (const char *) g_ptr_array_index (s390_subchannels, 2)); + } + svSetValue (ifcfg, "SUBCHANNELS", tmp, FALSE); + g_free (tmp); + } + + svSetValue (ifcfg, "NETTYPE", NULL, FALSE); + nettype = nm_setting_wired_get_s390_nettype (s_wired); + if (nettype) + svSetValue (ifcfg, "NETTYPE", nettype, FALSE); + + svSetValue (ifcfg, "PORTNAME", NULL, FALSE); + portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname"); + if (portname) + svSetValue (ifcfg, "PORTNAME", portname, FALSE); + + svSetValue (ifcfg, "OPTIONS", NULL, FALSE); + num_opts = nm_setting_wired_get_num_s390_options (s_wired); + if (s390_subchannels && num_opts) { + str = g_string_sized_new (30); + for (i = 0; i < num_opts; i++) { + nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val); + + /* portname is handled separately */ + if (!strcmp (s390_key, "portname")) + continue; + + if (str->len) + g_string_append_c (str, ' '); + g_string_append_printf (str, "%s=%s", s390_key, s390_val); + } + if (str->len) + svSetValue (ifcfg, "OPTIONS", str->str, FALSE); + g_string_free (str, TRUE); + } + svSetValue (ifcfg, "TYPE", TYPE_ETHERNET, FALSE); return TRUE;