about summary refs log tree commit diff
path: root/src/settings/plugins/ifcfg-rh/shvar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings/plugins/ifcfg-rh/shvar.c')
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c323
1 files changed, 203 insertions, 120 deletions
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 47ad5a23..df03bf65 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -38,10 +38,15 @@
 
 #include "nm-core-internal.h"
 #include "nm-core-utils.h"
+#include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/c-list.h"
 
 /*****************************************************************************/
 
 struct _shvarLine {
+
+	CList lst;
+
 	/* There are three cases:
 	 *
 	 * 1) the line is not a valid variable assignment (that is, it doesn't
@@ -68,7 +73,7 @@ typedef struct _shvarLine shvarLine;
 struct _shvarFile {
 	char      *fileName;
 	int        fd;
-	GList     *lineList;
+	CList      lst_head;
 	gboolean   modified;
 };
 
@@ -627,6 +632,7 @@ svFile_new (const char *name)
 	s = g_slice_new0 (shvarFile);
 	s->fd = -1;
 	s->fileName = g_strdup (name);
+	c_list_init (&s->lst_head);
 	return s;
 }
 
@@ -639,7 +645,7 @@ svFileGetName (const shvarFile *s)
 }
 
 void
-svFileSetName_test_only (shvarFile *s, const char *fileName)
+_nmtst_svFileSetName (shvarFile *s, const char *fileName)
 {
 	/* changing the file name is not supported for regular
 	 * operation. Only allowed to use in tests, othewise,
@@ -649,7 +655,7 @@ svFileSetName_test_only (shvarFile *s, const char *fileName)
 }
 
 void
-svFileSetModified_test_only (shvarFile *s)
+_nmtst_svFileSetModified (shvarFile *s)
 {
 	/* marking a file as modified is only for testing. */
 	s->modified = TRUE;
@@ -687,6 +693,7 @@ line_new_parse (const char *value, gsize len)
 	nm_assert (value);
 
 	line = g_slice_new0 (shvarLine);
+	c_list_init (&line->lst);
 
 	for (k = 0; k < len; k++) {
 		if (g_ascii_isspace (value[k]))
@@ -724,6 +731,7 @@ line_new_build (const char *key, const char *value)
 	value = svEscape (value, &value_escaped);
 
 	line = g_slice_new (shvarLine);
+	c_list_init (&line->lst);
 	line->line = value_escaped ?: g_strdup (value);
 	line->key_with_prefix = g_strdup (key);
 	line->key = line->key_with_prefix;
@@ -768,6 +776,7 @@ line_free (shvarLine *line)
 	ASSERT_shvarLine (line);
 	g_free (line->line);
 	g_free (line->key_with_prefix);
+	c_list_unlink (&line->lst);
 	g_slice_free (shvarLine, line);
 }
 
@@ -785,9 +794,8 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 	int errsv = 0;
 	char *arena;
 	const char *p, *q;
-	GError *local = NULL;
+	gs_free_error GError *local = NULL;
 	nm_auto_close int fd = -1;
-	GList *lineList = NULL;
 
 	if (create)
 		fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
@@ -810,34 +818,35 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 		return NULL;
 	}
 
-	if (nm_utils_fd_get_contents (fd,
+	if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
+	                              closefd,
 	                              10 * 1024 * 1024,
 	                              &arena,
 	                              NULL,
 	                              &local) < 0) {
+		if (create)
+			return svFile_new (name);
+
 		g_set_error (error, G_FILE_ERROR,
 		             local->domain == G_FILE_ERROR ? local->code : G_FILE_ERROR_FAILED,
 		             "Could not read file '%s': %s",
 		             name, local->message);
-		g_error_free (local);
 		return NULL;
 	}
 
+	s = svFile_new (name);
+
 	for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
-		lineList = g_list_prepend (lineList, line_new_parse (p, q - p));
+		c_list_link_tail (&s->lst_head, &line_new_parse (p, q - p)->lst);
 	if (p[0])
-		lineList = g_list_prepend (lineList, line_new_parse (p, strlen (p)));
+		c_list_link_tail (&s->lst_head, &line_new_parse (p, strlen (p))->lst);
 	g_free (arena);
