diff options
author | Thomas Haller <thaller@redhat.com> | 2017-05-04 16:26:02 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2017-05-06 14:12:19 +0200 |
commit | 79be44d99020bbaf23b8226b7e459f321348c0b1 (patch) | |
tree | 03b0e95f01fa8088e2bcde8729fad1a4ecf22437 | |
parent | 22fd7d2e39327c9f288dd8f17529c1898bc20e47 (diff) | |
download | NetworkManager-79be44d99020bbaf23b8226b7e459f321348c0b1.tar.gz |
ifcfg: add read/write support for user-data
The user data values are encoded in shell variables named
prefix "NM_USER_". The variable name is an encoded form of the
data key, consisting only of upper-case letters, digits, and underscore.
The alternative would be something like
NM_USER_1_KEY=my.keys.1
NM_USER_1_VAL='some value'
NM_USER_2_KEY=my.other.KEY.42
NM_USER_2_VAL='other value'
contary to
NM_USER_MY__KEYS__1='some value'
NM_USER_MY__OTHER___K_E_Y__42='other value'
The advantage of the former, numbered scheme is that it may be easier to
find the key of a user-data entry. With the current implementation, the
shell script would have to decode the key, like the ifcfg-rh plugin
does.
However, user data keys are opaque identifers for values. Usually, you
are not concerned with a certain name of the key, you already know it.
Hence, you don't need to write a shell script to decode the key name,
instead, you can use it directly:
if [ -z ${NM_USER_MY__OTHER___K_E_Y__42+x} ]; then
do_something_with_key "$NM_USER_MY__OTHER___K_E_Y__42"
fi
Otherwise, you'd first have to search write a shell script to search
for the interesting key -- in this example "$NM_USER_2_KEY", before being
able to access the value "$NM_USER_2_VAL".
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 55 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c | 114 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h | 4 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 37 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/shvar.c | 51 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/shvar.h | 4 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected | 34 | ||||
-rw-r--r-- | src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 59 |
9 files changed, 356 insertions, 5 deletions
diff --git a/Makefile.am b/Makefile.am index 9191b1c015..335239bdbd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2157,7 +2157,8 @@ EXTRA_DIST += \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected \ - src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected \ + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected # make target dependencies can't have colons in their names, which ends up # meaning that we can't add the alias files to EXTRA_DIST. They are instead diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 2df922313b..5baa9c792b 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -48,6 +48,7 @@ #include "nm-setting-bridge.h" #include "nm-setting-bridge-port.h" #include "nm-setting-dcb.h" +#include "nm-setting-user.h" #include "nm-setting-proxy.h" #include "nm-setting-generic.h" #include "nm-core-internal.h" @@ -1023,6 +1024,54 @@ error: } static NMSetting * +make_user_setting (shvarFile *ifcfg, GError **error) +{ + gboolean has_user_data = FALSE; + gs_unref_object NMSettingUser *s_user = NULL; + gs_unref_hashtable GHashTable *keys = NULL; + GHashTableIter iter; + const char *key; + nm_auto_free_gstring GString *str = NULL; + + keys = svGetKeys (ifcfg); + if (!keys) + return NULL; + + g_hash_table_iter_init (&iter, keys); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) { + const char *value; + gs_free char *value_to_free = NULL; + + if (!g_str_has_prefix (key, "NM_USER_")) + continue; + + value = svGetValue (ifcfg, key, &value_to_free); + + if (!value) + continue; + + if (!str) + str = g_string_sized_new (100); + else + g_string_set_size (str, 0); + + if (!nms_ifcfg_rh_utils_user_key_decode (key + NM_STRLEN ("NM_USER_"), str)) + continue; + + if (!s_user) + s_user = NM_SETTING_USER (nm_setting_user_new ()); + + if (nm_setting_user_set_data (s_user, str->str, + value, NULL)) + has_user_data = TRUE; + } + + return has_user_data + ? g_steal_pointer (&s_user) + : NULL; +} + +static NMSetting * make_proxy_setting (shvarFile *ifcfg, GError **error) { NMSettingProxy *s_proxy = NULL; @@ -5121,7 +5170,7 @@ connection_from_file_full (const char *filename, gs_unref_object NMConnection *connection = NULL; gs_free char *type = NULL; char *devtype, *bootproto; - NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL; + NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL, *s_user; const char *ifcfg_name = NULL; gboolean has_ip4_defroute = FALSE; @@ -5361,6 +5410,10 @@ connection_from_file_full (const char *filename, if (s_proxy) nm_connection_add_setting (connection, s_proxy); + s_user = make_user_setting (parsed, error); + if (s_user) + nm_connection_add_setting (connection, s_user); + /* Bridge port? */ s_port = make_bridge_port_setting (parsed); if (s_port) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c index 0dd49fb4a7..e82ef60c63 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c @@ -365,3 +365,117 @@ utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg) return NULL; return utils_get_ifcfg_path (path); } + +void +nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer) +{ + gsize i; + + nm_assert (key); + nm_assert (str_buffer); + + for (i = 0; key[i]; i++) { + char ch = key[i]; + + /* we encode the key in only upper case letters, digits, and underscore. + * As we expect lower-case letters to be more common, we encode lower-case + * letters as upper case, and upper-case letters with a leading underscore. */ + + if (ch >= '0' && ch <= '9') { + g_string_append_c (str_buffer, ch); + continue; + } + if (ch >= 'a' && ch <= 'z') { + g_string_append_c (str_buffer, ch - 'a' + 'A'); + continue; + } + if (ch == '.') { + g_string_append (str_buffer, "__"); + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c (str_buffer, '_'); + g_string_append_c (str_buffer, ch); + continue; + } + g_string_append_printf (str_buffer, "_%03o", (unsigned) ch); + } +} + +gboolean +nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer) +{ + gsize i; + + nm_assert (name); + nm_assert (str_buffer); + + if (!name[0]) + return FALSE; + + for (i = 0; name[i]; ) { + char ch = name[i]; + + if (ch >= '0' && ch <= '9') { + g_string_append_c (str_buffer, ch); + i++; + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c (str_buffer, ch - 'A' + 'a'); + i++; + continue; + } + + if (ch == '_') { + ch = name[i + 1]; + if (ch == '_') { + g_string_append_c (str_buffer, '.'); + i += 2; + continue; + } + if (ch >= 'A' && ch <= 'Z') { + g_string_append_c (str_buffer, ch); + i += 2; + continue; + } + if (ch >= '0' && ch <= '7') { + char ch2, ch3; + unsigned v; + + ch2 = name[i + 2]; + if (!(ch2 >= '0' && ch2 <= '7')) + return FALSE; + + ch3 = name[i + 3]; + if (!(ch3 >= '0' && ch3 <= '7')) + return FALSE; + +#define OCTAL_VALUE(ch) ((unsigned) ((ch) - '0')) + v = (OCTAL_VALUE (ch) << 6) + + (OCTAL_VALUE (ch2) << 3) + + OCTAL_VALUE (ch3); + if ( v > 0xFF + || v == 0) + return FALSE; + ch = (char) v; + if ( (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || (ch == '.') + || (ch >= 'a' && ch <= 'z')) { + /* such characters are not expected to be encoded via + * octal representation. The encoding is invalid. */ + return FALSE; + } + g_string_append_c (str_buffer, ch); + i += 4; + continue; + } + return FALSE; + } + + return FALSE; + } + + return TRUE; +} diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h index d209a0673c..2b2c27558f 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h @@ -54,5 +54,7 @@ gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg); char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg); -#endif /* _UTILS_H_ */ +void nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer); +gboolean nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer); +#endif /* _UTILS_H_ */ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index 8dd8737ad9..28bea262ed 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -43,6 +43,7 @@ #include "nm-setting-ip6-config.h" #include "nm-setting-pppoe.h" #include "nm-setting-vlan.h" +#include "nm-setting-user.h" #include "nm-setting-team.h" #include "nm-setting-team-port.h" #include "nm-utils.h" @@ -2003,6 +2004,39 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) } static gboolean +write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingUser *s_user; + guint i, len; + const char *const*keys; + + s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER)); + + svUnsetValuesWithPrefix (ifcfg, "NM_USER_"); + + if (!s_user) + return TRUE; + + keys = nm_setting_user_get_keys (s_user, &len); + if (len) { + nm_auto_free_gstring GString *str = g_string_sized_new (100); + + for (i = 0; i < len; i++) { + const char *key = keys[i]; + + g_string_set_size (str, 0); + g_string_append (str, "NM_USER_"); + nms_ifcfg_rh_utils_user_key_encode (key, str); + svSetValue (ifcfg, + str->str, + nm_setting_user_get_data (s_user, key)); + } + } + + return TRUE; +} + +static gboolean write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) { NMSettingIPConfig *s_ip4; @@ -2882,6 +2916,9 @@ write_connection (NMConnection *connection, if (!write_proxy_setting (connection, ifcfg, error)) return FALSE; + if (!write_user_setting (connection, ifcfg, error)) + return FALSE; + svUnsetValue (ifcfg, "DHCP_HOSTNAME"); svUnsetValue (ifcfg, "DHCP_FQDN"); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index cddf3daa72..ca6aa72e0f 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -42,7 +42,7 @@ /*****************************************************************************/ -typedef struct { +struct _shvarLine { /* There are three cases: * * 1) the line is not a valid variable assignment (that is, it doesn't @@ -62,7 +62,9 @@ typedef struct { char *line; const char *key; char *key_with_prefix; -} shvarLine; +}; + +typedef struct _shvarLine shvarLine; struct _shvarFile { char *fileName; @@ -880,6 +882,30 @@ shlist_find (const GList *current, const char *key) /*****************************************************************************/ +GHashTable * +svGetKeys (shvarFile *s) +{ + GHashTable *keys = NULL; + const GList *current; + const shvarLine *line; + + nm_assert (s); + + for (current = s->lineList; current; current = current->next) { + line = current->data; + if (line->key && line->line) { + /* we don't clone the keys. The keys are only valid + * until @s gets modified. */ + if (!keys) + keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + g_hash_table_add (keys, (gpointer) line->key); + } + } + return keys; +} + +/*****************************************************************************/ + static const char * _svGetValue (shvarFile *s, const char *key, char **to_free) { @@ -1171,6 +1197,27 @@ svUnsetValue (shvarFile *s, const char *key) svSetValue (s, key, NULL); } +void +svUnsetValuesWithPrefix (shvarFile *s, const char *prefix) +{ + GList *current; + + g_return_if_fail (s); + g_return_if_fail (prefix); + + for (current = s->lineList; current; current = current->next) { + shvarLine *line = current->data; + + ASSERT_shvarLine (line); + if ( line->key + && g_str_has_prefix (line->key, prefix)) { + if (nm_clear_g_free (&line->line)) + s->modified = TRUE; + } + ASSERT_shvarLine (line); + } +} + /*****************************************************************************/ /* Write the current contents iff modified. Returns FALSE on error diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 8654d73c75..62b07c2f57 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -56,6 +56,8 @@ char *svGetValueStr_cp (shvarFile *s, const char *key); gint svParseBoolean (const char *value, gint def); +GHashTable *svGetKeys (shvarFile *s); + /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false") * return <def> otherwise @@ -81,6 +83,8 @@ void svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value); void svUnsetValue (shvarFile *s, const char *key); +void svUnsetValuesWithPrefix (shvarFile *s, const char *prefix); + /* Write the current contents iff modified. Returns FALSE on error * and TRUE on success. Do not write if no values have been modified. * The mode argument is only used if creating the file, not if diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected new file mode 100644 index 0000000000..a48be78a23 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected @@ -0,0 +1,34 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +NM_USER__M_Y___053=val=MY.+ +NM_USER__M_Y___055=val=MY.- +NM_USER__M_Y___057=val=MY./ +NM_USER__M_Y__8_053_V=val=MY.8+V +NM_USER__M_Y__8_055_V=val=MY.8-V +NM_USER__M_Y__8_057_V=val=MY.8/V +NM_USER__M_Y__8_075_V=val=MY.8=V +NM_USER__M_Y__8_V=val=MY.8V +NM_USER__M_Y__8_137_V=val=MY.8_V +NM_USER__M_Y___075=val=MY.= +NM_USER__M_Y___A_V=val=MY.AV +NM_USER__M_Y___137=val=MY._ +NM_USER_MY___AV=val=my.Av +NM_USER_MY___137V=val=my._v +NM_USER_MY__KEYS__1=val=my.keys.1 +NM_USER_MY__OTHER___K_E_Y__42=val=my.other.KEY.42 +NM_USER_MY__V_053=val=my.v+ +NM_USER_MY__V_137_137AL3=val=my.v__al3 +NM_USER_MY__VAL1= +NM_USER_MY__VAL2=val=my.val2 +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=yes +IPV6_AUTOCONF=yes +IPV6_DEFROUTE=yes +IPV6_FAILURE_FATAL=no +IPV6_ADDR_GEN_MODE=stable-privacy +NAME="Test User 1" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index b588cf1979..b7ae5ad511 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -33,6 +33,7 @@ #include "nm-utils.h" #include "nm-setting-connection.h" #include "nm-setting-wired.h" +#include "nm-setting-user.h" #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" #include "nm-setting-ip4-config.h" @@ -1097,6 +1098,62 @@ test_read_wired_obsolete_gateway_n (void) } static void +test_user_1 (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingUser *s_user; + + connection = nmtst_create_minimal_connection ("Test User 1", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); + s_user = NM_SETTING_USER (nm_setting_user_new ()); + +#define _USER_SET_DATA(s_user, key, val) \ + G_STMT_START { \ + GError *_error = NULL; \ + gboolean _success; \ + \ + _success = nm_setting_user_set_data ((s_user), (key), (val), &_error); \ + nmtst_assert_success (_success, _error); \ + } G_STMT_END + +#define _USER_SET_DATA_X(s_user, key) \ + _USER_SET_DATA (s_user, key, "val="key"") + + _USER_SET_DATA (s_user, "my.val1", ""); + _USER_SET_DATA_X (s_user, "my.val2"); + _USER_SET_DATA_X (s_user, "my.v__al3"); + _USER_SET_DATA_X (s_user, "my._v"); + _USER_SET_DATA_X (s_user, "my.v+"); + _USER_SET_DATA_X (s_user, "my.Av"); + _USER_SET_DATA_X (s_user, "MY.AV"); + _USER_SET_DATA_X (s_user, "MY.8V"); + _USER_SET_DATA_X (s_user, "MY.8-V"); + _USER_SET_DATA_X (s_user, "MY.8_V"); + _USER_SET_DATA_X (s_user, "MY.8+V"); + _USER_SET_DATA_X (s_user, "MY.8/V"); + _USER_SET_DATA_X (s_user, "MY.8=V"); + _USER_SET_DATA_X (s_user, "MY.-"); + _USER_SET_DATA_X (s_user, "MY._"); + _USER_SET_DATA_X (s_user, "MY.+"); + _USER_SET_DATA_X (s_user, "MY./"); + _USER_SET_DATA_X (s_user, "MY.="); + _USER_SET_DATA_X (s_user, "my.keys.1"); + _USER_SET_DATA_X (s_user, "my.other.KEY.42"); + + nm_connection_add_setting (connection, NM_SETTING (s_user)); + + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR "/network-scripts/", + TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_User_1.cexpected", + &testfile); + + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + +static void test_read_wired_never_default (void) { NMConnection *connection; @@ -9311,6 +9368,8 @@ int main (int argc, char **argv) nmtst_add_test_func (TPATH "wired/read/manual/3", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-3", "System test-wired-ipv4-manual-3"); nmtst_add_test_func (TPATH "wired/read/manual/4", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-4", "System test-wired-ipv4-manual-4"); + g_test_add_func (TPATH "user/1", test_user_1); + g_test_add_func (TPATH "wired/ipv6-manual", test_read_wired_ipv6_manual); nmtst_add_test_func (TPATH "wired-ipv6-only/0", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-only", "System test-wired-ipv6-only"); |