aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael K. Johnson <johnsonm@redhat.com>1999-09-06 22:42:05 +0000
committerMichael K. Johnson <johnsonm@redhat.com>1999-09-06 22:42:05 +0000
commit57487e296e885786382588f547d259bec4c3595e (patch)
treeb85655e478e1c22807d754df6a6c3d4f39cb46fc
parent5ae520ecf238d6c5d1db515bb3217501f93c8a13 (diff)
downloadinitscripts-57487e296e885786382588f547d259bec4c3595e.tar
initscripts-57487e296e885786382588f547d259bec4c3595e.tar.gz
initscripts-57487e296e885786382588f547d259bec4c3595e.tar.bz2
initscripts-57487e296e885786382588f547d259bec4c3595e.tar.xz
initscripts-57487e296e885786382588f547d259bec4c3595e.zip
first pass at ppp-watch
-rw-r--r--src/Makefile12
-rw-r--r--src/ppp-watch.c420
-rw-r--r--src/shvar.c377
-rw-r--r--src/shvar.h100
4 files changed, 908 insertions, 1 deletions
diff --git a/src/Makefile b/src/Makefile
index 8b9616ad..6dfe6a93 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,6 +1,8 @@
CFLAGS+=-Wall -D_GNU_SOURCE -g
-PROGS=usernetctl doexec netreport testd usleep ipcalc initlog minilogd loglevel getkey
+PROGS=usernetctl doexec netreport testd usleep ipcalc initlog minilogd \
+ loglevel getkey ppp-watch
+PPPWATCH_OBJS=ppp-watch.o shvar.o
INITLOG_OBJS=initlog.o process.o
USLEEP_OBJS=usleep.o
@@ -20,6 +22,7 @@ install:
install -s -m 755 minilogd $(ROOT)/sbin/minilogd
install -s -m 755 loglevel $(ROOT)/sbin/loglevel
install -s -m 755 getkey $(ROOT)/sbin/getkey
+ install -s -m 755 ppp-watch $(ROOT)/sbin/ppp-watch
install -m 644 initlog.1 $(ROOT)/usr/man/man1
install -m 644 doexec.1 $(ROOT)/usr/man/man1
install -m 644 netreport.1 $(ROOT)/usr/man/man1
@@ -42,3 +45,10 @@ initlog: $(INITLOG_OBJS)
usleep: $(USLEEP_OBJS)
$(CC) $(LDFLAGS) -o $@ $(USLEEP_OBJS) -lpopt
+
+ppp-watch: $(PPPWATCH_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(PPPWATCH_OBJS) -lglib
+shvar.o: shvar.c
+ $(CC) $(CFLAGS) `glib-config --cflags` -c shvar.c -o shvar.o
+ppp-watch.o: ppp-watch.c
+ $(CC) $(CFLAGS) `glib-config --cflags` -c ppp-watch.c -o ppp-watch.o
diff --git a/src/ppp-watch.c b/src/ppp-watch.c
new file mode 100644
index 00000000..2e3cd257
--- /dev/null
+++ b/src/ppp-watch.c
@@ -0,0 +1,420 @@
+/*
+ * ppp-watch.c
+ *
+ * Bring up a PPP connection and Do The Right Thing[tm] to make bringing
+ * the connection up or down with ifup and ifdown syncronous. Takes
+ * one argument: the logical name of the device to bring up. Does not
+ * detach until the interface is up or has permanently failed to come up.
+ *
+ * Copyright 1999 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* Algorithm:
+ * fork
+ * if child:
+ * Register with netreport. (now exit implies deregister first)
+ * fork/exec ifup-ppp daemon <interface>
+ * else:
+ * while (1):
+ * sigsuspend()
+ * if SIGTERM:
+ * kill pppd pgrp
+ * exit
+ * if SIGHUP:
+ * reload ifcfg files
+ * kill pppd pgrp
+ * wait for SIGCHLD to redial
+ * if SIGIO:
+ * if no physical device found: continue
+ * elif physical device is down:
+ * wait for pppd to exit to redial if appropriate
+ * else: (physical device is up)
+ * detach; continue
+ * if SIGCHLD: (pppd exited)
+ * wait()
+ * if pppd exited:
+ * if PERSIST: redial
+ * else: exit
+ * else: (pppd was killed)
+ * exit
+ *
+ *
+ * When ppp-watch itself dies for reasons of its own, it uses a return code
+ * higher than 30 so as not to clash with pppd return codes, which, as of
+ * this writing, range from 0 to 19.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <net/if.h>
+
+#include "shvar.h"
+
+static int theSigterm = 0;
+static int theSighup = 0;
+static int theSigio = 0;
+static int theSigchld = 0;
+
+
+static void
+detach(int now, int parentExitCode) {
+ static int pipeArray[2];
+ char exitCode;
+
+ if (now) {
+ /* execute -- ignore errors in case called more than once */
+ exitCode = parentExitCode;
+ write(pipeArray[1], &exitCode, 1);
+ close(pipeArray[1]);
+
+ } else {
+ /* set up */
+ int child;
+
+ if (pipe(pipeArray)) exit (25);
+
+ child = fork();
+ if (child < 0) exit(26);
+ if (child) {
+ /* parent process */
+ close (pipeArray[1]);
+ while (read (pipeArray[0], &exitCode, 1) < 0) {
+ switch (errno) {
+ case EINTR: continue;
+ default: exit (27); /* this will catch EIO in particular */
+ }
+ }
+ exit(exitCode);
+
+ } else {
+ /* child process */
+ close (pipeArray[0]);
+ /* become a daemon */
+ close (0);
+ close (1);
+ close (2);
+ }
+ }
+}
+
+
+static void
+doPidFile(char *device) {
+ static char *pidFileName = NULL;
+ char *pidFilePath;
+ int fd; FILE *f;
+
+ if (pidFileName) {
+ /* remove it */
+ pidFilePath = alloca(strlen(pidFileName) + 25);
+ sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
+ unlink(pidFilePath); /* not much we can do in case of error... */
+ }
+
+ if (device) {
+ /* create it */
+ pidFileName = device;
+ pidFilePath = alloca(strlen(pidFileName) + 25);
+ sprintf(pidFilePath, "/var/run/pppwatch-%s.pid", pidFileName);
+ fd = open(pidFilePath, O_WRONLY|O_TRUNC|O_CREAT,
+ S_IRUSR|S_IWUSR | S_IRGRP | S_IROTH);
+ f = fdopen(fd, "r+");
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ }
+}
+
+
+
+
+
+int
+fork_exec(int wait, char *path, char *arg1, char *arg2)
+{
+ pid_t child;
+ int status;
+
+ if (!(child = fork())) {
+ /* child */
+ execl(path, path, arg1, arg2, 0);
+ perror(path);
+ _exit (1);
+ }
+
+ if (wait) {
+ wait4 (child, &status, 0, NULL);
+ if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+}
+
+
+
+static void
+cleanExit(int exitCode) {
+ fork_exec(1, "/sbin/netreport", "-r", NULL);
+ detach(1, exitCode);
+ exit(exitCode);
+ doPidFile(NULL);
+}
+
+
+
+static void
+signal_handler (int signum) {
+ switch(signum) {
+ case SIGTERM:
+ theSigterm = 1; break;
+ case SIGHUP:
+ theSighup = 1; break;
+ case SIGIO:
+ theSigio = 1; break;
+ case SIGCHLD:
+ theSigchld = 1; break;
+ }
+}
+
+
+static shvarFile *
+shvarfilesGet(char *interfaceName) {
+ shvarFile *ifcfg;
+ char *ifcfgName, *ifcfgParentName, *ifcfgParentDiff;
+ static char ifcfgPrefix[] = "/etc/sysconfig/network-scripts/ifcfg-";
+
+ ifcfgName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
+ sprintf(ifcfgName, "%s%s", ifcfgPrefix, interfaceName);
+ ifcfg = svNewFile(ifcfgName);
+ if (!ifcfg) return NULL;
+
+ /* Do we have a parent interface to inherit? */
+ ifcfgParentDiff = strchr(interfaceName, '-');
+ if (ifcfgParentDiff) {
+ /* allocate more than enough memory... */
+ ifcfgParentName = alloca(sizeof(ifcfgPrefix)+strlen(interfaceName)+1);
+ strcpy(ifcfgParentName, ifcfgPrefix);
+ strncat(ifcfgParentName, interfaceName, ifcfgParentDiff-interfaceName);
+ ifcfg->parent = svNewFile(ifcfgParentName);
+ }
+
+ return ifcfg;
+}
+
+
+
+static char *
+pppLogicalToPhysical(int *pppdPid, char *logicalName) {
+ char *mapFileName;
+ char buffer[20]; /* more than enough space for ppp<n> */
+ char *p, *q;
+ int f, n;
+ char *physicalDevice = NULL;
+
+ mapFileName = alloca (strlen(logicalName)+20);
+ sprintf(mapFileName, "/var/run/ppp-%s.pid", logicalName);
+ if ((f = open(mapFileName, O_RDONLY)) >= 0) {
+ n = read(f, buffer, 20);
+ if (n > 0) {
+ buffer[n] = '\0';
+ /* get past pid */
+ p = buffer; while (*p && *p != '\n') p++; *p = '\0'; p++;
+ if (pppdPid) *pppdPid = atoi(buffer);
+ /* get rid of \n */
+ q = p; while (*q && *q != '\n' && q < buffer+n) q++; *q = '\0';
+ if (*p) physicalDevice = strdup(p);
+ }
+ close(f);
+ }
+
+ return physicalDevice;
+}
+
+
+static int
+interfaceStatus(char *device) {
+ int sock = 0;
+ int pfs[] = {AF_INET, AF_IPX, AF_AX25, AF_APPLETALK, 0};
+ int p = 0;
+ struct ifreq ifr;
+ int retcode = 0;
+
+ while (!sock && pfs[p]) {
+ sock = socket(pfs[p++], SOCK_DGRAM, 0);
+ }
+ if (!sock) return 0;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, device);
+
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
+ retcode = 0;
+ } else if (ifr.ifr_flags & IFF_UP) {
+ retcode = 1;
+ }
+
+ close(sock);
+ return retcode;
+}
+
+
+
+
+
+
+
+
+
+
+int
+main(int argc, char **argv) {
+ int status, waited;
+ char *device, *logicalDevice;
+ shvarFile *ifcfg;
+ sigset_t sigs;
+ int pppdPid;
+ int timeout = 30;
+ char *temp;
+ struct timeval tv;
+
+ if (argc != 2) {
+ fprintf (stderr, "usage: ppp-watch [ifcfg-]<logical-name>");
+ exit(30);
+ }
+
+ detach(0, 0); /* prepare */
+
+ signal(SIGTERM, signal_handler);
+ signal(SIGHUP, signal_handler);
+ signal(SIGIO, signal_handler);
+ signal(SIGCHLD, signal_handler);
+
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGTERM);
+ sigaddset(&sigs, SIGHUP);
+ sigaddset(&sigs, SIGIO);
+ sigaddset(&sigs, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigs, NULL);
+
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGTERM);
+ sigdelset(&sigs, SIGHUP);
+ sigdelset(&sigs, SIGIO);
+ sigdelset(&sigs, SIGCHLD);
+
+ doPidFile(device);
+
+ fork_exec(1, "/sbin/netreport", NULL, NULL);
+
+ if (!strncmp(argv[1], "ifcfg-", 6)) {
+ device = argv[1] + 6;
+ } else {
+ device = argv[1];
+ }
+ ifcfg = shvarfilesGet(device);
+ if (!ifcfg) cleanExit(28);
+
+ fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device);
+ temp = svGetValue(ifcfg, "RETRYTIMEOUT");
+ if (temp) {
+ timeout = atoi(temp);
+ free(temp);
+ } else {
+ timeout = 30;
+ }
+
+ while (1) {
+ sigsuspend(&sigs);
+ if (theSigterm) {
+ theSigterm = 0;
+ if (logicalDevice) free(logicalDevice);
+ logicalDevice = pppLogicalToPhysical(&pppdPid, device);
+ if (logicalDevice) free(logicalDevice);
+ kill(-pppdPid, SIGTERM);
+ cleanExit(0);
+ }
+ if (theSighup) {
+ theSighup = 0;
+ if (ifcfg->parent) svCloseFile(ifcfg->parent);
+ svCloseFile(ifcfg);
+ ifcfg = shvarfilesGet(device);
+ logicalDevice = pppLogicalToPhysical(&pppdPid, device);
+ if (logicalDevice) free(logicalDevice);
+ kill(-pppdPid, SIGTERM);
+ /* redial when SIGCHLD arrives */
+ timeout = 1; /* give things time to stabilize... */
+ }
+ if (theSigio) {
+ theSigio = 0;
+ if (logicalDevice) {
+ free(logicalDevice);
+ temp = svGetValue(ifcfg, "DISCONNECTTIMEOUT");
+ if (temp) {
+ timeout = atoi(temp);
+ free(temp);
+ } else {
+ timeout = 30;
+ }
+ }
+ logicalDevice = pppLogicalToPhysical(NULL, device);
+ if (logicalDevice) {
+ if (interfaceStatus(device)) {
+ /* device is up */
+ detach(1, 0);
+ }
+ }
+ }
+ if (theSigchld) {
+ theSigchld = 0;
+ waited = wait3(&status, 0, NULL);
+ if (waited < 0) continue;
+
+ if (!WIFEXITED(status)) cleanExit(29);
+
+ if (svTrueValue(ifcfg, "PERSIST", 0)) {
+ fork_exec(0, "/etc/sysconfig/network-scripts/ifup-ppp", "daemon", device);
+ temp = svGetValue(ifcfg, "RETRYTIMEOUT");
+ if (temp) {
+ timeout = atoi(temp);
+ free(temp);
+ } else {
+ timeout = 30;
+ }
+ tv.tv_sec = timeout;
+ select(0, NULL, NULL, NULL, &tv);
+ } else {
+ cleanExit(0);
+ }
+ }
+ }
+}
diff --git a/src/shvar.c b/src/shvar.c
new file mode 100644
index 00000000..5c5233e1
--- /dev/null
+++ b/src/shvar.c
@@ -0,0 +1,377 @@
+/* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
+/*
+ * 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 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "shvar.h"
+
+/* Open the file <name>, return shvarFile on success, NULL on failure */
+shvarFile *
+svNewFile(char *name)
+{
+ shvarFile *s = NULL;
+ int closefd = 0;
+
+ s = calloc(sizeof(shvarFile), 1);
+ if (!s) return NULL;
+
+ s->fd = open(name, O_RDWR); /* NOT O_CREAT */
+ if (s->fd == -1) {
+ /* try read-only */
+ s->fd = open(name, O_RDONLY); /* NOT O_CREAT */
+ if (s->fd) closefd = 1;
+ }
+ s->fileName = strdup(name);
+
+ if (s->fd != -1) {
+ struct stat buf;
+ char *tmp;
+
+ if (fstat(s->fd, &buf) < 0) goto bail;
+ s->arena = calloc(buf.st_size, 1);
+ if (!s->arena) goto bail;
+ if (read(s->fd, s->arena, buf.st_size) < 0) goto bail;
+ /* Yes, I know that strtok is evil, except that this is
+ * precisely what it was intended for in the first place...
+ */
+ tmp = strtok(s->arena, "\n");
+ while (tmp) {
+ s->lineList = g_list_append(s->lineList, tmp);
+ tmp = strtok(NULL, "\n");
+ }
+ if (closefd) {
+ close(s->fd);
+ s->fd = -1;
+ }
+ }
+
+ return s;
+
+bail:
+ if (s->fd != -1) close(s->fd);
+ if (s->arena) free (s->arena);
+ if (s->fileName) free (s->fileName);
+ free (s);
+ return NULL;
+}
+
+/* remove escaped characters in place */
+static void
+unescape(char *s) {
+ int len, i;
+
+ len = strlen(s);
+ if ((s[0] == '"' || s[0] == '\'') && s[0] == s[len-1]) {
+ i = len - 2;
+ memmove(s, s+1, i);
+ s[i+1] = '\0';
+ len = i;
+ }
+ for (i = 0; i < len; i++) {
+ if (s[i] == '\\') {
+ memmove(s+i, s+i+1, len-(i+1));
+ len--;
+ }
+ s[len] = '\0';
+ }
+}
+
+
+/* create a new string with all necessary characters escaped.
+ * caller must free returned string
+ */
+static const char escapees[] = "\"'\\$~`"; /* must be escaped */
+static const char spaces[] = " \t"; /* only require "" */
+static char *
+escape(char *s) {
+ char *new;
+ int i, j, mangle = 0, space = 0;
+ int newlen, slen;
+ static int esclen, splen;
+
+ if (!esclen) esclen = strlen(escapees);
+ if (!splen) splen = strlen(spaces);
+ for (i = 0; i < esclen; i++) {
+ if (strchr(s, escapees[i])) mangle++;
+ }
+ for (i = 0; i < splen; i++) {
+ if (strchr(s, spaces[i])) space++;
+ }
+ if (!mangle && !space) return strdup(s);
+
+ slen = strlen(s);
+ newlen = slen + mangle + 3; /* 3 is extra ""\0 */
+ new = calloc(newlen, 1);
+ if (!new) return NULL;
+
+ new[0] = '"';
+ for (i = 0, j = 1; i < slen; i++, j++) {
+ if (strchr(escapees, s[i])) {
+ new[j++] = '\\';
+ }
+ new[j] = s[i];
+ }
+ new[j] = '"';
+
+ return new;
+}
+
+/* Get the value associated with the key, and leave the current pointer
+ * pointing at the line containing the value. The char* returned MUST
+ * be freed by the caller.
+ */
+char *
+svGetValue(shvarFile *s, char *key)
+{
+ char *value = NULL;
+ char *line;
+ char *keyString;
+ int len;
+
+ assert(s);
+ assert(key);
+
+ keyString = calloc (strlen(key) + 2, 1);
+ if (!keyString) return NULL;
+ 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 = strdup(line + len);
+ unescape(value);
+ break;
+ }
+ }
+ free(keyString);
+
+ if (value) {
+ if (value[0]) {
+ return value;
+ } else {
+ free (value);
+ return NULL;
+ }
+ }
+ if (s->parent) value = svGetValue(s->parent, key);
+ 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 <default> otherwise
+ */
+int
+svTrueValue(shvarFile *s, char *key, int def)
+{
+ char *tmp;
+ int returnValue = def;
+
+ tmp = svGetValue(s, key);
+ 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;
+
+ 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.
+ *
+ */
+void
+svSetValue(shvarFile *s, char *key, char *value)
+{
+ char *val1 = NULL, *val2 = NULL;
+ char *keyValue;
+
+ assert(s);
+ assert(key);
+ /* value may be NULL */
+
+ if (value) value = escape(value);
+ keyValue = malloc (strlen(key) + (value?strlen(value):0) + 2);
+ if (!keyValue) return;
+ sprintf(keyValue, "%s=%s", key, value?value:"");
+
+ val1 = svGetValue(s, key);
+ if (val1 && value && !strcmp(val1, value)) goto bail;
+ if (s->parent) val2 = svGetValue(s->parent, key);
+
+ if (!value) {
+ /* 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->freeList = g_list_append(s->freeList, keyValue);
+ s->modified = 1;
+ } else if (val1) {
+ /* 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 */
+ }
+ goto end;
+ }
+
+ if (!val1) {
+ if (val2 && !strcmp(val2, value)) goto end;
+ /* append line */
+ s->lineList = g_list_append(s->lineList, keyValue);
+ s->freeList = g_list_append(s->freeList, keyValue);
+ s->modified = 1;
+ goto end;
+ }
+
+ /* deal with a whole line of noops */
+ if (val1 && !strcmp(val1, value)) goto end;
+
+ /* At this point, val1 && val1 != value */
+ if (val2 && !strcmp(val2, value)) {
+ /* 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->freeList = g_list_append(s->freeList, keyValue);
+ s->modified = 1;
+ }
+
+end:
+ if (value) free(value);
+ if (val1) free(val1);
+ if (val2) free(val2);
+ return;
+
+bail:
+ if (keyValue) 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.
+ * 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)
+{
+ 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);
+ }
+ fclose(f);
+ }
+
+ return 0;
+}
+
+
+/* Close the file descriptor (if open) and delete the shvarFile.
+ * Returns -1 on error and 0 on success.
+ */
+int
+svCloseFile(shvarFile *s)
+{
+
+ assert(s);
+
+ if (s->fd != -1) close(s->fd);
+
+ free(s->arena);
+ for (s->current = s->freeList; s->current; s->current = s->current->next) {
+ free(s->current->data);
+ }
+ free(s->fileName);
+ g_list_free(s->freeList);
+ g_list_free(s->lineList); /* implicitly frees s->current */
+ free(s);
+ return 0;
+}
diff --git a/src/shvar.h b/src/shvar.h
new file mode 100644
index 00000000..ee60e12b
--- /dev/null
+++ b/src/shvar.h
@@ -0,0 +1,100 @@
+/* copied from rp3 -- DO NOT EDIT HERE, ONLY COPY FROM rp3 */
+/*
+ * shvar.h
+ *
+ * Interface for 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 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef _SHVAR_H
+#define _SHVAR_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _shvarFile shvarFile;
+struct _shvarFile {
+ char *fileName; /* read-only */
+ int fd; /* read-only */
+ char *arena; /* ignore */
+ GList *lineList; /* read-only */
+ GList *freeList; /* ignore */
+ GList *current; /* set implicitly or explicitly,
+ points to element of lineList */
+ shvarFile *parent; /* set explicitly */
+ int modified; /* ignore */
+};
+
+
+/* Open the file <name>, return shvarFile on success, NULL on failure */
+shvarFile *
+svNewFile(char *name);
+
+/* Get the value associated with the key, and leave the current pointer
+ * pointing at the line containing the value. The char* returned MUST
+ * be freed by the caller.
+ */
+char *
+svGetValue(shvarFile *s, char *key);
+
+/* 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 <def> otherwise
+ */
+int
+svTrueValue(shvarFile *s, char *key, int def);
+
+/* 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.
+ */
+void
+svSetValue(shvarFile *s, char *key, char *value);
+
+
+/* Write the current contents iff modified. Returns -1 on error
+ * and 0 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);
+
+/* Close the file descriptor (if open) and delete the shvarFile.
+ * Returns -1 on error and 0 on success.
+ */
+int
+svCloseFile(shvarFile *s);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ! _SHVAR_H */