summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--configure.ac2
-rw-r--r--src/tbf.c212
-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, 175 insertions, 83 deletions
diff --git a/configure.ac b/configure.ac
index 9c228f6..4ca4e6f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,7 +14,7 @@
# 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)
diff --git a/src/tbf.c b/src/tbf.c
index 1dfb6c6..7050fc7 100644
--- a/src/tbf.c
+++ b/src/tbf.c
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include <syslog.h>
#include <inttypes.h>
+#include <sys/stat.h>
#include <db.h>
#include "vrt.h"
#include "vcc_if.h"
@@ -41,14 +42,19 @@ debugprt(const char *fmt, ...)
# 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
@@ -122,147 +128,217 @@ keylock_remove_safe(struct keylock *kp)
}
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);
}
-
- while (*mode) {
- struct mode_kw *mkw;
+ if (!dbname) {
+ dbname = strdup(DEFDBNAME);
+ AN(dbname);
+ }
+
+ 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 %s: %s",
+ 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 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;
}
@@ -275,25 +351,29 @@ tbf_init(struct vmod_priv *priv, const struct VCL_conf *vclconf)
}
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
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
@@ -13,13 +13,13 @@
.\"
.\" 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();
@@ -108,13 +108,17 @@ sub vcl_recv {
.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
@@ -141,10 +145,16 @@ 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:
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 383f18a..4790115 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -6,7 +6,9 @@ VMOD_TESTS = \
time00.vtc
EXTRA_DIST=$(VMOD_TESTS)
-DISTCLEANFILES=tbf.db
+distclean-local:
+ rm -fr tbf
+
check:
cd $(abs_srcdir); \
for t in $(VMOD_TESTS); do \
diff --git a/tests/test00.vtc b/tests/test00.vtc
index 10f99ca..f69f5ed 100644
--- a/tests/test00.vtc
+++ b/tests/test00.vtc
@@ -8,7 +8,7 @@ server s1 {
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();
diff --git a/tests/test01.vtc b/tests/test01.vtc
index ad6f7d1..39eb4c3 100644
--- a/tests/test01.vtc
+++ b/tests/test01.vtc
@@ -8,7 +8,7 @@ server s1 {
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();
diff --git a/tests/test02.vtc b/tests/test02.vtc
index b957181..8df6730 100644
--- a/tests/test02.vtc
+++ b/tests/test02.vtc
@@ -8,7 +8,7 @@ server s1 {
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();
diff --git a/tests/test03.vtc b/tests/test03.vtc
index 5ffba9a..aa23da4 100644
--- a/tests/test03.vtc
+++ b/tests/test03.vtc
@@ -8,7 +8,7 @@ server s1 {
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();

Return to:

Send suggestions and report system problems to the System administrator.