summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/dynstorage.c86
-rw-r--r--src/server/dynstorage.h11
-rw-r--r--src/server/s6-rcd.h13
-rw-r--r--src/server/state.c135
-rw-r--r--src/server/state.h12
5 files changed, 216 insertions, 41 deletions
diff --git a/src/server/dynstorage.c b/src/server/dynstorage.c
new file mode 100644
index 0000000..c17e8e6
--- /dev/null
+++ b/src/server/dynstorage.c
@@ -0,0 +1,86 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <skalibs/gensetdyn.h>
+#include <skalibs/avltree.h>
+
+#include "dynstorage.h"
+
+/*
+ This is probably way overengineered, we could strdup the instance parameters
+ into the instances themselves; there would be a little param duplication, but
+ likely not much, and chances are we would save memory.
+ However, a fundamental principle of C memory management is: separate structure
+ from storage. Failing to abide by it leads to spaghettification of storage
+ paths, and ultimately memory leaks.
+ So, we store all our dynamic strings (i.e. user-provided strings that only
+ appear in transient storage like buffers but that need a longer lifetime) here,
+ and nowhere else. Duplication is prevented via refcounting.
+*/
+
+typedef struct cell_s cell_t, *cell_t_ref ;
+struct cell_s
+{
+ char *s ;
+ uint32_t refcount ;
+} ;
+#define CELL_ZERO { .s = 0, .refcount = 0 } ;
+
+static gensetdyn cells = GENSETDYN_INIT(cell_t, 3, 3, 8) ;
+
+static void *dynstorage_dtok (uint32_t d, void *data)
+{
+ return GENSETDYN_P(cell_t, (gensetdyn *)data, d)->s ;
+}
+
+static int dynstorage_cmp (void const *a, void const *b, void *data)
+{
+ (void)data ;
+ return strcmp((char const *)a, (char const *)b) ;
+}
+
+static avltree dynstorage = AVLTREE_INIT(3, 3, 8, &dynstorage_dtok, &dynstorage_cmp, &cells) ;
+
+char const *dynstorage_add (char const *s)
+{
+ cell_t *p ;
+ uint32_t d ;
+ if (avltree_search(&dynstorage, s, &d))
+ {
+ p = GENSETDYN_P(cell_t, &cells, d) ;
+ p->refcount++ ;
+ return p->s ;
+ }
+
+ if (!gensetdyn_new(&cells, &d)) return 0 ;
+ p = GENSETDYN_P(cell_t, &cells, d) ;
+ p->s = strdup(s) ;
+ if (!p->s) goto err0 ;
+ p->refcount = 1 ;
+ if (!avltree_insert(&dynstorage, d)) goto err1 ;
+ return p->s ;
+
+ err1:
+ free(p->s) ;
+ err0:
+ gensetdyn_delete(&cells, d) ;
+ return 0 ;
+}
+
+void dynstorage_remove (char const *s)
+{
+ uint32_t d ;
+ if (avltree_search(&dynstorage, s, &d)
+ {
+ cell_t *p = GENSETDYN_P(cell_t, &cells, d) ;
+ if (!--p->refcount)
+ {
+ avltree_delete(&dynstorage, s) ;
+ free(p->s) ;
+ gensetdyn_delete(&cells, d) ;
+ }
+ }
+}
diff --git a/src/server/dynstorage.h b/src/server/dynstorage.h
new file mode 100644
index 0000000..b935cc9
--- /dev/null
+++ b/src/server/dynstorage.h
@@ -0,0 +1,11 @@
+/* ISC license. */
+
+#ifndef S6RCD_DYNSTORAGE_H
+#define S6RCD_DYNSTORAGE_H
+
+ /* Storage for dynamic strings such as instance parameters */
+
+extern char const *dynstorage_add (char const *) ;
+extern void dynstorage_remove (char const *) ;
+
+#endif
diff --git a/src/server/s6-rcd.h b/src/server/s6-rcd.h
index 2436c54..ca0867a 100644
--- a/src/server/s6-rcd.h
+++ b/src/server/s6-rcd.h
@@ -3,15 +3,16 @@
#ifndef S6RCD_H
#define S6RCD_H
-#include "ep.h"
-#include "ev.h"
-#include "transition.h"
-#include "clientrules.h"
#include "client.h"
+#include "clientrules.h"
#include "command.h"
-#include "livedir.h"
#include "db.h"
-#include "signals.h"
+#include "dynstorage.h"
+#include "ep.h"
+#include "ev.h"
+#include "livedir.h"
#include "main.h"
+#include "signals.h"
+#include "state.h"
#endif
diff --git a/src/server/state.c b/src/server/state.c
index 0e2afe3..c6fb2d0 100644
--- a/src/server/state.c
+++ b/src/server/state.c
@@ -11,32 +11,53 @@
#include <skalibs/djbunix.h>
#include <s6-rc/db.h>
+#include "dynstorage.h"
#include "state.h"
+static stateatom_t const stateatom_zero = STATEATOM_ZERO ;
+
+void instance_free (instance_t *ins)
+{
+ dynstorage_remove(ins->param) ;
+}
+
+int instance_new (instance_t *ins, stateatom_t const *state, char const *param) ;
+{
+ char const *s = dynstorage_add(param) ;
+ if (!s) return 0 ;
+ ins->state = state ;
+ inst->param = s ;
+ return 1 ;
+}
+
void state_free (state_t *st, uint32_t const *dbn)
{
for (s6rc_stype_t type = S6RC_STYPE_LONGRUN ; type < S6RC_STYPE_PHAIL ; type++)
{
alloc_free(st->sta[type]) ;
for (size_t i = 0 ; i < dbn[S6RC_STYPE_PHAIL + type] ; i++)
+ {
+ for (size_t j = 0 ; j < genalloc_len(instance_t, st->dyn[type] + i) ; j++)
+ instance_free(genalloc_s(instance_t, st->dyn[type] + i) + j) ;
genalloc_free(instance_t, st->dyn[type] + i) ;
+ }
alloc_free(st->dyn[type]) ;
}
}
-int state_ready (state_t *st, uint32_t const *dbn)
+int state_init (state_t *st, uint32_t const *dbn)
{
s6rc_stype_t type = 0 ;
for (; type < S6RC_STYPE_PHAIL ; type++)
{
- genalloc *q ;
- stateatom_t *p = alloc(sizeof(stateatom_t) * dbn[type]) ;
- if (!p) goto err ;
- q = alloc(sizeof(genalloc) * dbn[S6RC_STYPE_PHAIL + type]) ;
- if (!q) { alloc_free(p) ; goto err ; }
- st->sta[type] = p ;
- for (size_t i = 0 ; i < dbn[S6RC_STYPE_PHAIL + type] ; i++) q[i] = genalloc_zero ;
- st->dyn[type] = q ;
+ st->sta[type] = alloc(sizeof(stateatom_t) * dbn[type]) ;
+ if (!st->sta[type]) goto err ;
+ st->dyn[type] = alloc(sizeof(genalloc) * dbn[S6RC_STYPE_PHAIL + type]) ;
+ if (!st->dyn[type]) { alloc_free(st->sta[type]) ; goto err ; }
+ for (uint32_t i = 0 ; i < dbn[type] ; i++)
+ st->sta[type][i] = stateatom_zero ;
+ for (size_t i = 0 ; i < dbn[S6RC_STYPE_PHAIL + type] ; i++)
+ st->dyn[type][i] = genalloc_zero ;
}
return 1 ;
@@ -49,19 +70,6 @@ int state_ready (state_t *st, uint32_t const *dbn)
return 0 ;
}
-stateatom_t *state_atom (state_t const *st, s6rc_sid_t const *id)
-{
- if (id->param)
- {
- size_t n = genalloc_len(instance_t, st->dyn[id->stype] + id->i) ;
- instance_t *instances = genalloc_s(instance_t, st->dyn[id->stype] + id->i) ;
- for (size_t i = 0 ; i < n ; i++)
- if (!strcmp(id->param, instances[i].param)) return &instances[i].state ;
- return 0 ;
- }
- else return st->sta[id->stype] + id->i ;
-}
-
static int atom_write (buffer *b, stateatom_t const *state)
{
char c = state->wanted | (state->current << 1) | (state->transitioning << 2) ;
@@ -82,15 +90,10 @@ int state_write (char const *file, state_t const *st, uint32_t const *dbn)
buffer_init(&b, &buffer_write, fd, buf, 1024) ;
for (s6rc_stype_t type = 0 ; type < S6RC_STYPE_PHAIL ; type++)
- {
- char pack[4] ;
- uint32_pack_big(pack, dbn[type]) ;
- if (buffer_put(&b, pack, 4) < 4) goto err ;
- }
- for (s6rc_stype_t type = 0 ; type < S6RC_STYPE_PHAIL ; type++)
- {
for (uint32_t i = 0 ; i < dbn[type] ; i++)
if (!atom_write(&b, st->sta[type] + i)) goto err ;
+
+ for (s6rc_stype_t type = 0 ; type < S6RC_STYPE_PHAIL ; type++)
for (uint32_t i = 0 ; i < dbn[S6RC_STYPE_PHAIL + type] ; i++)
{
uint32_t n = genalloc_len(instance_t, st->dyn[type] + i) ;
@@ -107,7 +110,6 @@ int state_write (char const *file, state_t const *st, uint32_t const *dbn)
if (buffer_put(&b, p[j].param, len+1) < len+1) goto err ;
}
}
- }
if (!buffer_flush(&b)) goto err ;
fd_close(fd) ;
@@ -120,3 +122,76 @@ int state_write (char const *file, state_t const *st, uint32_t const *dbn)
unlink_void(fn) ;
return 0 ;
}
+
+static int atom_read (buffer *b, stateatom_t *state)
+{
+ char c ;
+ if (buffer_get(b, &c, 1) < 1) return 0 ;
+ state->wanted = c & 1 ;
+ state->current = !!(c & 2) ;
+ state->transitioning = !!(c & 4) ;
+ return 1 ;
+}
+
+int state_read (char const *file, state_t *st, uint32_t const *dbn)
+{
+ int fd ;
+ buffer b ;
+ char buf[1024] ;
+ if (!state_init(st, dbn)) return 0 ;
+ fd = openc_read(file) ;
+ if (fd == -1) goto err0 ;
+ buffer_init(&b, &buffer_read, fd, buf, 1024) ;
+
+ for (s6rc_stype_t type = 0 ; type < S6RC_STYPE_PHAIL ; type++)
+ for (uint32_t i = 0 ; i < dbn[type] ; i++)
+ if (!atom_read(&b, st->sta[type] + i)) goto err ;
+
+ for (s6rc_stype_t type = 0 ; type < S6RC_STYPE_PHAIL ; type++)
+ for (uint32_t i = 0 ; i < dbn[S6RC_STYPE_PHAIL + type] ; i++)
+ {
+ uint32_t n ;
+ char pack[4] ;
+ if (buffer_get(&b, pack, 4) < 4) goto err ;
+ uint32_unpack_big(pack, &n) ;
+ if (!genalloc_ready(instance_t, st->dyn[type] + i, n)) goto err ;
+ for (uint32_t j = 0 ; i < n ; j++)
+ {
+ uint32_t len ;
+ instance_t *ins = genalloc_s(instance_t, st->dyn[type] + i) + j ;
+ if (!atom_read(&b, &ins->state)) goto err ;
+ if (buffer_get(&b, pack, 4) < 4) goto err ;
+ uint32_unpack_big(pack, &len) ;
+ {
+ char param[len + 1] ;
+ if (buffer_get(&b, param, len + 1) < len + 1) goto err ;
+ if (param[len]) { errno = EINVAL ; goto err ; }
+ ins->param = dynstorage_add(param) ;
+ if (!ins_param) goto err ;
+ }
+ }
+ genalloc_setlen(instance_t, st->dyn[type] + i, n) ;
+ }
+
+ fd_close(fd) ;
+ return 1 ;
+
+ err:
+ fd_close(fd) ;
+ err0:
+ state_free(st, dbn) ;
+ return 0 ;
+}
+
+stateatom_t *state_atom (state_t const *st, s6rc_sid_t const *id)
+{
+ if (id->param)
+ {
+ size_t n = genalloc_len(instance_t, st->dyn[id->stype] + id->i) ;
+ instance_t *instances = genalloc_s(instance_t, st->dyn[id->stype] + id->i) ;
+ for (size_t i = 0 ; i < n ; i++)
+ if (!strcmp(id->param, instances[i].param)) return &instances[i].state ;
+ return 0 ;
+ }
+ else return st->sta[id->stype] + id->i ;
+}
diff --git a/src/server/state.h b/src/server/state.h
index f0149bc..f49ffb4 100644
--- a/src/server/state.h
+++ b/src/server/state.h
@@ -17,11 +17,12 @@ struct stateatom_s
uint8_t current : 1 ;
uint8_t transitioning : 1 ;
} ;
+#define STATEATOM_ZERO { .wanted = 0, .current = 0, .transitioning = 0 }
typedef struct instance_s instance_t, *instance_t_ref ;
struct instance_s
{
- char const *param ;
+ char const *param ; /* refcounted pointer to dynstorage */
stateatom_t state ;
} ;
@@ -32,14 +33,15 @@ struct state_s
genalloc *dyn[S6RC_STYPE_PHAIL] ; /* every genalloc is a list of instance_t */
} ;
+extern void instance_free (instance_t *) ;
+extern int instance_new (instance_t *, stateatom_t const *, char const *) ;
+
extern void state_free (state_t *, uint32_t const *) ;
-extern int state_ready (state_t *, uint32_t const *) ;
+extern int state_init (state_t *, uint32_t const *) ;
extern int state_write (char const *, state_t const *, uint32_t const *) ;
-extern int state_read (char const *, state_t *, uint32_t const *) ;
-
+extern int state_read (char const *, state_t *, uint32_t const *) ; /* also inits */
extern stateatom_t *state_atom (s6rc_db_t const *, state_t const *, s6rc_sid_t const *) ;
-
#endif