summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2017-01-02 17:04:01 +0100
committerThomas Haller <thaller@redhat.com>2017-01-02 17:04:01 +0100
commit922cb4ab77b634469d08992e3172fe9313fda386 (patch)
treee33405ca65b7e1311f816f78a683e26cd52f70c9
parent8adbeb821ea6f82c1f4df57eb34cc1834a429343 (diff)
parentca3cb90fc2301ddb10d482786ac6e2432eabbc2e (diff)
downloadNetworkManager-922cb4ab77b634469d08992e3172fe9313fda386.tar.gz
ifcfg-rh: merge branch 'th/ifcfg-shvar-line-bgo776467'
https://bugzilla.gnome.org/show_bug.cgi?id=776467
-rw-r--r--shared/nm-utils/nm-macros-internal.h12
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c484
2 files changed, 275 insertions, 221 deletions
diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h
index 5044c13f94..eb00357765 100644
--- a/shared/nm-utils/nm-macros-internal.h
+++ b/shared/nm-utils/nm-macros-internal.h
@@ -584,6 +584,18 @@ _NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, FUNC, _##FUNC##_##VERSION, ARGS_T
/*****************************************************************************/
+#define nm_str_skip_leading_spaces(str) \
+ ({ \
+ typeof (*(str)) *_str = (str); \
+ _nm_unused const char *_str_type_check = _str; \
+ \
+ if (_str) { \
+ while (g_ascii_isspace (_str[0])) \
+ _str++; \
+ } \
+ _str; \
+ })
+
static inline char *
nm_strstrip (char *str)
{
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index ddf879438f..65bcc0d9dd 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -37,19 +37,37 @@
#include <unistd.h>
#include "nm-core-internal.h"
+#include "nm-core-utils.h"
/*****************************************************************************/
typedef struct {
- gchar *original;
- gchar *value;
+ /* There are three cases:
+ *
+ * 1) the line is not a valid variable assignment (that is, it doesn't
+ * start with a "FOO=" with possible whitespace prefix).
+ * In that case, @key and @key_with_prefix are %NULL, and the entire
+ * original line is in @line. Such entries are ignored for the most part.
+ *
+ * 2) if the line can be parsed with a "FOO=" assignment, then @line contains
+ * the part after '=', @key_with_prefix contains the key "FOO" with possible
+ * whitespace prefix, and @key points into @key_with_prefix skipping over the
+ * whitespace.
+ *
+ * 3) like 2, but if the value was deleted via svSetValue(), the entry is not removed,
+ * but only marked for deletion. That is done by clearing @line but preserving
+ * @key/@key_with_prefix.
+ * */
+ char *line;
+ const char *key;
+ char *key_with_prefix;
} shvarLine;
struct _shvarFile {
- char *fileName; /* read-only */
- int fd; /* read-only */
- GList *lineList; /* read-only */
- gboolean modified; /* ignore */
+ char *fileName;
+ int fd;
+ GList *lineList;
+ gboolean modified;
};
/*****************************************************************************/
@@ -90,34 +108,27 @@ svParseBoolean (const char *value, gint fallback)
/*****************************************************************************/
static gboolean
-_shell_is_name (const char *key)
+_shell_is_name (const char *key, gssize len)
{
+ gssize i;
+
/* whether @key is a valid identifier (name). */
- if (!key)
+ if (!key || len == 0)
return FALSE;
if ( !g_ascii_isalpha (key[0])
&& key[0] != '_')
return FALSE;
- return NM_STRCHAR_ALL (&key[1], ch,
- g_ascii_isalnum (ch) || ch == '_');
-}
-
-static const char *
-_shell_is_name_assignment (const char *key)
-{
- /* whether @key is a valid identifier (name). */
- if (!key)
- return NULL;
- if ( !g_ascii_isalpha (key[0])
- && key[0] != '_')
- return NULL;
- while (TRUE) {
- const char ch = (++key)[0];
-
- if (ch == '=')
- return &key[1];
- if (!g_ascii_isalnum (ch) && ch != '_')
- return NULL;
+ for (i = 1; TRUE; i++) {
+ if (len < 0) {
+ if (!key[i])
+ return TRUE;
+ } else {
+ if (i >= len)
+ return TRUE;
+ }
+ if ( !g_ascii_isalnum (key[i])
+ && key[i] != '_')
+ return FALSE;
}
}
@@ -608,6 +619,17 @@ out_error:
/*****************************************************************************/
+static shvarFile *
+svFile_new (const char *name)
+{
+ shvarFile *s;
+
+ s = g_slice_new0 (shvarFile);
+ s->fd = -1;
+ s->fileName = g_strdup (name);
+ return s;
+}
+
const char *
svFileGetName (const shvarFile *s)
{
@@ -631,23 +653,117 @@ svFileSetModified (shvarFile *s)
/*****************************************************************************/
+static void
+ASSERT_shvarLine (const shvarLine *line)
+{
+#if NM_MORE_ASSERTS > 5
+ const char *s, *s2;
+
+ nm_assert (line);
+ if (!line->key) {
+ nm_assert (line->line);
+ nm_assert (!line->key_with_prefix);
+ s = nm_str_skip_leading_spaces (line->line);
+ s2 = strchr (s, '=');
+ nm_assert (!s2 || !_shell_is_name (s, s2 - s));
+ } else {
+ nm_assert (line->key_with_prefix);
+ nm_assert (line->key == nm_str_skip_leading_spaces (line->key_with_prefix));
+ nm_assert (_shell_is_name (line->key, -1));
+ }
+#endif
+}
+
static shvarLine *
-line_new (char *value)
+line_new_parse (const char *value, gsize len)
{
shvarLine *line;
+ gsize k, e;
+
+ nm_assert (value);
line = g_slice_new0 (shvarLine);
- line->original = line->value = value;
+ for (k = 0; k < len; k++) {
+ if (g_ascii_isspace (value[k]))
+ continue;
+
+ if ( g_ascii_isalpha (value[k])
+ || value[k] == '_') {
+ for (e = k + 1; e < len; e++) {
+ if (value[e] == '=') {
+ nm_assert (_shell_is_name (&value[k], e - k));
+ line->line = g_strndup (&value[e + 1], len - e - 1);
+ line->key_with_prefix = g_strndup (value, e);
+ line->key = &line->key_with_prefix[k];
+ ASSERT_shvarLine (line);
+ return line;
+ }
+ if ( !g_ascii_isalnum (value[e])
+ && value[e] != '_')
+ break;
+ }
+ }
+ break;
+ }
+ line->line = g_strndup (value, len);
+ ASSERT_shvarLine (line);
+ return line;
+}
+
+static shvarLine *
+line_new_build (const char *key, const char *value)
+{
+ char *value_escaped = NULL;
+ shvarLine *line;
+
+ value = svEscape (value, &value_escaped);
+
+ line = g_slice_new (shvarLine);
+ line->line = value_escaped ?: g_strdup (value);
+ line->key_with_prefix = g_strdup (key);
+ line->key = line->key_with_prefix;
+ ASSERT_shvarLine (line);
return line;
}
+static gboolean
+line_set (shvarLine *line, const char *value)
+{
+ char *value_escaped = NULL;
+ gboolean changed = FALSE;
+
+ ASSERT_shvarLine (line);
+ nm_assert (line->key);
+
+ if (line->key != line->key_with_prefix) {
+ memmove (line->key_with_prefix, line->key, strlen (line->key) + 1);
+ line->key = line->key_with_prefix;
+ changed = TRUE;
+ ASSERT_shvarLine (line);
+ }
+
+ value = svEscape (value, &value_escaped);
+
+ if (line->line) {
+ if (nm_streq (value, line->line)) {
+ g_free (value_escaped);
+ return changed;
+ }
+ g_free (line->line);
+ }
+
+ line->line = value_escaped ?: g_strdup (value);
+ ASSERT_shvarLine (line);
+ return TRUE;
+}
+
static void
line_free (shvarLine *line)
{
- g_free (line->value);
- if (line->original != line->value)
- g_free (line->original);
+ ASSERT_shvarLine (line);
+ g_free (line->line);
+ g_free (line->key_with_prefix);
g_slice_free (shvarLine, line);
}
@@ -660,82 +776,67 @@ line_free (shvarLine *line)
static shvarFile *
svOpenFileInternal (const char *name, gboolean create, GError **error)
{
- shvarFile *s = NULL;
+ shvarFile *s;
gboolean closefd = FALSE;
int errsv = 0;
+ char *arena;
+ const char *p, *q;
+ GError *local = NULL;
+ nm_auto_close int fd = -1;
+ GList *lineList = NULL;
- s = g_slice_new0 (shvarFile);
-
- s->fd = -1;
if (create)
- s->fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
-
- if (!create || s->fd == -1) {
+ fd = open (name, O_RDWR | O_CLOEXEC); /* NOT O_CREAT */
+ if (fd < 0) {
/* try read-only */
- s->fd = open (name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */
- if (s->fd == -1)
+ fd = open (name, O_RDONLY | O_CLOEXEC); /* NOT O_CREAT */
+ if (fd < 0)
errsv = errno;
else
closefd = TRUE;
}
- s->fileName = g_strdup (name);
-
- if (s->fd != -1) {
- struct stat buf;
- char *arena, *p, *q;
- ssize_t nread, total = 0;
-
- if (fstat (s->fd, &buf) < 0) {
- errsv = errno;
- goto bail;
- }
- arena = g_malloc (buf.st_size + 1);
- arena[buf.st_size] = '\0';
-
- while (total < buf.st_size) {
- nread = read (s->fd, arena + total, buf.st_size - total);
- if (nread == -1 && errno == EINTR)
- continue;
- if (nread <= 0) {
- errsv = errno;
- g_free (arena);
- goto bail;
- }
- total += nread;
- }
- /* we'd use g_strsplit() here, but we want a list, not an array */
- for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
- s->lineList = g_list_prepend (s->lineList, line_new (g_strndup (p, q - p)));
- if (p[0])
- s->lineList = g_list_prepend (s->lineList, line_new (g_strdup (p)));
- g_free (arena);
- s->lineList = g_list_reverse (s->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) {
- close (s->fd);
- s->fd = -1;
- }
+ if (fd < 0) {
+ if (create)
+ return svFile_new (name);
- return s;
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
+ "Could not read file '%s': %s",
+ name, strerror (errsv));
+ return NULL;
}
- if (create)
- return s;
+ if (nm_utils_fd_get_contents (fd,
+ 10 * 1024 * 1024,
+ &arena,
+ NULL,
+ &local) < 0) {
+ 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;
+ }
- bail:
- if (s->fd != -1)
- close (s->fd);
- g_free (s->fileName);
- g_slice_free (shvarFile, s);
+ for (p = arena; (q = strchr (p, '\n')) != NULL; p = q + 1)
+ lineList = g_list_prepend (lineList, line_new_parse (p, q - p));
+ if (p[0])
+ lineList = g_list_prepend (lineList, line_new_parse (p, strlen (p)));
+ 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;
+ }
- g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
- "Could not read file '%s': %s",
- name, errsv ? strerror (errsv) : "Unknown error");
- return NULL;
+ return s;
}
/* Open the file <name>, return shvarFile on success, NULL on failure */
@@ -757,105 +858,48 @@ svCreateFile (const char *name)
/*****************************************************************************/
static const GList *
-shlist_find (const GList *current, const char *key, const char **out_value)
+shlist_find (const GList *current, const char *key)
{
- gsize len;
-
- nm_assert (_shell_is_name (key));
+ nm_assert (_shell_is_name (key, -1));
if (current) {
- len = strlen (key);
do {
shvarLine *line = current->data;
- const char *original = line->original;
-
- /* skip over leading spaces */
- while (g_ascii_isspace (original[0]))
- original++;
- if (!strncmp (key, original, len) && original[len] == '=') {
- if (out_value) {
- const char *value = line->value;
-
- if (value) {
- while (g_ascii_isspace (value[0]))
- value++;
- nm_assert (!strncmp (key, value, len) && value[len] == '=');
- value += len + 1;
- }
- *out_value = value;
- }
+ ASSERT_shvarLine (line);
+ if (line->key && nm_streq (line->key, key))
return current;
- }
current = current->next;
} while (current);
}
-
- NM_SET_OUT (out_value, NULL);
return NULL;
}
-static void
-shlist_delete (GList *head, GList *current)
-{
- shvarLine *line = current->data;
-
- nm_assert (head);
- nm_assert (current);
- nm_assert (current->data);
- nm_assert (g_list_position (head, current) >= 0);
-
- if (line->value != line->original)
- g_free (line->value);
- line->value = NULL;
-}
-
-static gboolean
-shlist_delete_all (GList *head, const char *key, gboolean including_last)
-{
- GList *current, *last;
- gboolean changed = FALSE;
-
- last = (GList *) shlist_find (head, key, NULL);
- if (last) {
- while ((current = (GList *) shlist_find (last->next, key, NULL))) {
- shlist_delete (head, last);
- changed = TRUE;
- last = current;
- }
- if (including_last) {
- shlist_delete (head, last);
- changed = TRUE;
- }
- }
- return changed;
-}
-
/*****************************************************************************/
static const char *
_svGetValue (shvarFile *s, const char *key, char **to_free)
{
- const GList *current;
- const char *line_val;
- const char *last_val = NULL;
+ const GList *current, *last;
+ const shvarLine *line;
nm_assert (s);
- nm_assert (_shell_is_name (key));
+ nm_assert (_shell_is_name (key, -1));
nm_assert (to_free);
+ last = NULL;
current = s->lineList;
- while ((current = shlist_find (current, key, &line_val))) {
- last_val = line_val;
+ while ((current = shlist_find (current, key))) {
+ last = current;
current = current->next;
}
-
- if (!last_val) {
- *to_free = NULL;
- return NULL;
+ if (last) {
+ line = last->data;
+ if (line->line)
+ return svUnescape (line->line, to_free);
}
-
- return svUnescape (last_val, to_free);
+ *to_free = NULL;
+ return NULL;
}
const char *
@@ -953,42 +997,42 @@ void
svSetValue (shvarFile *s, const char *key, const char *value)
{
GList *current, *last;
- shvarLine *line;
- gchar *new_value;
- gs_free char *newval_free = NULL;
g_return_if_fail (s != NULL);
g_return_if_fail (key != NULL);
- nm_assert (_shell_is_name (key));
-
- if (shlist_delete_all (s->lineList, key, TRUE))
- s->modified = TRUE;
-
- if (!value)
- return;
-
- new_value = g_strdup_printf ("%s=%s", key, svEscape (value, &newval_free));
-
- current = (GList *) shlist_find (s->lineList, key, NULL);
- if (!current) {
- s->lineList = g_list_append (s->lineList, line_new (new_value));
- s->modified = TRUE;
- return;
- }
+ nm_assert (_shell_is_name (key, -1));
- last = current;
- while ((current = (GList *) shlist_find (current->next, key, NULL)))
+ 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;
+ }
last = current;
- current = last;
+ current = current->next;
+ }
- line = current->data;
- if (line->value != line->original)
- g_free (line->value);
- line->value = new_value;
+ if (!value) {
+ if (last) {
+ shvarLine *line = last->data;
- if (!nm_streq0 (line->original, line->value))
- s->modified = TRUE;
+ if (nm_clear_g_free (&line->line))
+ s->modified = TRUE;
+ }
+ } else {
+ if (!last) {
+ s->lineList = g_list_append (s->lineList, line_new_build (key, value));
+ s->modified = TRUE;
+ } else {
+ if (line_set (last->data, value))
+ s->modified = TRUE;
+ }
+ }
}
/* Set the variable <key> equal to the value <value>.
@@ -1069,34 +1113,35 @@ svWriteFile (shvarFile *s, int mode, GError **error)
f = fdopen (tmpfd, "w");
fseek (f, 0, SEEK_SET);
for (current = s->lineList; current; current = current->next) {
- shvarLine *line = current->data;
+ const shvarLine *line = current->data;
const char *str;
- const char *value;
+ char *s_tmp;
+ gboolean valid_value;
+
+ ASSERT_shvarLine (line);
+
+ if (!line->key) {
+ str = nm_str_skip_leading_spaces (line->line);
+ if (NM_IN_SET (str[0], '\0', '#'))
+ fprintf (f, "%s\n", line->line);
+ else
+ fprintf (f, "#NM: %s\n", line->line);
+ continue;
+ }
- str = line->value;
- if (!str)
+ if (!line->line)
continue;
- while (g_ascii_isspace (str[0]))
- str++;
- if (NM_IN_SET (str[0], '\0', '#'))
- goto write_regular;
-
- value = _shell_is_name_assignment (str);
- if (value) {
- gs_free char *s_tmp = NULL;
-
- /* we check that the assignment can be properly unescaped. */
- if (svUnescape (value, &s_tmp))
- goto write_regular;
- nm_clear_g_free (&s_tmp);
- s_tmp = g_strndup (str, value - str);
- fprintf (f, "%s\n", s_tmp);
+ /* we check that the assignment can be properly unescaped. */
+ valid_value = !!svUnescape (line->line, &s_tmp);
+ g_free (s_tmp);
+
+ if (valid_value)
+ fprintf (f, "%s=%s\n", line->key_with_prefix, line->line);
+ else {
+ fprintf (f, "%s=\n", line->key);
+ fprintf (f, "#NM: %s=%s\n", line->key_with_prefix, line->line);
}
- fprintf (f, "#NM: %s\n", line->value);
- continue;
-write_regular:
- fprintf (f, "%s\n", line->value);
}
fclose (f);
}
@@ -1104,8 +1149,6 @@ write_regular:
return TRUE;
}
-
-/* Close the file descriptor (if open) and free the shvarFile. */
void
svCloseFile (shvarFile *s)
{
@@ -1113,7 +1156,6 @@ svCloseFile (shvarFile *s)
if (s->fd != -1)
close (s->fd);
-
g_free (s->fileName);
g_list_free_full (s->lineList, (GDestroyNotify) line_free);
g_slice_free (shvarFile, s);