aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-08-01 19:20:54 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-08-02 10:48:54 +0300
commit73f605f76d82aec40a7f1bbd309e2097d9abfd42 (patch)
tree5820713d17efcb482a8801f4f805b0ae121903f7
parentd78a58be5fa2385987a3141ccbe942107332e677 (diff)
downloadvmod-tbf-73f605f76d82aec40a7f1bbd309e2097d9abfd42.tar.gz
vmod-tbf-73f605f76d82aec40a7f1bbd309e2097d9abfd42.tar.bz2
Switch to CDB environment.
* configure.ac: Version 0.99.91 * src/tbf.c (dbdir): New static. (tbf_set_db_name): Remove. (tbf_set_db_dir): New function. (tbf_open): Rename the parameter and related variables. Create a CDB environment and the database in it. (vmod_open): Change semantics of the first argument. * src/vmod-tbf.3: Update. * tests/Makefile.am (distclean-local): New rule, instead of DISTCLEANFILES. * tests/test00.vtc: Update call to tbf.open. * tests/test01.vtc: Likewise. * tests/test02.vtc: Likewise. * tests/test03.vtc: Likewise.
-rw-r--r--configure.ac2
-rw-r--r--src/tbf.c208
-rw-r--r--src/vmod-tbf.332
-rw-r--r--tests/Makefile.am4
-rw-r--r--tests/test00.vtc2
-rw-r--r--tests/test01.vtc2
-rw-r--r--tests/test02.vtc2
-rw-r--r--tests/test03.vtc2
8 files changed, 173 insertions, 81 deletions
diff --git a/configure.ac b/configure.ac
index 9c228f6..4ca4e6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,13 +11,13 @@
# 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/>.
AC_PREREQ(2.69)
-AC_INIT([vmod-tbf], 0.99.90, [gray@gnu.org])
+AC_INIT([vmod-tbf], 0.99.91, [gray@gnu.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/vmod_tbf.vcc)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
diff --git a/src/tbf.c b/src/tbf.c
index 1dfb6c6..7050fc7 100644
--- a/src/tbf.c
+++ b/src/tbf.c
@@ -17,12 +17,13 @@
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <syslog.h>
#include <inttypes.h>
+#include <sys/stat.h>
#include <db.h>
#include "vrt.h"
#include "vcc_if.h"
#include "bin/varnishd/cache.h"
static int debug_level;
@@ -38,20 +39,25 @@ debugprt(const char *fmt, ...)
#define debug(n,c) do { if (debug_level>=(n)) debugprt c; } while (0)
#ifndef USEC_PER_SEC
# define USEC_PER_SEC 1000000L
#endif
+#define DEFDBNAME "tbf.bdb"
+#define DEFOPENPARAMS "truncate"
+#define DBFILEMODE 0640
+
+static char *dbdir;
static char *dbname;
+static DB_ENV *dbenv;
static DB *db;
static uint64_t autosync_max;
static uint64_t autosync_count;
static int tbf_disabled;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-#define DBFILEMODE 0640
/* The keylock structure serializes accesses to each db record, ensuring
that no other thread could modify the data between calls to get and
put */
@@ -119,184 +125,258 @@ keylock_remove_safe(struct keylock *kp)
VTAILQ_REMOVE(&keylock_head, kp, list);
VTAILQ_INSERT_TAIL(&keylock_avail, kp, list);
pthread_mutex_unlock(&mutex);
}
static void
-tbf_set_db_name(const char *file_name)
+tbf_set_db_dir(const char *dir)
{
- if (dbname)
- free(dbname);
- dbname = strdup(file_name);
- if (!dbname)
- abort();
+ if (dbdir)
+ free(dbdir);
+ dbdir = strdup(dir);
+ AN(dbdir);
}
-struct mode_kw {
- char *mkw_str;
- int mkw_len;
- int mkw_tok;
+struct param_kw {
+ char *pkw_str;
+ int pkw_len;
+ int pkw_tok;
};
enum {
- MKW_TRUNCATE,
- MKW_MODE,
- MKW_SYNC,
- MKW_DEBUG,
+ PKW_TRUNCATE,
+ PKW_MODE,
+ PKW_SYNC,
+ PKW_DEBUG,
+ PKW_DBNAME
};
-static struct mode_kw mode_kw_tab[] = {
+static struct param_kw param_kw_tab[] = {
#define S(s) #s, sizeof(#s)-1
- { S(truncate), MKW_TRUNCATE },
- { S(trunc), MKW_TRUNCATE },
- { S(mode=), MKW_MODE },
- { S(sync=), MKW_SYNC },
- { S(debug=), MKW_DEBUG },
+ { S(truncate), PKW_TRUNCATE },
+ { S(trunc), PKW_TRUNCATE },
+ { S(mode=), PKW_MODE },
+ { S(sync=), PKW_SYNC },
+ { S(debug=), PKW_DEBUG },
+ { S(dbname=), PKW_DBNAME },
{ NULL }
#undef S
};
static void
-tbf_open(const char *mode)
+tbf_open(const char *params)
{
int rc;
- int flags = DB_CREATE|DB_THREAD;
int filemode = DBFILEMODE;
uint64_t n;
char *p;
+ struct stat st;
+ int truncate = 0;
- if (!dbname)
- tbf_set_db_name(LOCALSTATEDIR "/tbf.db");
-
- rc = db_create(&db, NULL, 0);
- if (rc) {
- syslog(LOG_DAEMON|LOG_ERR, "cannot create db struct");
- return;
+ if (!dbdir) {
+ dbdir = strdup(LOCALSTATEDIR "/vmod-tbf");
+ AN(dbdir);
+ }
+ if (!dbname) {
+ dbname = strdup(DEFDBNAME);
+ AN(dbname);
}
- while (*mode) {
- struct mode_kw *mkw;
+ while (*params) {
+ struct param_kw *pkw;
- for (mkw = mode_kw_tab; mkw->mkw_str; mkw++) {
- if (strncmp(mode, mkw->mkw_str, mkw->mkw_len) == 0)
+ for (pkw = param_kw_tab; pkw->pkw_str; pkw++) {
+ if (strncmp(params, pkw->pkw_str, pkw->pkw_len) == 0)
break;
}
- if (!mkw->mkw_str) {
- syslog(LOG_DAEMON|LOG_ERR, "invalid keyword %s", mode);
+ if (!pkw->pkw_str) {
+ syslog(LOG_DAEMON|LOG_ERR, "invalid keyword %s", params);
break;
}
- mode += mkw->mkw_len;
+ params += pkw->pkw_len;
- switch (mkw->mkw_tok) {
- case MKW_TRUNCATE:
- flags |= DB_TRUNCATE;
+ switch (pkw->pkw_tok) {
+ case PKW_TRUNCATE:
+ truncate = 1;
break;
- case MKW_MODE:
+ case PKW_MODE:
errno = 0;
- n = strtoul(mode, &p, 8);
+ n = strtoul(params, &p, 8);
if (errno || (n & ~0777) || !(*p == 0 || *p == ';')) {
syslog(LOG_DAEMON|LOG_ERR,
"invalid file mode near %s", p);
- mode += strlen(mode);
+ params += strlen(params);
} else {
filemode = n;
- mode = p;
+ params = p;
}
break;
- case MKW_SYNC:
+ case PKW_SYNC:
errno = 0;
- n = strtoul(mode, &p, 10);
+ n = strtoul(params, &p, 10);
if (errno || !(*p == 0 || *p == ';')) {
syslog(LOG_DAEMON|LOG_ERR,
"invalid count near %s", p);
- mode += strlen(mode);
+ params += strlen(params);
} else {
autosync_max = n;
autosync_count = 0;
- mode = p;
+ params = p;
}
break;
- case MKW_DEBUG:
+ case PKW_DEBUG:
errno = 0;
- n = strtoul(mode, &p, 10);
+ n = strtoul(params, &p, 10);
if (errno || !(*p == 0 || *p == ';')) {
syslog(LOG_DAEMON|LOG_ERR,
"invalid debug level near %s", p);
- mode += strlen(mode);
+ params += strlen(params);
} else {
debug_level = n;
- mode = p;
+ params = p;
}
+ break;
+
+ case PKW_DBNAME:
+ if (dbname)
+ free(dbname);
+ n = strcspn(params, ";");
+ dbname = malloc(n + 1);
+ AN(dbname);
+ memcpy(dbname, params, n);
+ dbname[n] = 0;
+ params += n;
+ break;
}
- if (*mode == 0)
+ if (*params == 0)
break;
- else if (*mode == ';')
- mode++;
+ else if (*params == ';')
+ params++;
else {
syslog(LOG_DAEMON|LOG_ERR,
- "expected ';' near %s", mode);
+ "expected ';' near %s", params);
break;
}
}
- debug(1, ("opening database %s", dbname));
- rc = db->open(db, NULL, dbname, NULL, DB_HASH, flags, filemode);
+ debug(1, ("opening database %s/%s", dbdir, dbname));
+
+ if (rc = db_env_create(&dbenv, 0)) {
+ syslog(LOG_DAEMON|LOG_ERR, "cannot create db environment: %s",
+ db_strerror(rc));
+ return;
+ }
+
+ if (stat(dbdir, &st)) {
+ if (errno == ENOENT) {
+ if (mkdir(dbdir,
+ filemode | 0100 |
+ ((filemode & 0060) ? 0010 : 0) |
+ ((filemode & 0006) ? 0001 : 0))) {
+ syslog(LOG_DAEMON|LOG_ERR,
+ "cannot create db environment directory %s: %m",
+ dbdir);
+ }
+ } else {
+ syslog(LOG_DAEMON|LOG_ERR,
+ "cannot stat db environment directory %s: %m",
+ dbdir);
+ return;
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ syslog(LOG_DAEMON|LOG_ERR, "%s is not a directory",
+ dbdir);
+ return;
+ }
+
+ rc = dbenv->open(dbenv, dbdir,
+ DB_THREAD | DB_CREATE | DB_INIT_MPOOL | DB_INIT_CDB,
+ 0);
+ if (rc) {
+ syslog(LOG_DAEMON|LOG_ERR, "cannot open db environment %s: %s",
+ dbdir, db_strerror(rc));
+ tbf_disabled = 1;
+ return;
+ }
+
+ rc = db_create(&db, dbenv, 0);
if (rc) {
- syslog(LOG_DAEMON|LOG_ERR, "cannot open %s: %s",
+ syslog(LOG_DAEMON|LOG_ERR, "cannot create db struct");
+ return;
+ }
+
+ rc = db->open(db, NULL, dbname, NULL, DB_HASH,
+ DB_THREAD | DB_CREATE, filemode);
+ if (rc) {
+ syslog(LOG_DAEMON|LOG_ERR, "cannot open database %s: %s",
dbname, db_strerror (rc));
db->close(db, 0);
db = NULL;
+ dbenv->close(dbenv, 0);
+ dbenv = NULL;
tbf_disabled = 1;
}
+
+ if (truncate) {
+ rc = db->truncate(db, NULL, NULL, 0);
+ if (rc)
+ syslog(LOG_DAEMON|LOG_WARNING,
+ "failed to truncate database %s: %s",
+ dbname, db_strerror(rc));
+ }
}
static DB *
-tbf_open_safe(const char *mode)
+tbf_open_safe(const char *params)
{
if (tbf_disabled)
return NULL;
pthread_mutex_lock(&mutex);
if (!db)
- tbf_open(mode ? mode : "truncate");
+ tbf_open(params ? params : DEFOPENPARAMS);
pthread_mutex_unlock(&mutex);
return db;
}
int
tbf_init(struct vmod_priv *priv, const struct VCL_conf *vclconf)
{
VTAILQ_INIT(&keylock_head);
VTAILQ_INIT(&keylock_avail);
}
void
-vmod_open(struct sess *sp, const char *file_name, const char *mode)
+vmod_open(struct sess *sp, const char *dir, const char *params)
{
if (db) {
syslog(LOG_DAEMON|LOG_ERR, "tbf.open called twice");
return;
}
- tbf_set_db_name(file_name);
- tbf_open_safe(mode);
+ tbf_set_db_dir(dir);
+ tbf_open_safe(params);
}
void
vmod_close(struct sess *sp)
{
+ pthread_mutex_lock(&mutex);
if (db) {
debug(1, ("closing database %s", dbname));
db->close(db, 0);
db = NULL;
+ dbenv->close(dbenv, 0);
+ dbenv = NULL;
tbf_disabled = 0;
}
+ pthread_mutex_unlock(&mutex);
}
void
vmod_sync(struct sess *sp)
{
if (db) {
diff --git a/src/vmod-tbf.3 b/src/vmod-tbf.3
index cf12ea7..bd7b233 100644
--- a/src/vmod-tbf.3
+++ b/src/vmod-tbf.3
@@ -10,19 +10,19 @@
.\" 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/>.
-.TH VMOD-TBF 1 "July 23, 2013" "VMOD-TBF" "User Reference"
+.TH VMOD-TBF 1 "August 2, 2013" "VMOD-TBF" "User Reference"
.SH NAME
vmod-tbf \- token bucket filtering for Varnish
.SH SYNOPSIS
.B import tbf;
-.BI "VOID tbf.open(STRING " dbfile ", STRING " params ");"
+.BI "VOID tbf.open(STRING " dbdir ", STRING " params ");"
.B VOID tbf.close();
.BI "BOOL tbf.rate(STRING " key ", INT " COST ", DURATION " interval ", INT " burst_size ");"
.BI "BOOL tbf.check(STRING " key ", STRING " rate ");"
@@ -105,19 +105,23 @@ sub vcl_recv {
error 429 "Request rate exceeded."
}
}
.EE
.SS Storage
.PP
-Buckets are kept in a Berkeley DB file. The \fBtbf.open\fR function
-controls its location and permissions. The \fBdbfile\fR argument
-supplies the full pathname to the file. The \fBparams\fR argument is
-a semicolon separated list of the following parameters:
+Buckets are kept in a Berkeley database file. The \fBtbf.open\fR function
+controls its location and permissions. The \fBdbdir\fR argument
+supplies the full pathname to the directory where the database is
+located. The \fBparams\fR argument is a semicolon separated list of
+the following parameters:
+.TP
+.BI dbname= NAME
+Sets the name of the database file. Default is \fBtbf.bdb\fR.
.TP
.BR truncate " or " trunc
-Truncate the file if it already exists.
+Truncate the database if it already exists.
.TP
.BI mode= OCT
Set the file mode. \fIOCT\fR is an octal number. The default file
mode is \fB640\fR. Note that this parameter takes effect only when
creating the file. If the database file already exists by the time
\fBtbf.open\fR is called, its mode will not be altered.
@@ -138,16 +142,22 @@ sub vcl_init {
.EE
.PP
Note that the directory where the database file is located must be
writable for the user \fBVarnish\fR runs as.
.PP
Unless the \fBtbf.open\fR function was called, both \fBtbf.rate\fR and
-\fBtbf.check\fR will attempt to use the file \fIlocalstatedir\fB/tbf.db\fR,
-where \fIlocalstatedir\fR is the directory for modifiable
-single-machine data, which is set when configuring the package
-(e.g. \fB/var/run/tbf\fR or the like).
+\fBtbf.check\fR will attempt to use the database located in
+\fIlocalstatedir\fB/vmod-tbf\fR, where \fIlocalstatedir\fR is the
+directory for modifiable single-machine data, which is set when
+configuring the package (e.g. \fB/var/run\fR or the like).
+.PP
+If the database directory does not exist, \fBtbf.open\fR will attempt
+to create it, deducing its mode from the database file mode (see the
+\fBmode=\fR parameter above) by setting executable
+bit in each triplet that has read or write bit set (e.g. \fB640\fR
+will become \fB750\fR).
.PP
The \fBtbf.close\fR function flushes the data and closes the database.
It is normally called from the \fBvcl_fini\fR subroutine:
.PP
.EX
sub vcl_fini {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 383f18a..4790115 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -3,13 +3,15 @@ VMOD_TESTS = \
test01.vtc\
test02.vtc\
test03.vtc\
time00.vtc
EXTRA_DIST=$(VMOD_TESTS)
-DISTCLEANFILES=tbf.db
+distclean-local:
+ rm -fr tbf
+
check:
cd $(abs_srcdir); \
for t in $(VMOD_TESTS); do \
$(VARNISHSRC)/bin/varnishtest/varnishtest \
-Dvarnishd=$(VARNISHSRC)/bin/varnishd/varnishd \
-Dvmod_topsrc=$(abs_top_srcdir) \
diff --git a/tests/test00.vtc b/tests/test00.vtc
index 10f99ca..f69f5ed 100644
--- a/tests/test00.vtc
+++ b/tests/test00.vtc
@@ -5,13 +5,13 @@ server s1 {
txresp
} -start
varnish v1 -vcl+backend {
import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
sub vcl_init {
- tbf.open("${vmod_topbuild}/tests/tbf.db", "truncate");
+ tbf.open("${vmod_topbuild}/tests/tbf", "truncate");
}
sub vcl_fini {
tbf.close();
}
sub vcl_recv {
if (!tbf.rate("url:"+req.url, 1, 20 s, 5)) {
diff --git a/tests/test01.vtc b/tests/test01.vtc
index ad6f7d1..39eb4c3 100644
--- a/tests/test01.vtc
+++ b/tests/test01.vtc
@@ -5,13 +5,13 @@ server s1 {
txresp
} -start
varnish v1 -vcl+backend {
import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
sub vcl_init {
- tbf.open("${vmod_topbuild}/tests/tbf.db", "trunc");
+ tbf.open("${vmod_topbuild}/tests/tbf", "trunc");
}
sub vcl_fini {
tbf.close();
}
sub vcl_deliver {
set resp.http.result = tbf.rate("url:"+req.url, 1, 1s, 5);
diff --git a/tests/test02.vtc b/tests/test02.vtc
index b957181..8df6730 100644
--- a/tests/test02.vtc
+++ b/tests/test02.vtc
@@ -5,13 +5,13 @@ server s1 {
txresp
} -start
varnish v1 -vcl+backend {
import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
sub vcl_init {
- tbf.open("${vmod_topbuild}/tests/tbf.db", "truncate");
+ tbf.open("${vmod_topbuild}/tests/tbf", "truncate");
}
sub vcl_fini {
tbf.close();
}
sub vcl_recv {
if (!tbf.check("url:"+req.url, "4 req/s")) {
diff --git a/tests/test03.vtc b/tests/test03.vtc
index 5ffba9a..aa23da4 100644
--- a/tests/test03.vtc
+++ b/tests/test03.vtc
@@ -5,13 +5,13 @@ server s1 {
txresp
} -start
varnish v1 -vcl+backend {
import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
sub vcl_init {
- tbf.open("${vmod_topbuild}/tests/tbf.db", "trunc");
+ tbf.open("${vmod_topbuild}/tests/tbf", "trunc");
}
sub vcl_fini {
tbf.close();
}
sub vcl_deliver {
set resp.http.result = tbf.check("url:"+req.url, "4req/s");

Return to:

Send suggestions and report system problems to the System administrator.