From 57487e296e885786382588f547d259bec4c3595e Mon Sep 17 00:00:00 2001 From: "Michael K. Johnson" Date: Mon, 6 Sep 1999 22:42:05 +0000 Subject: first pass at ppp-watch --- src/Makefile | 12 +- src/ppp-watch.c | 420 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/shvar.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/shvar.h | 100 ++++++++++++++ 4 files changed, 908 insertions(+), 1 deletion(-) create mode 100644 src/ppp-watch.c create mode 100644 src/shvar.c create mode 100644 src/shvar.h 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ + 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-]"); + 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 +#include +#include +#include +#include +#include +#include +#include + +#include "shvar.h" + +/* Open the file , 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 resolves to any truth value (e.g. "yes", "y", "true") + * return 0 if resolves to any non-truth value (e.g. "no", "n", "false") + * return 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 equal to the value . + * If does not exist, and the 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 + +#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 , 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 resolves to any truth value (e.g. "yes", "y", "true") + * return 0 if resolves to any non-truth value (e.g. "no", "n", "false") + * return otherwise + */ +int +svTrueValue(shvarFile *s, char *key, int def); + +/* Set the variable equal to the value . + * If does not exist, and the 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 */ -- cgit v1.2.1