-	lineList = g_list_reverse (lineList);
-
-	s = svFile_new (name);
-	s->lineList = lineList;
 
 	/* closefd is set if we opened the file read-only, so go ahead and
 	 * close it, because we can't write to it anyway */
 	if (!closefd) {
-		s->fd = fd;
-		fd = -1;
+		nm_assert (fd > 0);
+		s->fd = nm_steal_fd (&fd);
 	}
 
 	return s;
@@ -861,42 +870,22 @@ svCreateFile (const char *name)
 
 /*****************************************************************************/
 
-static const GList *
-shlist_find (const GList *current, const char *key)
-{
-	nm_assert (_shell_is_name (key, -1));
-
-	if (current) {
-		do {
-			shvarLine *line = current->data;
-
-			ASSERT_shvarLine (line);
-			if (line->key && nm_streq (line->key, key))
-				return current;
-			current = current->next;
-		} while (current);
-	}
-	return NULL;
-}
-
-/*****************************************************************************/
-
 GHashTable *
 svGetKeys (shvarFile *s)
 {
 	GHashTable *keys = NULL;
-	const GList *current;
+	CList *current;
 	const shvarLine *line;
 
 	nm_assert (s);
 
-	for (current = s->lineList; current; current = current->next) {
-		line = current->data;
+	c_list_for_each (current, &s->lst_head) {
+		line = c_list_entry (current, shvarLine, lst);
 		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);
+				keys = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL);
 			g_hash_table_add (keys, (gpointer) line->key);
 		}
 	}
