From 8b70aa21da96d9dcad641c3fe2787307d5610036 Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Mon, 28 Oct 2019 19:21:44 -0400 Subject: Add option to save and load ring contents across restarts --- bounce.c | 27 +++++++++++++++++++++- bounce.h | 3 +++ pounce.1 | 10 +++++++++ ring.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/bounce.c b/bounce.c index 65789ab..4e7458c 100644 --- a/bounce.c +++ b/bounce.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -88,11 +89,20 @@ static char *sensitive(char *arg) { return value; } +static FILE *saveFile; +static void exitSave(void) { + int error = ringSave(saveFile); + if (error) warn("fwrite"); + error = fclose(saveFile); + if (error) warn("fclose"); +} + int main(int argc, char *argv[]) { const char *localHost = "localhost"; const char *localPort = "6697"; char certPath[PATH_MAX] = ""; char privPath[PATH_MAX] = ""; + const char *save = NULL; bool insecure = false; const char *host = NULL; @@ -107,7 +117,8 @@ int main(int argc, char *argv[]) { const char *quit = "connection reset by purr"; int opt; - while (0 < (opt = getopt(argc, argv, "!A:C:H:K:NP:Q:W:a:h:j:n:p:r:u:vw:"))) { + const char *opts = "!A:C:H:K:NP:Q:W:a:f:h:j:n:p:r:u:vw:"; + while (0 < (opt = getopt(argc, argv, opts))) { switch (opt) { break; case '!': insecure = true; break; case 'A': away = optarg; @@ -119,6 +130,7 @@ int main(int argc, char *argv[]) { break; case 'Q': quit = optarg; break; case 'W': clientPass = sensitive(optarg); break; case 'a': auth = sensitive(optarg); + break; case 'f': save = optarg; break; case 'h': host = optarg; break; case 'j': join = optarg; break; case 'n': nick = optarg; @@ -146,6 +158,19 @@ int main(int argc, char *argv[]) { if (!user) user = nick; if (!real) real = nick; + if (save) { + umask(0066); + saveFile = fopen(save, "a+"); + if (!saveFile) err(EX_CANTCREAT, "%s", save); + + rewind(saveFile); + ringLoad(saveFile); + + int error = ftruncate(fileno(saveFile), 0); + if (error) err(EX_IOERR, "ftruncate"); + atexit(exitSave); + } + listenConfig(certPath, privPath); int bind[8]; diff --git a/bounce.h b/bounce.h index 9221185..6e9ddbd 100644 --- a/bounce.h +++ b/bounce.h @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -64,6 +65,8 @@ size_t ringDiff(size_t consumer); const char *ringPeek(time_t *time, size_t consumer); const char *ringConsume(time_t *time, size_t consumer); void ringInfo(void); +int ringSave(FILE *file); +void ringLoad(FILE *file); void listenConfig(const char *cert, const char *priv); size_t listenBind(int fds[], size_t cap, const char *host, const char *port); diff --git a/pounce.1 b/pounce.1 index 41555ed..632ca3e 100644 --- a/pounce.1 +++ b/pounce.1 @@ -17,6 +17,7 @@ .Op Fl Q Ar quit .Op Fl W Ar pass .Op Fl a Ar auth +.Op Fl f Ar file .Op Fl h Ar host .Op Fl j Ar chan .Op Fl n Ar nick @@ -108,6 +109,15 @@ Set to the first line read from .Ar file . . +.It Fl f Ar file +Load the contents of the ring buffer from +.Ar file +if it exists. +The file is then truncated. +On shutdown, +save the contents of the ring buffer to +.Ar file . +. .It Fl h Ar host Connect to .Ar host . diff --git a/ring.c b/ring.c index 49fdd9b..474a6e4 100644 --- a/ring.c +++ b/ring.c @@ -103,3 +103,81 @@ void ringInfo(void) { ); } } + +static const size_t FileVersion = 0x0165636E756F70; + +static int writeSize(FILE *file, size_t value) { + return (fwrite(&value, sizeof(value), 1, file) ? 0 : -1); +} +static int writeTime(FILE *file, time_t time) { + return (fwrite(&time, sizeof(time), 1, file) ? 0 : -1); +} +static int writeString(FILE *file, const char *str) { + return (fwrite(str, strlen(str) + 1, 1, file) ? 0 : -1); +} + +int ringSave(FILE *file) { + if (writeSize(file, FileVersion)) return -1; + if (writeSize(file, producer)) return -1; + if (writeSize(file, consumers.len)) return -1; + for (size_t i = 0; i < consumers.len; ++i) { + if (writeString(file, consumers.ptr[i].name)) return -1; + if (writeSize(file, consumers.ptr[i].pos)) return -1; + } + for (size_t i = 0; i < RingLen; ++i) { + if (writeTime(file, ring.times[i])) return -1; + } + for (size_t i = 0; i < RingLen; ++i) { + if (!ring.lines[i]) break; + if (writeString(file, ring.lines[i])) return -1; + } + return 0; +} + +static void readSize(FILE *file, size_t *value) { + fread(value, sizeof(*value), 1, file); + if (ferror(file)) err(EX_IOERR, "fread"); + if (feof(file)) err(EX_DATAERR, "unexpected eof"); +} +static void readTime(FILE *file, time_t *time) { + fread(time, sizeof(*time), 1, file); + if (ferror(file)) err(EX_IOERR, "fread"); + if (feof(file)) err(EX_DATAERR, "unexpected eof"); +} +static void readString(FILE *file, char **buf, size_t *cap) { + ssize_t len = getdelim(buf, cap, '\0', file); + if (len < 0 && !feof(file)) err(EX_IOERR, "getdelim"); +} + +void ringLoad(FILE *file) { + size_t version; + fread(&version, sizeof(version), 1, file); + if (ferror(file)) err(EX_IOERR, "fread"); + if (feof(file)) return; + + if (version != FileVersion) errx(EX_DATAERR, "unknown file version"); + readSize(file, &producer); + + char *buf = NULL; + size_t cap = 0; + + size_t len; + readSize(file, &len); + for (size_t i = 0; i < len; ++i) { + readString(file, &buf, &cap); + size_t consumer = ringConsumer(buf); + readSize(file, &consumers.ptr[consumer].pos); + } + + for (size_t i = 0; i < RingLen; ++i) { + readTime(file, &ring.times[i]); + } + for (size_t i = 0; i < RingLen; ++i) { + readString(file, &buf, &cap); + if (feof(file)) break; + ring.lines[i] = strdup(buf); + if (!ring.lines[i]) err(EX_OSERR, "strdup"); + } + + free(buf); +} -- cgit 1.4.1