summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2013-08-01 16:20:54 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2013-08-02 07:48:54 (GMT)
commit73f605f76d82aec40a7f1bbd309e2097d9abfd42 (patch) (unidiff)
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.
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 @@
14# You should have received a copy of the GNU General Public License 14# You should have received a copy of the GNU General Public License
15# along with vmod-tbf. If not, see <http://www.gnu.org/licenses/>. 15# along with vmod-tbf. If not, see <http://www.gnu.org/licenses/>.
16AC_PREREQ(2.69) 16AC_PREREQ(2.69)
17AC_INIT([vmod-tbf], 0.99.90, [gray@gnu.org]) 17AC_INIT([vmod-tbf], 0.99.91, [gray@gnu.org])
18AC_CONFIG_AUX_DIR([build-aux]) 18AC_CONFIG_AUX_DIR([build-aux])
19AC_CONFIG_MACRO_DIR([m4]) 19AC_CONFIG_MACRO_DIR([m4])
20AC_CONFIG_SRCDIR(src/vmod_tbf.vcc) 20AC_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 @@
20#include <stdbool.h> 20#include <stdbool.h>
21#include <syslog.h> 21#include <syslog.h>
22#include <inttypes.h> 22#include <inttypes.h>
23#include <sys/stat.h>
23#include <db.h> 24#include <db.h>
24#include "vrt.h" 25#include "vrt.h"
25#include "vcc_if.h" 26#include "vcc_if.h"
@@ -41,14 +42,19 @@ debugprt(const char *fmt, ...)
41# define USEC_PER_SEC 1000000L 42# define USEC_PER_SEC 1000000L
42#endif 43#endif
43 44
45#define DEFDBNAME "tbf.bdb"
46#define DEFOPENPARAMS "truncate"
47#define DBFILEMODE 0640
48
49static char *dbdir;
44static char *dbname; 50static char *dbname;
51static DB_ENV *dbenv;
45static DB *db; 52static DB *db;
46static uint64_t autosync_max; 53static uint64_t autosync_max;
47static uint64_t autosync_count; 54static uint64_t autosync_count;
48static int tbf_disabled; 55static int tbf_disabled;
49 56
50static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 57static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
51#define DBFILEMODE 0640
52 58
53 59
54/* The keylock structure serializes accesses to each db record, ensuring 60/* The keylock structure serializes accesses to each db record, ensuring
@@ -122,147 +128,217 @@ keylock_remove_safe(struct keylock *kp)
122} 128}
123 129
124static void 130static void
125tbf_set_db_name(const char *file_name) 131tbf_set_db_dir(const char *dir)
126{ 132{
127 if (dbname) 133 if (dbdir)
128 free(dbname); 134 free(dbdir);
129 dbname = strdup(file_name); 135 dbdir = strdup(dir);
130 if (!dbname) 136 AN(dbdir);
131 abort();
132} 137}
133 138
134struct mode_kw { 139struct param_kw {
135 char *mkw_str; 140 char *pkw_str;
136 int mkw_len; 141 int pkw_len;
137 int mkw_tok; 142 int pkw_tok;
138}; 143};
139 144
140enum { 145enum {
141 MKW_TRUNCATE, 146 PKW_TRUNCATE,
142 MKW_MODE, 147 PKW_MODE,
143 MKW_SYNC, 148 PKW_SYNC,
144 MKW_DEBUG, 149 PKW_DEBUG,
150 PKW_DBNAME
145}; 151};
146 152
147static struct mode_kw mode_kw_tab[] = { 153static struct param_kw param_kw_tab[] = {
148#define S(s) #s, sizeof(#s)-1 154#define S(s) #s, sizeof(#s)-1
149 { S(truncate), MKW_TRUNCATE }, 155 { S(truncate), PKW_TRUNCATE },
150 { S(trunc), MKW_TRUNCATE }, 156 { S(trunc), PKW_TRUNCATE },
151 { S(mode=), MKW_MODE }, 157 { S(mode=), PKW_MODE },
152 { S(sync=), MKW_SYNC }, 158 { S(sync=), PKW_SYNC },
153 { S(debug=), MKW_DEBUG }, 159 { S(debug=), PKW_DEBUG },
160 { S(dbname=), PKW_DBNAME },
154 { NULL } 161 { NULL }
155#undef S 162#undef S
156}; 163};
157 164
158static void 165static void
159tbf_open(const char *mode) 166tbf_open(const char *params)
160{ 167{
161 int rc; 168 int rc;
162 int flags = DB_CREATE|DB_THREAD;
163 int filemode = DBFILEMODE; 169 int filemode = DBFILEMODE;
164 uint64_t n; 170 uint64_t n;
165 char *p; 171 char *p;
172 struct stat st;
173 int truncate = 0;
166 174
167 if (!dbname) 175 if (!dbdir) {
168 tbf_set_db_name(LOCALSTATEDIR "/tbf.db"); 176 dbdir = strdup(LOCALSTATEDIR "/vmod-tbf");
169 177 AN(dbdir);
170 rc = db_create(&db, NULL, 0);
171 if (rc) {
172 syslog(LOG_DAEMON|LOG_ERR, "cannot create db struct");
173 return;
174 } 178 }
175 179 if (!dbname) {
176 while (*mode) { 180 dbname = strdup(DEFDBNAME);
177 struct mode_kw *mkw; 181 AN(dbname);
182 }
183
184 while (*params) {
185 struct param_kw *pkw;
178 186
179 for (mkw = mode_kw_tab; mkw->mkw_str; mkw++) { 187 for (pkw = param_kw_tab; pkw->pkw_str; pkw++) {
180 if (strncmp(mode, mkw->mkw_str, mkw->mkw_len) == 0) 188 if (strncmp(params, pkw->pkw_str, pkw->pkw_len) == 0)
181 break; 189 break;
182 } 190 }
183 191
184 if (!mkw->mkw_str) { 192 if (!pkw->pkw_str) {
185 syslog(LOG_DAEMON|LOG_ERR, "invalid keyword %s", mode); 193 syslog(LOG_DAEMON|LOG_ERR, "invalid keyword %s", params);
186 break; 194 break;
187 } 195 }
188 196
189 mode += mkw->mkw_len; 197 params += pkw->pkw_len;
190 198
191 switch (mkw->mkw_tok) { 199 switch (pkw->pkw_tok) {
192 case MKW_TRUNCATE: 200 case PKW_TRUNCATE:
193 flags |= DB_TRUNCATE; 201 truncate = 1;
194 break; 202 break;
195 203
196 case MKW_MODE: 204 case PKW_MODE:
197 errno = 0; 205 errno = 0;
198 n = strtoul(mode, &p, 8); 206 n = strtoul(params, &p, 8);
199 if (errno || (n & ~0777) || !(*p == 0 || *p == ';')) { 207 if (errno || (n & ~0777) || !(*p == 0 || *p == ';')) {
200 syslog(LOG_DAEMON|LOG_ERR, 208 syslog(LOG_DAEMON|LOG_ERR,
201 "invalid file mode near %s", p); 209 "invalid file mode near %s", p);
202 mode += strlen(mode); 210 params += strlen(params);
203 } else { 211 } else {
204 filemode = n; 212 filemode = n;
205 mode = p; 213 params = p;
206 } 214 }
207 break; 215 break;
208 216
209 case MKW_SYNC: 217 case PKW_SYNC:
210 errno = 0; 218 errno = 0;
211 n = strtoul(mode, &p, 10); 219 n = strtoul(params, &p, 10);
212 if (errno || !(*p == 0 || *p == ';')) { 220 if (errno || !(*p == 0 || *p == ';')) {
213 syslog(LOG_DAEMON|LOG_ERR, 221 syslog(LOG_DAEMON|LOG_ERR,
214 "invalid count near %s", p); 222 "invalid count near %s", p);
215 mode += strlen(mode); 223 params += strlen(params);
216 } else { 224 } else {
217 autosync_max = n; 225 autosync_max = n;
218 autosync_count = 0; 226 autosync_count = 0;
219 mode = p; 227 params = p;
220 } 228 }
221 break; 229 break;
222 230
223 case MKW_DEBUG: 231 case PKW_DEBUG:
224 errno = 0; 232 errno = 0;
225 n = strtoul(mode, &p, 10); 233 n = strtoul(params, &p, 10);
226 if (errno || !(*p == 0 || *p == ';')) { 234 if (errno || !(*p == 0 || *p == ';')) {
227 syslog(LOG_DAEMON|LOG_ERR, 235 syslog(LOG_DAEMON|LOG_ERR,
228 "invalid debug level near %s", p); 236 "invalid debug level near %s", p);
229 mode += strlen(mode); 237 params += strlen(params);
230 } else { 238 } else {
231 debug_level = n; 239 debug_level = n;
232 mode = p; 240 params = p;
233 } 241 }
242 break;
243
244 case PKW_DBNAME:
245 if (dbname)
246 free(dbname);
247 n = strcspn(params, ";");
248 dbname = malloc(n + 1);
249 AN(dbname);
250 memcpy(dbname, params, n);
251 dbname[n] = 0;
252 params += n;
253 break;
234 } 254 }
235 255
236 if (*mode == 0) 256 if (*params == 0)
237 break; 257 break;
238 else if (*mode == ';') 258 else if (*params == ';')
239 mode++; 259 params++;
240 else { 260 else {
241 syslog(LOG_DAEMON|LOG_ERR, 261 syslog(LOG_DAEMON|LOG_ERR,
242 "expected ';' near %s", mode); 262 "expected ';' near %s", params);
243 break; 263 break;
244 } 264 }
245 } 265 }
246 266
247 debug(1, ("opening database %s", dbname)); 267 debug(1, ("opening database %s/%s", dbdir, dbname));
248 rc = db->open(db, NULL, dbname, NULL, DB_HASH, flags, filemode); 268
269 if (rc = db_env_create(&dbenv, 0)) {
270 syslog(LOG_DAEMON|LOG_ERR, "cannot create db environment: %s",
271 db_strerror(rc));
272 return;
273 }
274
275 if (stat(dbdir, &st)) {
276 if (errno == ENOENT) {
277 if (mkdir(dbdir,
278 filemode | 0100 |
279 ((filemode & 0060) ? 0010 : 0) |
280 ((filemode & 0006) ? 0001 : 0))) {
281 syslog(LOG_DAEMON|LOG_ERR,
282 "cannot create db environment directory %s: %m",
283 dbdir);
284 }
285 } else {
286 syslog(LOG_DAEMON|LOG_ERR,
287 "cannot stat db environment directory %s: %m",
288 dbdir);
289 return;
290 }
291 } else if (!S_ISDIR(st.st_mode)) {
292 syslog(LOG_DAEMON|LOG_ERR, "%s is not a directory",
293 dbdir);
294 return;
295 }
296
297 rc = dbenv->open(dbenv, dbdir,
298 DB_THREAD | DB_CREATE | DB_INIT_MPOOL | DB_INIT_CDB,
299 0);
249 if (rc) { 300 if (rc) {
250 syslog(LOG_DAEMON|LOG_ERR, "cannot open %s: %s", 301 syslog(LOG_DAEMON|LOG_ERR, "cannot open db environment %s: %s",
302 dbdir, db_strerror(rc));
303 tbf_disabled = 1;
304 return;
305 }
306
307 rc = db_create(&db, dbenv, 0);
308 if (rc) {
309 syslog(LOG_DAEMON|LOG_ERR, "cannot create db struct");
310 return;
311 }
312
313 rc = db->open(db, NULL, dbname, NULL, DB_HASH,
314 DB_THREAD | DB_CREATE, filemode);
315 if (rc) {
316 syslog(LOG_DAEMON|LOG_ERR, "cannot open database %s: %s",
251 dbname, db_strerror (rc)); 317 dbname, db_strerror (rc));
252 db->close(db, 0); 318 db->close(db, 0);
253 db = NULL; 319 db = NULL;
320 dbenv->close(dbenv, 0);
321 dbenv = NULL;
254 tbf_disabled = 1; 322 tbf_disabled = 1;
255 } 323 }
324
325 if (truncate) {
326 rc = db->truncate(db, NULL, NULL, 0);
327 if (rc)
328 syslog(LOG_DAEMON|LOG_WARNING,
329 "failed to truncate database %s: %s",
330 dbname, db_strerror(rc));
331 }
256} 332}
257 333
258static DB * 334static DB *
259tbf_open_safe(const char *mode) 335tbf_open_safe(const char *params)
260{ 336{
261 if (tbf_disabled) 337 if (tbf_disabled)
262 return NULL; 338 return NULL;
263 pthread_mutex_lock(&mutex); 339 pthread_mutex_lock(&mutex);
264 if (!db) 340 if (!db)
265 tbf_open(mode ? mode : "truncate"); 341 tbf_open(params ? params : DEFOPENPARAMS);
266 pthread_mutex_unlock(&mutex); 342 pthread_mutex_unlock(&mutex);
267 return db; 343 return db;
268} 344}
@@ -275,25 +351,29 @@ tbf_init(struct vmod_priv *priv, const struct VCL_conf *vclconf)
275} 351}
276 352
277void 353void
278vmod_open(struct sess *sp, const char *file_name, const char *mode) 354vmod_open(struct sess *sp, const char *dir, const char *params)
279{ 355{
280 if (db) { 356 if (db) {
281 syslog(LOG_DAEMON|LOG_ERR, "tbf.open called twice"); 357 syslog(LOG_DAEMON|LOG_ERR, "tbf.open called twice");
282 return; 358 return;
283 } 359 }
284 tbf_set_db_name(file_name); 360 tbf_set_db_dir(dir);
285 tbf_open_safe(mode); 361 tbf_open_safe(params);
286} 362}
287 363
288void 364void
289vmod_close(struct sess *sp) 365vmod_close(struct sess *sp)
290{ 366{
367 pthread_mutex_lock(&mutex);
291 if (db) { 368 if (db) {
292 debug(1, ("closing database %s", dbname)); 369 debug(1, ("closing database %s", dbname));
293 db->close(db, 0); 370 db->close(db, 0);
294 db = NULL; 371 db = NULL;
372 dbenv->close(dbenv, 0);
373 dbenv = NULL;
295 tbf_disabled = 0; 374 tbf_disabled = 0;
296 } 375 }
376 pthread_mutex_unlock(&mutex);
297} 377}
298 378
299void 379void
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 @@
13.\" 13.\"
14.\" You should have received a copy of the GNU General Public License 14.\" You should have received a copy of the GNU General Public License
15.\" along with vmod-tbf. If not, see <http://www.gnu.org/licenses/>. 15.\" along with vmod-tbf. If not, see <http://www.gnu.org/licenses/>.
16.TH VMOD-TBF 1 "July 23, 2013" "VMOD-TBF" "User Reference" 16.TH VMOD-TBF 1 "August 2, 2013" "VMOD-TBF" "User Reference"
17.SH NAME 17.SH NAME
18vmod-tbf \- token bucket filtering for Varnish 18vmod-tbf \- token bucket filtering for Varnish
19.SH SYNOPSIS 19.SH SYNOPSIS
20.B import tbf; 20.B import tbf;
21 21
22.BI "VOID tbf.open(STRING " dbfile ", STRING " params ");" 22.BI "VOID tbf.open(STRING " dbdir ", STRING " params ");"
23 23
24.B VOID tbf.close(); 24.B VOID tbf.close();
25 25
@@ -108,13 +108,17 @@ sub vcl_recv {
108.EE 108.EE
109.SS Storage 109.SS Storage
110.PP 110.PP
111Buckets are kept in a Berkeley DB file. The \fBtbf.open\fR function 111Buckets are kept in a Berkeley database file. The \fBtbf.open\fR function
112controls its location and permissions. The \fBdbfile\fR argument 112controls its location and permissions. The \fBdbdir\fR argument
113supplies the full pathname to the file. The \fBparams\fR argument is 113supplies the full pathname to the directory where the database is
114a semicolon separated list of the following parameters: 114located. The \fBparams\fR argument is a semicolon separated list of
115the following parameters:
116.TP
117.BI dbname= NAME
118Sets the name of the database file. Default is \fBtbf.bdb\fR.
115.TP 119.TP
116.BR truncate " or " trunc 120.BR truncate " or " trunc
117Truncate the file if it already exists. 121Truncate the database if it already exists.
118.TP 122.TP
119.BI mode= OCT 123.BI mode= OCT
120Set the file mode. \fIOCT\fR is an octal number. The default file 124Set 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
141writable for the user \fBVarnish\fR runs as. 145writable for the user \fBVarnish\fR runs as.
142.PP 146.PP
143Unless the \fBtbf.open\fR function was called, both \fBtbf.rate\fR and 147Unless the \fBtbf.open\fR function was called, both \fBtbf.rate\fR and
144\fBtbf.check\fR will attempt to use the file \fIlocalstatedir\fB/tbf.db\fR, 148\fBtbf.check\fR will attempt to use the database located in
145where \fIlocalstatedir\fR is the directory for modifiable 149\fIlocalstatedir\fB/vmod-tbf\fR, where \fIlocalstatedir\fR is the
146single-machine data, which is set when configuring the package 150directory for modifiable single-machine data, which is set when
147(e.g. \fB/var/run/tbf\fR or the like). 151configuring the package (e.g. \fB/var/run\fR or the like).
152.PP
153If the database directory does not exist, \fBtbf.open\fR will attempt
154to create it, deducing its mode from the database file mode (see the
155\fBmode=\fR parameter above) by setting executable
156bit in each triplet that has read or write bit set (e.g. \fB640\fR
157will become \fB750\fR).
148.PP 158.PP
149The \fBtbf.close\fR function flushes the data and closes the database. 159The \fBtbf.close\fR function flushes the data and closes the database.
150It is normally called from the \fBvcl_fini\fR subroutine: 160It 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 = \
6 time00.vtc 6 time00.vtc
7 7
8EXTRA_DIST=$(VMOD_TESTS) 8EXTRA_DIST=$(VMOD_TESTS)
9DISTCLEANFILES=tbf.db 9distclean-local:
10 rm -fr tbf
11
10check: 12check:
11 cd $(abs_srcdir); \ 13 cd $(abs_srcdir); \
12 for t in $(VMOD_TESTS); do \ 14 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 {
8varnish v1 -vcl+backend { 8varnish v1 -vcl+backend {
9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so"; 9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
10 sub vcl_init { 10 sub vcl_init {
11 tbf.open("${vmod_topbuild}/tests/tbf.db", "truncate"); 11 tbf.open("${vmod_topbuild}/tests/tbf", "truncate");
12 } 12 }
13 sub vcl_fini { 13 sub vcl_fini {
14 tbf.close(); 14 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 {
8varnish v1 -vcl+backend { 8varnish v1 -vcl+backend {
9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so"; 9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
10 sub vcl_init { 10 sub vcl_init {
11 tbf.open("${vmod_topbuild}/tests/tbf.db", "trunc"); 11 tbf.open("${vmod_topbuild}/tests/tbf", "trunc");
12 } 12 }
13 sub vcl_fini { 13 sub vcl_fini {
14 tbf.close(); 14 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 {
8varnish v1 -vcl+backend { 8varnish v1 -vcl+backend {
9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so"; 9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
10 sub vcl_init { 10 sub vcl_init {
11 tbf.open("${vmod_topbuild}/tests/tbf.db", "truncate"); 11 tbf.open("${vmod_topbuild}/tests/tbf", "truncate");
12 } 12 }
13 sub vcl_fini { 13 sub vcl_fini {
14 tbf.close(); 14 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 {
8varnish v1 -vcl+backend { 8varnish v1 -vcl+backend {
9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so"; 9 import tbf from "${vmod_topbuild}/src/.libs/libvmod_tbf.so";
10 sub vcl_init { 10 sub vcl_init {
11 tbf.open("${vmod_topbuild}/tests/tbf.db", "trunc"); 11 tbf.open("${vmod_topbuild}/tests/tbf", "trunc");
12 } 12 }
13 sub vcl_fini { 13 sub vcl_fini {
14 tbf.close(); 14 tbf.close();

Return to:

Send suggestions and report system problems to the System administrator.