@@ -908,14 +897,14 @@ svGetKeys (shvarFile *s)
 const char *
 svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix)
 {
-	const GList *current;
+	CList *current;
 	const shvarLine *l;
 
 	g_return_val_if_fail (s, NULL);
 	g_return_val_if_fail (key_prefix, NULL);
 
-	for (current = s->lineList; current; current = current->next) {
-		l = current->data;
+	c_list_for_each (current, &s->lst_head) {
+		l = c_list_entry (current, shvarLine, lst);
 		if (   l->key
 		    && l->line
 		    && g_str_has_prefix (l->key, key_prefix))
@@ -930,34 +919,31 @@ svFindFirstKeyWithPrefix (shvarFile *s, const char *key_prefix)
 static const char *
 _svGetValue (shvarFile *s, const char *key, char **to_free)
 {
-	const GList *current, *last;
-	const shvarLine *line;
+	CList *current;
+	const shvarLine *line, *l;
+	const char *v;
 
 	nm_assert (s);
 	nm_assert (_shell_is_name (key, -1));
 	nm_assert (to_free);
 
-	last = NULL;
-	current = s->lineList;
-	while ((current = shlist_find (current, key))) {
-		last = current;
-		current = current->next;
+	line = NULL;
+	c_list_for_each (current, &s->lst_head) {
+		l = c_list_entry (current, shvarLine, lst);
+		if (l->key && nm_streq (l->key, key))
+			line = l;
 	}
-	if (last) {
-		line = last->data;
-		if (line->line) {
-			const char *v;
-
-			v = svUnescape (line->line, to_free);
-			if (!v) {
-				/* a wrongly quoted value is treated like the empty string.
-				 * See also svWriteFile(), which handles unparsable values
-				 * that way. */
-				nm_assert (!*to_free);
-				return "";
-			}
-			return v;
+
+	if (line && line->line) {
+		v = svUnescape (line->line, to_free);
+		if (!v) {
+			/* a wrongly quoted value is treated like the empty string.
+			 * See also svWriteFile(), which handles unparsable values
+			 * that way. */
+			nm_assert (!*to_free);
+			return "";
 		}
+		return v;
 	}
 	*to_free = NULL;
 	return NULL;
@@ -1104,50 +1090,148 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 	return result;
 }
 
+gboolean
+svGetValueEnum (shvarFile *s, const char *key,
+                GType gtype, int *out_value,
+                GError **error)
+{
+	gs_free char *to_free = NULL;
+	const char *svalue;
+	gs_free char *err_token = NULL;
+	int value;
+
+	svalue = _svGetValue (s, key, &to_free);
+	if (!svalue) {
+		/* don't touch out_value. The caller is supposed
+		 * to initialize it with the default value. */
+		return TRUE;
+	}
+
+	if (!nm_utils_enum_from_str (gtype, svalue, &value, &err_token)) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "Invalid token \"%s\" in \"%s\" for %s",
+		             err_token, svalue, key);
+		return FALSE;
+	}
+
+	NM_SET_OUT (out_value, value);
+	return TRUE;
+}
+
 /*****************************************************************************/
 
+static gboolean
+_is_all_digits (const char *str)
+{
+	return    str[0]
+	       && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
+}
+
+#define IS_NUMBERED_TAG(key, tab_name) \
+	({ \
+		const char *_key = (key); \
+		\
+		(   (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
+		 && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
+	})
+
+gboolean
+svUnsetAll (shvarFile *s, SvKeyType match_key_type)
+{
+	CList *current;
+	shvarLine *line;
+	gboolean changed = FALSE;
+
+	g_return_val_if_fail (s, FALSE);
+
+	c_list_for_each (current, &s->lst_head) {
+		line = c_list_entry (current, shvarLine, lst);
+		ASSERT_shvarLine (line);
+		if (!line->key)
+			continue;
+
+		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
+			goto do_clear;
+		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
+			if (   IS_NUMBERED_TAG (line->key, "ADDRESS")
+			    || IS_NUMBERED_TAG (line->key, "NETMASK")
+			    || IS_NUMBERED_TAG (line->key, "GATEWAY")
+			    || IS_NUMBERED_TAG (line->key, "METRIC")
+			    || IS_NUMBERED_TAG (line->key, "OPTIONS"))
+				goto do_clear;
+		}
+		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
+			if (   IS_NUMBERED_TAG (line->key, "IPADDR")
+			    || IS_NUMBERED_TAG (line->key, "PREFIX")
+			    || IS_NUMBERED_TAG (line->key, "NETMASK")
+			    || IS_NUMBERED_TAG (line->key, "GATEWAY"))
+				goto do_clear;
+		}
+		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
+			if (g_str_has_prefix (line->key, "NM_USER_"))
+				goto do_clear;
+		}
+
+		continue;
+do_clear:
+		if (nm_clear_g_free (&line->line)) {
+			ASSERT_shvarLine (line);
+			changed = TRUE;
+		}
+	}
+
+	if (changed)
+		s->modified = TRUE;
+	return changed;
+}
+
 /* Same as svSetValueStr() but it preserves empty @value -- contrary to
  * svSetValueStr() for which "" effectively means to remove the value. */
-void
+gboolean
 svSetValue (shvarFile *s, const char *key, const char *value)
 {
-	GList *current, *last;
+	CList *current;
+	shvarLine *line, *l;
+	gboolean changed = FALSE;
 
-	g_return_if_fail (s != NULL);
-	g_return_if_fail (key != NULL);
+	g_return_val_if_fail (s, FALSE);
+	g_return_val_if_fail (key, FALSE);
 
 	nm_assert (_shell_is_name (key, -1));
 
-	last = NULL;
-	current = s->lineList;
-	while ((current = (GList *) shlist_find (current, key))) {
-		if (last) {
-			/* if we find multiple entries for the same key, we can
-			 * delete all but the last. */
-			line_free (last->data);
-			s->lineList = g_list_delete_link (s->lineList, last);
-			s->modified = TRUE;
+	line = NULL;
+	c_list_for_each (current, &s->lst_head) {
+		l = c_list_entry (current, shvarLine, lst);
+		if (l->key && nm_streq (l->key, key)) {
+			if (line) {
+				/* if we find multiple entries for the same key, we can
+				 * delete all but the last. */
+				line_free (line);
+				changed = TRUE;
+			}
+			line = l;
 		}
-		last = current;
-		current = current->next;
 	}
 
 	if (!value) {
-		if (last) {
-			shvarLine *line = last->data;
-
-			if (nm_clear_g_free (&line->line))
-				s->modified = TRUE;
+		if (line) {
+			if (nm_clear_g_free (&line->line)) {
+				changed = TRUE;
+			}
 		}
 	} else {
-		if (!last) {
-			s->lineList = g_list_append (s->lineList, line_new_build (key, value));
-			s->modified = TRUE;
+		if (!line) {
+			c_list_link_tail (&s->lst_head, &line_new_build (key, value)->lst);
+			changed = TRUE;
 		} else {
-			if (line_set (last->data, value))
-				s->modified = TRUE;
+			if (line_set (line, value))
+				changed = TRUE;
 		}
 	}
+
+	if (changed)
+		s->modified = TRUE;
+	return changed;
 }
 
 /* Set the variable <key> equal to the value <value>.
@@ -1155,51 +1239,48 @@ svSetValue (shvarFile *s, const char *key, const char *value)
  * the key=value pair after that line.  Otherwise, append the pair
  * to the bottom of the file.
  */
-void
+gboolean
 svSetValueStr (shvarFile *s, const char *key, const char *value)
 {
-	svSetValue (s, key, value && value[0] ? value : NULL);
+	return svSetValue (s, key, value && value[0] ? value : NULL);
 }
 
-void
+gboolean
 svSetValueInt64 (shvarFile *s, const char *key, gint64 value)
 {
 	char buf[NM_DECIMAL_STR_MAX (value)];
 
-	svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value));
+	return svSetValue (s, key, nm_sprintf_buf (buf, "%"G_GINT64_FORMAT, value));
 }
 
-void
-svSetValueBoolean (shvarFile *s, const char *key, gboolean value)
+gboolean
+svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value)
 {
-	svSetValue (s, key, value ? "yes" : "no");
+	if (do_set)
+		return svSetValueInt64 (s, key, value);
+	else
+		return svUnsetValue (s, key);
 }
 
-void
-svUnsetValue (shvarFile *s, const char *key)
+gboolean
+svSetValueBoolean (shvarFile *s, const char *key, gboolean value)
 {
-	svSetValue (s, key, NULL);
+	return svSetValue (s, key, value ? "yes" : "no");
 }
 
-void
-svUnsetValuesWithPrefix (shvarFile *s, const char *prefix)
+gboolean
+svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value)
 {
-	GList *current;
-
-	g_return_if_fail (s);
-	g_return_if_fail (prefix);
+	gs_free char *v = NULL;
 
-	for (current = s->lineList; current; current = current->next) {
-		shvarLine *line = current->data;
+	v = _nm_utils_enum_to_str_full (gtype, value, " ");
+	return svSetValueStr (s, key, v);
+}
 
-		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);
-	}
+gboolean
+svUnsetValue (shvarFile *s, const char *key)
+{
+	return svSetValue (s, key, NULL);
 }
 
 /*****************************************************************************/
@@ -1215,7 +1296,7 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 {
 	FILE *f;
 	int tmpfd;
-	const GList *current;
+	CList *current;
 
 	if (s->modified) {
 		if (s->fd == -1)
@@ -1248,8 +1329,8 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 		}
 		f = fdopen (tmpfd, "w");
 		fseek (f, 0, SEEK_SET);
-		for (current = s->lineList; current; current = current->next) {
-			const shvarLine *line = current->data;
+		c_list_for_each (current, &s->lst_head) {
+			const shvarLine *line = c_list_entry (current, shvarLine, lst);
 			const char *str;
 			char *s_tmp;
 			gboolean valid_value;
@@ -1288,11 +1369,13 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 void
 svCloseFile (shvarFile *s)
 {
+	CList *current, *safe;
+
 	g_return_if_fail (s != NULL);
 
-	if (s->fd != -1)
-		close (s->fd);
+	nm_close (s->fd);
 	g_free (s->fileName);
-	g_list_free_full (s->lineList, (GDestroyNotify) line_free);
+	c_list_for_each_safe (current, safe, &s->lst_head)
+		line_free (c_list_entry (current, shvarLine, lst));
 	g_slice_free (shvarFile, s);
 }