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.c612
1 files changed, 323 insertions, 289 deletions
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index cdbd7f24..4e8e29ce 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -1,15 +1,10 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /*
  * shvar.c
  *
  * Implementation of non-destructively reading/writing files containing
  * only shell variable declarations and full-line comments.
  *
- * Includes explicit inheritance mechanism intended for use with
- * Red Hat Linux ifcfg-* files.  There is no protection against
- * inheritance loops; they will generally cause stack overflows.
- * Furthermore, they are only intended for one level of inheritance;
- * the value setting algorithm assumes this.
- *
  * Copyright 1999,2000 Red Hat, Inc.
  *
  * This is free software; you can redistribute it and/or modify it
@@ -28,6 +23,7 @@
  *
  */
 
+#include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -39,103 +35,162 @@
 #include "shvar.h"
 
 /* Open the file <name>, returning a shvarFile on success and NULL on failure.
-   Add a wrinkle to let the caller specify whether or not to create the file
-   (actually, return a structure anyway) if it doesn't exist. */
+ * Add a wrinkle to let the caller specify whether or not to create the file
+ * (actually, return a structure anyway) if it doesn't exist.
+ */
 static shvarFile *
-svOpenFile(const char *name, gboolean create)
+svOpenFileInternal (const char *name, gboolean create, GError **error)
 {
-    shvarFile *s = NULL;
-    int closefd = 0;
-
-    s = g_malloc0(sizeof(shvarFile));
-
-    s->fd = -1;
-    if (create)
-	s->fd = open(name, O_RDWR); /* NOT O_CREAT */
- 
-    if (!create || s->fd == -1) {
-	/* try read-only */
-	s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
-	if (s->fd != -1) closefd = 1;
-    }
-    s->fileName = g_strdup(name);
-
-    if (s->fd != -1) {
-	struct stat buf;
-	char *p, *q;
-
-	if (fstat(s->fd, &buf) < 0) goto bail;
-	s->arena = g_malloc0(buf.st_size + 1);
-
-	if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
-
-	/* we'd use g_strsplit() here, but we want a list, not an array */
-	for(p = s->arena; (q = strchr(p, '\n')) != NULL; p = q + 1) {
-		s->lineList = g_list_append(s->lineList, g_strndup(p, q - p));
+	shvarFile *s = NULL;
+	gboolean closefd = FALSE;
+	int errsv = 0;
+
+	s = g_slice_new0 (shvarFile);
+
+	s->fd = -1;
+	if (create)
+		s->fd = open (name, O_RDWR); /* NOT O_CREAT */
+
+	if (!create || s->fd == -1) {
+		/* try read-only */
+		s->fd = open (name, O_RDONLY); /* NOT O_CREAT */
+		if (s->fd == -1)
+			errsv = errno;
+		else
+			closefd = TRUE;
 	}
-
-	/* 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;
+	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_append (s->lineList, g_strndup (p, q - p));
+		g_free (arena);
+
+		/* 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;
+		}
+
+		return s;
 	}
 
-        return s;
-    }
+	if (create)
+		return s;
 
-    if (create) {
-        return s;
-    }
+ bail:
+	if (s->fd != -1)
+		close (s->fd);
+	g_free (s->fileName);
+	g_slice_free (shvarFile, s);
 
-bail:
-    if (s->fd != -1) close(s->fd);
-    g_free (s->arena);
-    g_free (s->fileName);
-    g_free (s);
-    return NULL;
+	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;
 }
 
 /* Open the file <name>, return shvarFile on success, NULL on failure */
 shvarFile *
-svNewFile(const char *name)
+svOpenFile (const char *name, GError **error)
 {
-    return svOpenFile(name, FALSE);
+	return svOpenFileInternal (name, FALSE, error);
 }
 
 /* Create a new file structure, returning actual data if the file exists,
- * and a suitable starting point if it doesn't. */
+ * and a suitable starting point if it doesn't.
+ */
 shvarFile *
-svCreateFile(const char *name)
+svCreateFile (const char *name)
 {
-    return svOpenFile(name, TRUE);
+	return svOpenFileInternal (name, TRUE, NULL);
 }
 
 /* remove escaped characters in place */
 void
-svUnescape(char *s) {
-    int len, i;
-
-    len = strlen(s);
-    if (len >= 2 && (s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
-	i = len - 2;
-	if (i == 0)
-	  s[0] = '\0';
-	else {
-	  memmove(s, s+1, i);
-	  s[i+1] = '\0';
-	  len = i;
+svUnescape (char *s)
+{
+	size_t len, idx_rd = 0, idx_wr = 0;
+	char c;
+
+	len = strlen (s);
+	if (len < 2) {
+		if (s[0] == '\\')
+			s[0] = '\0';
+		return;
 	}
-    }
-    for (i = 0; i < len; i++) {
-	if (s[i] == '\\') {
-	    memmove(s+i, s+i+1, len-(i+1));
-	    len--;
+
+	if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
+		if (len == 2) {
+			s[0] = '\0';
+			return;
+		}
+		if (len == 3) {
+			if (s[1] == '\\') {
+				s[0] = '\0';
+			} else {
+				s[0] = s[1];
+				s[1] = '\0';
+			}
+			return;
+		}
+		s[--len] = '\0';
+		idx_rd = 1;
+	} else {
+		/* seek for the first escape... */
+		char *p = strchr (s, '\\');
+
+		if (!p)
+			return;
+		if (p[1] == '\0') {
+			p[0] = '\0';
+			return;
+		}
+		idx_wr = idx_rd = (p - s);
 	}
-	s[len] = '\0';
-    }
-}
 
+	/* idx_rd points to the first escape. Walk the string and shift the
+	 * characters from idx_rd to idx_wr.
+	 */
+	while ((c = s[idx_rd++])) {
+		if (c == '\\') {
+			if (s[idx_rd] == '\0') {
+				s[idx_wr] = '\0';
+				return;
+			}
+			s[idx_wr++] = s[idx_rd++];
+			continue;
+		}
+		s[idx_wr++] = c;
+	}
+	s[idx_wr] = '\0';
+}
 
 /* create a new string with all necessary characters escaped.
  * caller must free returned string
@@ -143,42 +198,46 @@ svUnescape(char *s) {
 static const char escapees[] = "\"'\\$~`";		/* must be escaped */
 static const char spaces[] = " \t|&;()<>";		/* only require "" */
 static const char newlines[] = "\n\r";			/* will be removed */
+
 char *
-svEscape(const char *s) {
-    char *new;
-    int i, j, mangle = 0, space = 0, newline = 0;
-    int newlen, slen;
-    static int esclen, splen;
-
-    if (!esclen) esclen = strlen(escapees);
-    if (!splen) splen = strlen(spaces);
-    slen = strlen(s);
-
-    for (i = 0; i < slen; i++) {
-	if (strchr(escapees, s[i])) mangle++;
-	if (strchr(spaces, s[i])) space++;
-	if (strchr(newlines, s[i])) newline++;
-    }
-    if (!mangle && !space && !newline) return strdup(s);
-
-    newlen = slen + mangle - newline + 3;	/* 3 is extra ""\0 */
-    new = g_malloc0(newlen);
-    if (!new) return NULL;
-
-    j = 0;
-    new[j++] = '"';
-    for (i = 0; i < slen; i++) {
-	if (strchr(newlines, s[i]))
-	    continue;
-	if (strchr(escapees, s[i])) {
-	    new[j++] = '\\';
+svEscape (const char *s)
+{
+	char *new;
+	int i, j, mangle = 0, space = 0, newline = 0;
+	int newlen, slen;
+
+	slen = strlen (s);
+
+	for (i = 0; i < slen; i++) {
+		if (strchr (escapees, s[i]))
+			mangle++;
+		if (strchr (spaces, s[i]))
+			space++;
+		if (strchr (newlines, s[i]))
+			newline++;
 	}
-	new[j++] = s[i];
-    }
-    new[j++] = '"';
-    g_assert(j == slen + mangle - newline + 2); /* j is the index of the '\0' */
+	if (!mangle && !space && !newline)
+		return strdup (s);
+
+	newlen = slen + mangle - newline + 3;	/* 3 is extra ""\0 */
+	new = g_malloc (newlen);
+
+	j = 0;
+	new[j++] = '"';
+	for (i = 0; i < slen; i++) {
+		if (strchr (newlines, s[i]))
+			continue;
+		if (strchr (escapees, s[i])) {
+			new[j++] = '\\';
+		}
+		new[j++] = s[i];
+	}
+	new[j++] = '"';
+	new[j++] = '\0'
+;
+	g_assert (j == slen + mangle - newline + 3);
 
-    return new;
+	return new;
 }
 
 /* Get the value associated with the key, and leave the current pointer
@@ -186,215 +245,190 @@ svEscape(const char *s) {
  * be freed by the caller.
  */
 char *
-svGetValue(shvarFile *s, const char *key, gboolean verbatim)
+svGetValue (shvarFile *s, const char *key, gboolean verbatim)
 {
-    char *value = NULL;
-    char *line;
-    char *keyString;
-    int len;
-
-    g_assert(s);
-    g_assert(key);
-
-    keyString = g_malloc0(strlen(key) + 2);
-    strcpy(keyString, key);
-    keyString[strlen(key)] = '=';
-    len = strlen(keyString);
-
-    for (s->current = s->lineList; s->current; s->current = s->current->next) {
-	line = s->current->data;
-	if (!strncmp(keyString, line, len)) {
-	    value = g_strdup(line + len);
-	    if (!verbatim)
-	      svUnescape(value);
-	    break;
+	char *value = NULL;
+	char *line;
+	char *keyString;
+	int len;
+
+	g_return_val_if_fail (s != NULL, NULL);
+	g_return_val_if_fail (key != NULL, NULL);
+
+	keyString = g_strdup_printf ("%s=", key);
+	len = strlen (keyString);
+
+	for (s->current = s->lineList; s->current; s->current = s->current->next) {
+		line = s->current->data;
+		if (!strncmp (keyString, line, len)) {
+			value = g_strdup (line + len);
+			if (!verbatim)
+				svUnescape (value);
+			break;
+		}
 	}
-    }
-    g_free(keyString);
+	g_free (keyString);
 
-    if (value) {
-	if (value[0]) {
-	    return value;
+	if (value && value[0]) {
+		return value;
 	} else {
-	    g_free(value);
-	    return NULL;
+		g_free (value);
+		return NULL;
 	}
-    }
-    if (s->parent) value = svGetValue(s->parent, key, verbatim);
-    return value;
 }
 
-/* return 1 if <key> resolves to any truth value (e.g. "yes", "y", "true")
- * return 0 if <key> resolves to any non-truth value (e.g. "no", "n", "false")
+/* 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 <default> otherwise
  */
-int
-svTrueValue(shvarFile *s, const char *key, int def)
+gboolean
+svTrueValue (shvarFile *s, const char *key, gboolean def)
 {
-    char *tmp;
-    int returnValue = def;
-
-    tmp = svGetValue(s, key, FALSE);
-    if (!tmp) return returnValue;
-
-    if ( (!strcasecmp("yes", tmp)) ||
-	 (!strcasecmp("true", tmp)) ||
-	 (!strcasecmp("t", tmp)) ||
-	 (!strcasecmp("y", tmp)) ) returnValue = 1;
-    else
-    if ( (!strcasecmp("no", tmp)) ||
-	 (!strcasecmp("false", tmp)) ||
-	 (!strcasecmp("f", tmp)) ||
-	 (!strcasecmp("n", tmp)) ) returnValue = 0;
-
-    g_free (tmp);
-    return returnValue;
+	char *tmp;
+	gboolean returnValue = def;
+
+	tmp = svGetValue (s, key, FALSE);
+	if (!tmp)
+		return returnValue;
+
+	if (   !g_ascii_strcasecmp ("yes", tmp)
+	    || !g_ascii_strcasecmp ("true", tmp)
+	    || !g_ascii_strcasecmp ("t", tmp)
+	    || !g_ascii_strcasecmp ("y", tmp))
+		returnValue = TRUE;
+	else if (   !g_ascii_strcasecmp ("no", tmp)
+	         || !g_ascii_strcasecmp ("false", tmp)
+	         || !g_ascii_strcasecmp ("f", tmp)
+	         || !g_ascii_strcasecmp ("n", tmp))
+		returnValue = FALSE;
+
+	g_free (tmp);
+	return returnValue;
 }
 
 
 /* Set the variable <key> equal to the value <value>.
  * If <key> does not exist, and the <current> pointer is set, append
- * the key=value pair after that line.  Otherwise, prepend the pair
- * to the top of the file.  Here's the algorithm, as the C code
- * seems to be rather dense:
- *
- * if (value == NULL), then:
- *     if val2 (parent): change line to key= or append line key=
- *     if val1 (this)  : delete line
- *     else noop
- * else use this table:
- *                                val2
- *             NULL              value               other
- * v   NULL    append line       noop                append line
- * a
- * l   value   noop              noop                noop
- * 1
- *     other   change line       delete line         change line
- *
- * No changes are ever made to the parent config file, only to the
- * specific file passed on the command line.
- *
+ * the key=value pair after that line.  Otherwise, append the pair
+ * to the bottom of the file.
  */
 void
-svSetValue(shvarFile *s, const char *key, const char *value, gboolean verbatim)
+svSetValue (shvarFile *s, const char *key, const char *value, gboolean verbatim)
 {
-    char *newval = NULL, *val1 = NULL, *val2 = NULL;
-    char *keyValue;
-
-    g_assert(s);
-    g_assert(key);
-    /* value may be NULL */
-
-    if (value)
-        newval = verbatim ? g_strdup(value) : svEscape(value);
-    keyValue = g_strdup_printf("%s=%s", key, newval ? newval : "");
-
-    val1 = svGetValue(s, key, FALSE);
-    if (val1 && newval && !strcmp(val1, newval)) goto bail;
-    if (s->parent) val2 = svGetValue(s->parent, key, FALSE);
-
-    if (!newval || !newval[0]) {
-	/* delete value somehow */
-	if (val2) {
-	    /* change/append line to get key= */
-	    if (s->current) s->current->data = keyValue;
-	    else s->lineList = g_list_append(s->lineList, keyValue);
-	    s->modified = 1;
-	    goto end;
-	} else if (val1) {
-	    /* delete line */
-	    s->lineList = g_list_remove_link(s->lineList, s->current);
-	    g_list_free_1(s->current);
-	    s->modified = 1;
+	char *newval = NULL, *oldval = NULL;
+	char *keyValue;
+
+	g_return_if_fail (s != NULL);
+	g_return_if_fail (key != NULL);
+	/* value may be NULL */
+
+	if (value)
+		newval = verbatim ? g_strdup (value) : svEscape (value);
+	keyValue = g_strdup_printf ("%s=%s", key, newval ? newval : "");
+
+	oldval = svGetValue (s, key, FALSE);
+
+	if (!newval || !newval[0]) {
+		/* delete value */
+		if (oldval) {
+			/* delete line */
+			s->lineList = g_list_remove_link (s->lineList, s->current);
+			g_list_free_1 (s->current);
+			s->modified = TRUE;
+		}
+		goto bail; /* do not need keyValue */
+	}
+
+	if (!oldval) {
+		/* append line */
+		s->lineList = g_list_append (s->lineList, keyValue);
+		s->modified = TRUE;
+		goto end;
+	}
+
+	if (strcmp (oldval, newval) != 0) {
+		/* change line */
+		if (s->current)
+			s->current->data = keyValue;
+		else
+			s->lineList = g_list_append (s->lineList, keyValue);
+		s->modified = TRUE;
 	}
-	goto bail; /* do not need keyValue */
-    }
-
-    if (!val1) {
-	if (val2 && !strcmp(val2, newval)) goto end;
-	/* append line */
-	s->lineList = g_list_append(s->lineList, keyValue);
-	s->modified = 1;
+
+ end:
+	g_free (newval);
+	g_free (oldval);
+	return;
+
+ bail:
+	g_free (keyValue);
 	goto end;
-    }
-
-    /* deal with a whole line of noops */
-    if (val1 && !strcmp(val1, newval)) goto end;
-
-    /* At this point, val1 && val1 != value */
-    if (val2 && !strcmp(val2, newval)) {
-	/* delete line */
-	s->lineList = g_list_remove_link(s->lineList, s->current);
-	g_list_free_1(s->current);
-	s->modified = 1;
-	goto bail; /* do not need keyValue */
-    } else {
-	/* change line */
-	if (s->current) s->current->data = keyValue;
-	else s->lineList = g_list_append(s->lineList, keyValue);
-	s->modified = 1;
-    }
-
-end:
-    g_free(newval);
-    g_free(val1);
-    g_free(val2);
-    return;
-
-bail:
-    g_free (keyValue);
-    goto end;
 }
 
-/* Write the current contents iff modified.  Returns -1 on error
- * and 0 on success.  Do not write if no values have been modified.
+/* 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
  * re-writing an existing file, and is passed unchanged to the
  * open() syscall.
  */
-int
-svWriteFile(shvarFile *s, int mode)
+gboolean
+svWriteFile (shvarFile *s, int mode, GError **error)
 {
-    FILE *f;
-    int tmpfd;
-
-    if (s->modified) {
-	if (s->fd == -1)
-	    s->fd = open(s->fileName, O_WRONLY|O_CREAT, mode);
-	if (s->fd == -1)
-	    return -1;
-	if (ftruncate(s->fd, 0) < 0)
-	    return -1;
-
-	tmpfd = dup(s->fd);
-	f = fdopen(tmpfd, "w");
-	fseek(f, 0, SEEK_SET);
-	for (s->current = s->lineList; s->current; s->current = s->current->next) {
-	    char *line = s->current->data;
-	    fprintf(f, "%s\n", line);
+	FILE *f;
+	int tmpfd;
+
+	if (s->modified) {
+		if (s->fd == -1)
+			s->fd = open (s->fileName, O_WRONLY | O_CREAT, mode);
+		if (s->fd == -1) {
+			int errsv = errno;
+
+			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
+			             "Could not open file '%s' for writing: %s",
+			             s->fileName, strerror (errsv));
+			return FALSE;
+		}
+		if (ftruncate (s->fd, 0) < 0) {
+			int errsv = errno;
+
+			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
+			             "Could not overwrite file '%s': %s",
+			             s->fileName, strerror (errsv));
+			return FALSE;
+		}
+
+		tmpfd = dup (s->fd);
+		if (tmpfd == -1) {
+			int errsv = errno;
+
+			g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
+			             "Internal error writing file '%s': %s",
+			             s->fileName, strerror (errsv));
+			return FALSE;
+		}
+		f = fdopen (tmpfd, "w");
+		fseek (f, 0, SEEK_SET);
+		for (s->current = s->lineList; s->current; s->current = s->current->next) {
+			char *line = s->current->data;
+			fprintf (f, "%s\n", line);
+		}
+		fclose (f);
 	}
-	fclose(f);
-    }
 
-    return 0;
+	return TRUE;
 }
 
- 
-/* Close the file descriptor (if open) and delete the shvarFile.
- * Returns -1 on error and 0 on success.
- */
-int
-svCloseFile(shvarFile *s)
-{
 
-    g_assert(s);
+/* Close the file descriptor (if open) and free the shvarFile. */
+void
+svCloseFile (shvarFile *s)
+{
+	g_return_if_fail (s != NULL);
 
-    if (s->fd != -1) close(s->fd);
+	if (s->fd != -1)
+		close (s->fd);
 
-    g_free(s->arena);
-    g_free(s->fileName);
-    g_list_foreach (s->lineList, (GFunc) g_free, NULL);
-    g_list_free(s->lineList); /* implicitly frees s->current */
-    g_free(s);
-    return 0;
+	g_free (s->fileName);
+	g_list_free_full (s->lineList, g_free); /* implicitly frees s->current */
+	g_slice_free (shvarFile, s);
 }