aboutsummaryrefslogtreecommitdiff
path: root/src/variable.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2015-02-10 16:48:15 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2015-02-10 16:48:15 +0200
commitb771edaa218bee8a8365cc8e4ee9af4005ad61b2 (patch)
treeb7042536b1517282b786a2d264a52d858305ba86 /src/variable.c
downloadvmod-variable-b771edaa218bee8a8365cc8e4ee9af4005ad61b2.tar.gz
vmod-variable-b771edaa218bee8a8365cc8e4ee9af4005ad61b2.tar.bz2
Initial commit
Diffstat (limited to 'src/variable.c')
-rw-r--r--src/variable.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/src/variable.c b/src/variable.c
new file mode 100644
index 0000000..c8ab8e6
--- /dev/null
+++ b/src/variable.c
@@ -0,0 +1,688 @@
+/* This file is part of vmod-tbf
+ Copyright (C) 2013-2015 Sergey Poznyakoff
+
+ Vmod-tbf 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 3, or (at your option)
+ any later version.
+
+ Vmod-tbf 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 vmod-tbf. If not, see <http://www.gnu.org/licenses/>.
+*/
+#include <config.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <pcre.h>
+#include "vrt.h"
+#include "vcc_if.h"
+#include "pthread.h"
+#if VARNISHVERSION == 3
+# include "bin/varnishd/cache.h"
+# define VCL_VOID void
+# define VCL_STRING const char *
+# define VCL_REAL double
+# define VCL_DURATION double
+# define WS_Copy(w,s,l) WS_Dup(w,s)
+# define VARIABLE_CTX struct sess *
+# define WSPTR(s) ((s)->wrk->ws)
+#else
+# include "bin/varnishd/cache/cache.h"
+# define VARIABLE_CTX const struct vrt_ctx *
+# define WSPTR(s) ((s)->ws)
+#endif
+
+/* |hash_size| defines a sequence of symbol table sizes. These are prime
+ numbers, each of which is approximately twice its predecessor. */
+
+static unsigned int hash_size[] = {
+ 7, 17, 37, 101, 229, 487, 1009, 2039, 4091, 8191, 16411
+};
+
+/* max_rehash keeps the number of entries in |hash_size| table. */
+static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]);
+
+enum variable_type {
+ variable_unset,
+ variable_string,
+ variable_int,
+ variable_real,
+ variable_duration
+};
+
+static char *typestr[] = {
+ "UNSET",
+ "STRING",
+ "INT",
+ "REAL",
+ "DURATION"
+};
+
+union value {
+ char *s;
+ int i;
+ double r;
+ double d;
+};
+
+struct variable {
+ char *name;
+ enum variable_type type;
+ union value v;
+};
+
+struct symtab {
+ uint32_t vxid;
+ unsigned int hash_num; /* Index to hash_size table */
+ struct variable **tab;
+};
+
+static struct variable *
+var_alloc(const char *name)
+{
+ struct variable *ent;
+
+ ent = malloc(sizeof(struct variable));
+ AN(ent);
+ ent->name = strdup(name);
+ ent->type = variable_unset;
+ AN(ent->name);
+ return ent;
+}
+
+static unsigned
+hash_string(const char *name, unsigned long hashsize)
+{
+ unsigned i;
+
+ for (i = 0; *name; name++) {
+ i <<= 1;
+ i ^= *(unsigned char*) name;
+ }
+ return i % hashsize;
+}
+
+static unsigned
+var_hash(struct variable *var, unsigned long hashsize)
+{
+ return hash_string(var->name, hashsize);
+}
+
+static void
+var_free(struct variable *var)
+{
+ free(var->name);
+ if (var->type == variable_string)
+ free(var->v.s);
+ free(var);
+}
+
+static unsigned
+symtab_insert_pos(struct symtab *st, struct variable *elt)
+{
+ unsigned i;
+ unsigned pos = var_hash(elt, hash_size[st->hash_num]);
+
+ for (i = pos; st->tab[i];) {
+ if (++i >= hash_size[st->hash_num])
+ i = 0;
+ if (i == pos)
+ abort();
+ }
+ return i;
+}
+
+static int
+symtab_rehash(struct symtab *st)
+{
+ struct variable **old_tab = st->tab;
+ struct variable **new_tab;
+ unsigned int i;
+ unsigned int hash_num = st->hash_num + 1;
+
+ if (hash_num >= max_rehash)
+ return E2BIG;
+
+ new_tab = calloc(hash_size[hash_num], sizeof(*new_tab));
+ AN(new_tab);
+ st->tab = new_tab;
+ if (old_tab) {
+ st->hash_num = hash_num;
+ for (i = 0; i < hash_size[hash_num-1]; i++) {
+ struct variable *elt = old_tab[i];
+ if (elt->name) {
+ unsigned n = symtab_insert_pos(st, elt);
+ new_tab[n] = elt;
+ }
+ }
+ free(old_tab);
+ }
+ return 0;
+}
+
+static int
+symtab_remove(struct symtab *st, const char *name)
+{
+ unsigned int pos, i, j, r;
+ struct variable *entry;
+
+ pos = hash_string(name, hash_size[st->hash_num]);
+ for (i = pos; (entry = st->tab[i]);) {
+ if (strcmp(entry->name, name) == 0)
+ break;
+ if (++i >= hash_size[st->hash_num])
+ i = 0;
+ if (i == pos)
+ return ENOENT;
+ }
+
+ var_free(entry);
+
+ for (;;) {
+ st->tab[i] = NULL;
+ j = i;
+
+ do {
+ if (++i >= hash_size[st->hash_num])
+ i = 0;
+ if (!st->tab[i])
+ return 0;
+ r = hash_string(st->tab[i]->name, hash_size[st->hash_num]);
+ }
+ while ((j < r && r <= i)
+ || (i < j && j < r) || (r <= i && i < j));
+ st->tab[j] = st->tab[i];
+ }
+ return 0;
+}
+
+static int
+symtab_get_index(unsigned *idx, struct symtab *st,
+ const char *name, int *install)
+{
+ int rc;
+ unsigned i, pos;
+ struct variable *elem;
+
+ if (!st->tab) {
+ if (install) {
+ rc = symtab_rehash(st);
+ if (rc)
+ return rc;
+ } else
+ return ENOENT;
+ }
+
+ pos = hash_string(name, hash_size[st->hash_num]);
+
+ for (i = pos; (elem = st->tab[i]);) {
+ if (strcmp(elem->name, name) == 0) {
+ if (install)
+ *install = 0;
+ *idx = i;
+ return 0;
+ }
+
+ if (++i >= hash_size[st->hash_num])
+ i = 0;
+ if (i == pos)
+ break;
+ }
+
+ if (!install)
+ return ENOENT;
+
+ if (!elem) {
+ *install = 1;
+ *idx = i;
+ return 0;
+ }
+
+ if ((rc = symtab_rehash(st)) != 0)
+ return rc;
+
+ return symtab_get_index(idx, st, name, install);
+}
+
+static struct variable *
+symtab_lookup_or_install(struct symtab *st, const char *name, int *install)
+{
+ unsigned i;
+ int rc = symtab_get_index(&i, st, name, install);
+ if (rc == 0) {
+ if (install && *install == 1) {
+ struct variable *ent = var_alloc(name);
+ if (!ent) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ st->tab[i] = ent;
+ return ent;
+ } else
+ return st->tab[i];
+ }
+ errno = rc;
+ return NULL;
+}
+
+static void
+symtab_clear(struct symtab *st)
+{
+ unsigned i, hs;
+
+ if (!st || !st->tab)
+ return;
+
+ hs = hash_size[st->hash_num];
+ for (i = 0; i < hs; i++) {
+ struct variable *elem = st->tab[i];
+ if (elem) {
+ var_free(elem);
+ st->tab[i] = NULL;
+ }
+ }
+}
+
+static struct symtab *
+symtab_create()
+{
+ struct symtab *st = calloc(1, sizeof(*st));
+ AN(st);
+ st->tab = calloc(hash_size[st->hash_num], sizeof(*st->tab));
+ AN(st->tab);
+ return st;
+}
+
+static void
+symtab_free(struct symtab *st)
+{
+ if (st) {
+ symtab_clear(st);
+ free(st->tab);
+ free(st);
+ }
+}
+
+
+static struct symtab **symtabv;
+static size_t symtabc;
+static pthread_mutex_t symtab_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+static struct symtab *
+get_symtab(VARIABLE_CTX ctx)
+{
+ struct symtab *st;
+ int fd = ctx->req->sp->fd;
+ AZ(pthread_mutex_lock(&symtab_mtx));
+ if (symtabc <= fd) {
+ size_t n = fd + 1;
+ symtabv = realloc(symtabv, n * sizeof(symtabv[0]));
+ while (symtabc < n)
+ symtabv[symtabc++] = NULL;
+ }
+ if (!symtabv[fd])
+ symtabv[fd] = symtab_create();
+ st = symtabv[fd];
+ if (st->vxid != ctx->req->sp->vxid)
+ symtab_clear(st);
+ st->vxid = ctx->req->sp->vxid;
+ AZ(pthread_mutex_unlock(&symtab_mtx));
+ return st;
+}
+
+#define getvar(vt, name) symtab_lookup_or_install(vt, name, NULL)
+
+struct variable *
+defvar(struct symtab *vt, const char *name, enum variable_type t,
+ union value *val)
+{
+ int inst = 1;
+ struct variable *var = symtab_lookup_or_install(vt, name, &inst);
+ if (!inst && var->type == variable_string)
+ free(var->v.s);
+ if (t != variable_unset && val) {
+ var->type = t;
+ var->v = *val;
+ } else
+ var->type = variable_unset;
+ return var;
+}
+
+VCL_VOID
+vmod_clear(VARIABLE_CTX ctx)
+{
+ symtab_clear(get_symtab(ctx));
+}
+
+VCL_STRING
+vmod_get_string(VARIABLE_CTX ctx, VCL_STRING name)
+{
+ struct variable *var = getvar(get_symtab(ctx), name);
+ if (var && var->type == variable_string)
+ return var->v.s;
+ return NULL;
+}
+
+VCL_VOID
+vmod_set_string(VARIABLE_CTX ctx, VCL_STRING name, VCL_STRING value)
+{
+ struct symtab *vt = get_symtab(ctx);
+ struct variable *var = defvar(vt, name, variable_unset, NULL);
+ var->type = variable_string;
+ var->v.s = strdup(value ? value : "");
+ AN(var->v.s);
+}
+
+VCL_STRING
+vmod_get(VARIABLE_CTX ctx, VCL_STRING name)
+{
+ return vmod_get_string(ctx, name);
+}
+
+VCL_VOID
+vmod_set(VARIABLE_CTX ctx, VCL_STRING name, VCL_STRING value)
+{
+ vmod_set_string(ctx, name, value);
+}
+
+#define __cat__(a,b) a ## b
+#define DEFGET(r_type, vcl_type, memb) \
+vcl_type \
+__cat__(vmod_get_,r_type)(VARIABLE_CTX ctx, VCL_STRING name) \
+{ \
+ struct variable *var = getvar(get_symtab(ctx), name); \
+ if (var && var->type == __cat__(variable_,r_type)) \
+ return var->v.memb; \
+ return 0; \
+}
+
+#define DEFSET(r_type, vcl_type, memb) \
+VCL_VOID \
+__cat__(vmod_set_,r_type)(VARIABLE_CTX ctx, VCL_STRING name, \
+ vcl_type value) \
+{ \
+ struct symtab *vt = get_symtab(ctx); \
+ struct variable *var = defvar(vt, name, variable_unset, NULL); \
+ var->type = __cat__(variable_,r_type); \
+ var->v.memb = value; \
+}
+
+#define DEF(name, vcl_type, memb) \
+DEFGET(name, vcl_type, memb) \
+DEFSET(name, vcl_type, memb)
+
+DEF(int, VCL_INT, i)
+DEF(real, VCL_REAL, r)
+DEF(duration, VCL_DURATION, d)
+
+VCL_INT
+vmod_defined(VARIABLE_CTX ctx, VCL_STRING name)
+{
+ return !!getvar(get_symtab(ctx), name);
+}
+
+VCL_STRING
+vmod_type_of(VARIABLE_CTX ctx, VCL_STRING name)
+{
+ struct variable *var = getvar(get_symtab(ctx), name);
+ return typestr[var ? var->type : variable_unset];
+}
+
+VCL_VOID
+vmod_undef(VARIABLE_CTX ctx, VCL_STRING name)
+{
+ symtab_remove(get_symtab(ctx), name);
+}
+
+void
+log_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_DAEMON|LOG_NOTICE, fmt, ap);
+ va_end(ap);
+}
+
+#define S(s) ((s) ? (s) : "NULL")
+
+struct vardef {
+ struct vardef *next;
+ enum variable_type type;
+ char name[1];
+};
+
+static void
+vardef_free(struct vardef *v)
+{
+ while (v) {
+ struct vardef *n = v->next;
+ free(v);
+ v = n;
+ }
+}
+
+static enum variable_type
+str2type(const char *s, size_t len)
+{
+ int i;
+
+ for (i = 0; i < sizeof(typestr)/sizeof(typestr[0]); i++) {
+ if (strlen(typestr[i]) < len)
+ continue;
+ if (strncasecmp(typestr[i], s, len) == 0)
+ return i;
+ }
+ return variable_unset;
+}
+
+static double
+str2duration(const char *str)
+{
+ char *p;
+ double r;
+
+ errno = 0;
+ r = strtod(str, &p);
+ if (errno)
+ return 0.0;
+ while (*p && isspace(*p))
+ p++;
+
+ switch (*p++) {
+ case 's':
+ break;
+ case 'm':
+ if (*p == 's') {
+ r *= 1e-3;
+ p++;
+ } else
+ r *= 60.;
+ break;
+ case 'h':
+ r *= 60.*60.;
+ break;
+ case 'd':
+ r *= 60.*60.*24.;
+ break;
+ case 'w':
+ r *= 60.*60.*24.*7.;
+ break;
+ case 'y':
+ r *= 60.*60.*24.*365.;
+ break;
+ default:
+ return 0.0;
+ }
+ while (*p && isspace(*p))
+ p++;
+ return *p ? 0 : r;
+}
+
+VCL_VOID
+vmod_batchset(VARIABLE_CTX ctx,
+ VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
+{
+ struct symtab *vt = get_symtab(ctx);
+ struct vardef *head = NULL, *tail = NULL, *def;
+ size_t count = 0;
+ size_t n;
+ const char *v = vars;
+ const char *error_ptr;
+ int error_offset;
+ int cflags = 0;
+ long lval;
+ union value value;
+ char *p;
+ int ovsize;
+ int *ovector;
+ int i;
+ int rc;
+ pcre *re;
+
+ if (!vars || !rxs || !input) {
+ log_error("variable.batchset: bad arguments: vars=%s, rxs=%s, input=%s",
+ S(vars), S(rxs), S(input));
+ return;
+ }
+
+ while (*v) {
+ size_t n = strcspn(v, "=");
+
+ if (!v[n]) {
+ log_error("variable.batchset: vars argument invalid near %s",
+ v);
+ vardef_free(head);
+ return;
+ }
+ def = malloc(sizeof(def[0]) + n);
+ def->next = NULL;
+ memcpy(def->name, v, n);
+ def->name[n] = 0;
+ if (tail)
+ tail->next = def;
+ else
+ head = def;
+ tail = def;
+ ++count;
+
+ v += n + 1;
+ if (!*v) {
+ log_error("variable.batchset: no type for %s", def->name);
+ vardef_free(head);
+ return;
+ }
+
+ n = strcspn(v, ",");
+ def->type = str2type(v, n);
+ if (def->type == variable_unset) {
+ log_error("variable.batchset: invalid type for %s",
+ def->name);
+ vardef_free(head);
+ return;
+ }
+
+ v += n;
+ if (*v)
+ ++v;
+ }
+
+ re = pcre_compile(rxs, cflags, &error_ptr, &error_offset, NULL);
+ if (!re) {
+ log_error("variable.batchset: %s: compilation failed near %s: %s",
+ rxs, rxs + error_offset, error_ptr);
+ vardef_free(head);
+ return;
+ }
+
+ rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &n);
+ if (rc) {
+ log_error("pcre_fullinfo() failed: %d", rc);
+ vardef_free(head);
+ return;
+ }
+
+ if (n < count) {
+ log_error("variable.batchset: %s: too few subexpressions to satisfy %s",
+ rxs, vars);
+ vardef_free(head);
+ return;
+ }
+
+ ovsize = (count + 1) * 3;
+ ovector = calloc(ovsize, sizeof(*ovector));
+
+ rc = pcre_exec(re, 0, input, strlen(input), 0, 0, ovector, ovsize);
+ if (rc <= 0) {
+ if (rc == 0)
+ log_error("matched, but too many substrings");
+ else
+ /*FIXME*/;
+ vardef_free(head);
+ return;
+ }
+
+ for (def = head, i = 1; def; def = def->next, i++) {
+ const char *s;
+
+ rc = pcre_get_substring(input, ovector, ovsize, i, &s);
+ if (rc < 0) {
+ log_error("variable.batchset: %s(%s) can't get subexpr #%d: %d",
+ rxs, input, i, rc);
+ vardef_free(head);
+ return;
+ }
+
+ switch (def->type) {
+ case variable_string:
+ value.s = (char*)s;
+ break;
+ case variable_int:
+ errno = 0;
+ lval = strtol(s, &p, 10);
+ if (*p) {
+ log_error("variable.batchset: %s(%s)#%d: not an integer", rxs, input, i);
+ value.i = 0;
+ } else if (errno) {
+ log_error("variable.batchset: %s(%s)#%d: %s",
+ rxs, input, i, strerror(errno));
+ value.i = 0;
+ } else if (lval < INT_MIN || lval > INT_MAX) {
+ log_error("variable.batchset: %s(%s)#%d: value out of range",
+ rxs, input, i);
+ value.i = 0;
+ } else
+ value.i = lval;
+ break;
+ case variable_real:
+ errno = 0;
+ value.r = strtod(s, &p);
+ if (*p) {
+ log_error("variable.batchset: %s(%s)#%d: not a valid number", rxs, input, i);
+ value.r = 0;
+ } else if (errno) {
+ log_error("variable.batchset: %s(%s)#%d: %s",
+ rxs, input, i, strerror(errno));
+ value.r = 0;
+ }
+ break;
+
+ case variable_duration:
+ value.d = str2duration(s);
+ break;
+ default:
+ abort();
+ }
+ defvar(vt, def->name, def->type, &value);
+ }
+
+ pcre_free(re);
+ free(ovector);
+ vardef_free(head);
+}

Return to:

Send suggestions and report system problems to the System administrator.