summaryrefslogtreecommitdiffabout
path: root/src
authorSergey Poznyakoff <gray@gnu.org.ua>2013-10-10 21:12:43 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2013-10-11 09:22:57 (GMT)
commitdf83b714395d41b096f7ad8cc3a090c9341f7598 (patch) (unidiff)
tree1c3bcc412e633d3fc678e2cc083ea947e5dcd8e8 /src
downloadvmod-binlog-df83b714395d41b096f7ad8cc3a090c9341f7598.tar.gz
vmod-binlog-df83b714395d41b096f7ad8cc3a090c9341f7598.tar.bz2
Initial commit
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/.gitignore2
-rw-r--r--src/Makefile.am41
-rw-r--r--src/binlog.c516
-rw-r--r--src/binlogcat.c134
-rw-r--r--src/vmod-binlog.h71
-rw-r--r--src/vmod.vcc7
6 files changed, 771 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..7f6e438
--- a/dev/null
+++ b/src/.gitignore
@@ -0,0 +1,2 @@
1vcc_if.c
2vcc_if.h
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..c9daf9a
--- a/dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,41 @@
1# This file is part of vmod-binlog
2# Copyright (C) 2013 Sergey Poznyakoff
3#
4# Vmod-binlog is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 3, or (at your option)
7# any later version.
8#
9# Vmod-binlog is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with vmod-binlog. If not, see <http://www.gnu.org/licenses/>.
16
17AM_CPPFLAGS = -I$(VARNISHSRC)/include -I$(VARNISHSRC)
18
19bin_PROGRAMS = binlogcat
20binlogcat_SOURCES = binlogcat.c
21
22vmoddir = $(VMODDIR)
23vmod_LTLIBRARIES = libvmod_binlog.la
24
25libvmod_binlog_la_LDFLAGS = -module -export-dynamic -avoid-version
26libvmod_binlog_la_LIBADD=
27
28libvmod_binlog_la_SOURCES = \
29 binlog.c\
30 vmod-binlog.h\
31 vcc_if.c vcc_if.h
32
33BUILT_SOURCES = vcc_if.c vcc_if.h
34
35vcc_if.c vcc_if.h: $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod.vcc
36 @PYTHON@ $(VARNISHSRC)/lib/libvmod_std/vmod.py $(top_srcdir)/src/vmod.vcc
37
38EXTRA_DIST = vmod.vcc
39
40CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h
41
diff --git a/src/binlog.c b/src/binlog.c
new file mode 100644
index 0000000..f896803
--- a/dev/null
+++ b/src/binlog.c
@@ -0,0 +1,516 @@
1/* This file is part of vmod-binlog
2 Copyright (C) 2013 Sergey Poznyakoff
3
4 Vmod-binlog is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 Vmod-binlog is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with vmod-binlog. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <config.h>
19#include <sys/stat.h>
20#include <sys/mman.h>
21#include <fcntl.h>
22#include <unistd.h>
23#include <errno.h>
24#include <syslog.h>
25#include <stdlib.h>
26#include <ctype.h>
27#include <time.h>
28#include "vrt.h"
29#include "vcc_if.h"
30#include "bin/varnishd/cache.h"
31#include "vmod-binlog.h"
32
33#ifndef O_SEARCH
34# define O_SEARCH 0
35#endif
36
37#define BLF_ROUNDTS 0x01
38
39struct binlog_config {
40 size_t size; /* maximum file size */
41 unsigned interval; /* file rotation interval */
42 char *pattern; /* file name pattern */
43 int umask; /* umask for new files and directories */
44 char *dir; /* root storage directory */
45 int dd; /* directory descriptor */
46 char *fname; /* current file name */
47 int fd; /* current file descriptor */
48 union binlog_header *base; /* mmap base */
49 struct binlog_record *recbase; /* record base */
50 size_t recnum; /* number of records in recbase */
51 size_t recidx; /* index of the next free entry in recbase */
52 time_t stoptime; /* when to rotate the current file */
53 pthread_mutex_t mutex;
54 int debug;
55 int flags;
56};
57
58static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
59
60void
61binlog_error(const char *fmt, ...)
62{
63 va_list ap;
64 va_start(ap, fmt);
65 vsyslog(LOG_DAEMON|LOG_ERR, fmt, ap);
66 va_end(ap);
67}
68
69void
70binlog_debug(const char *fmt, ...)
71{
72 va_list ap;
73 va_start(ap, fmt);
74 vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);
75 va_end(ap);
76}
77
78#define debug(c,l,s) do { if ((c)->debug>=(l)) binlog_debug s; } while(0)
79
80int
81module_init(struct vmod_priv *priv, const struct VCL_conf *vclconf)
82{
83 struct binlog_config *conf = calloc(1, sizeof(*conf));
84 AN(conf);
85 priv->priv = conf;
86 return 0;
87}
88
89static char *
90findparam(const char *param, char *name)
91{
92 const char *p;
93 char *q;
94
95 while (*param) {
96 for (p = param, q = name; *p && *q && *p == *q; p++, q++);
97 for (param = p; *param && *param != ';'; param++);
98 if (*q == 0 && *p == '=') {
99 size_t len = param - p - 1;
100 q = malloc(len + 1);
101 AN(q);
102 memcpy(q, p + 1, len);
103 q[len] = 0;
104 return q;
105 }
106 if (*param)
107 ++param;
108 }
109 return NULL;
110}
111
112static unsigned long
113getinterval(char *p, char **endp)
114{
115 int n;
116 unsigned long hours = 0, minutes = 0, seconds = 0;
117
118 for (;;) {
119 n = 0;
120 while (*p && isdigit(*p)) {
121 n = 10*n + *p - '0';
122 p++;
123 }
124
125 switch (*p) {
126 case 'h':
127 case 'H':
128 if (hours) {
129 *endp = p;
130 return -1;
131 }
132 hours = n;
133 break;
134 case 'm':
135 case 'M':
136 if (minutes) {
137 *endp = p;
138 return -1;
139 }
140 minutes = n;
141 break;
142 case 's':
143 case 'S':
144 if (seconds) {
145 *endp = p;
146 return -1;
147 }
148 seconds = n;
149 break;
150 default:
151 *endp = p;
152 if (!hours && !minutes && !seconds)
153 return n;
154 return (hours*60 + minutes)*60 + seconds;
155 }
156 p++;
157 }
158}
159
160void
161vmod_init(struct sess *sp, struct vmod_priv *priv, const char *param)
162{
163 struct binlog_config *conf = priv->priv;
164 struct stat st;
165 char *dir, *p, *q;
166 unsigned long n;
167
168 p = findparam(param, "debug");
169 if (p) {
170 conf->debug = atoi(p);
171 free(p);
172 }
173
174 dir = findparam(param, "dir");
175 if (!dir) {
176 binlog_error("parameter \"dir\" not set");
177 abort();
178 }
179 if (stat(dir, &st)) {
180 if (errno == ENOENT)
181 binlog_error("logging directory does not exist");
182 else
183 binlog_error("cannot stat logging directory %s: %s",
184 dir, strerror(errno));
185 abort();
186 }
187 if (!S_ISDIR(st.st_mode)) {
188 binlog_error("%s exists, but is not a directory");
189 abort();
190 }
191 conf->dd = open(dir, O_SEARCH | O_DIRECTORY);
192 if (conf->dd == -1) {
193 binlog_error("cannot open directory %s: %s",
194 dir, strerror(errno));
195 abort();
196 }
197 conf->dir = dir;
198
199 p = findparam(param, "pattern");
200 if (!p) {
201 p = strdup(BINLOG_PATTERN);
202 AN(p);
203 }
204 conf->pattern = p;
205
206 p = findparam(param, "size");
207 if (p) {
208 uintmax_t u;
209
210 errno = 0;
211 u = strtoul(p, &q, 10);
212 if (errno) {
213 binlog_error("invalid size value");
214 abort();
215 }
216 free(p);
217 switch (*q) {
218 case 'g':
219 case 'G':
220 u <<= 10;
221 case 'm':
222 case 'M':
223 u <<= 10;
224 case 'k':
225 case 'K':
226 u <<= 10;
227 }
228 if (u > SIZE_T_MAX) {
229 binlog_error("invalid size value");
230 abort();
231 }
232 conf->size = u;
233 } else
234 conf->size = BINLOG_SIZE;
235
236 p = findparam(param, "interval");
237 if (p) {
238 conf->interval = getinterval(p, &q);
239 free(p);
240 if (*q) {
241 binlog_error("invalid interval (near \"%s\")", q);
242 abort();
243 }
244 } else
245 conf->interval = BINLOG_INTERVAL;
246
247 p = findparam(param, "umask");
248 if (p) {
249 n = strtoul(p, &q, 8);
250 free(p);
251 if (n & ~0777) {
252 binlog_error("umask out of range");
253 abort();
254 }
255 if (*q) {
256 binlog_error("invalid umask (near \"%s\")", q);
257 abort();
258 }
259 } else
260 conf->umask = BINLOG_UMASK;
261
262 p = findparam(param, "roundts");
263 if (p) {
264 if (atoi(p))
265 conf->flags |= BLF_ROUNDTS;
266 else
267 conf->flags &= ~BLF_ROUNDTS;
268 free(p);
269 }
270
271 conf->fd = -1;
272 conf->base = NULL;
273 conf->stoptime = time(NULL);
274 pthread_mutex_init(&conf->mutex, NULL);
275}
276
277static char *
278mkfilename(struct sess *sp, struct binlog_config *conf)
279{
280 time_t ts = time(NULL);
281 size_t u, n;
282 char *p, *q;
283
284 if (conf->flags & BLF_ROUNDTS)
285 ts -= ts % conf->interval;
286 u = WS_Reserve(sp->wrk->ws, 0);
287 p = sp->wrk->ws->f;
288 n = strftime(p, u, conf->pattern, gmtime(&ts));
289 if (n == 0) {
290 WS_Release(sp->wrk->ws, 0);
291 return NULL;
292 }
293 q = strdup(p);
294 AN(q);
295 WS_Release(sp->wrk->ws, 0);
296 return q;
297}
298
299static int
300mkdir_p(struct binlog_config *conf, char *dir)
301{
302 int rc = 0;
303 char *p;
304 struct stat st;
305
306 for (p = dir; rc == 0 && *p; p++) {
307 if (*p != '/')
308 continue;
309 *p = 0;
310 rc = fstatat(conf->dd, dir, &st, 0);
311 if (rc) {
312 if (errno == ENOENT) {
313 rc = mkdirat(conf->dd, dir,
314 0777 & ~conf->umask);
315 if (rc)
316 binlog_error("cannot create %s: %s",
317 dir,
318 strerror(errno));
319 } else
320 binlog_error("cannot stat %s: %s", dir,
321 strerror(errno));
322 } else if (!S_ISDIR(st.st_mode)) {
323 binlog_error("component \"%s\" is not a directory",
324 dir);
325 rc = -1;
326 }
327 *p = '/';
328 }
329 return rc;
330}
331
332static int
333createfile(struct sess *sp, struct binlog_config *conf)
334{
335 char *fname;
336 int fd;
337
338 conf->fname = NULL;
339 conf->fd = -1;
340
341 fname = mkfilename(sp, conf);
342 if (!fname)
343 return -1;
344 if (mkdir_p(conf, fname)) {
345 free(fname);
346 return -1;
347 }
348
349 fd = openat(conf->dd, fname, O_CREAT|O_RDWR|O_TRUNC,
350 0666 & ~conf->umask);
351 if (fd == -1) {
352 binlog_error("cannot create log file %s/%s: %s",
353 conf->dir, fname, strerror(errno));
354 free(fname);
355 }
356
357 conf->fname = fname;
358 conf->fd = fd;
359 return 0;
360}
361
362static void
363reset(struct binlog_config *conf)
364{
365 conf->fname = NULL;
366 conf->fd = -1;
367 conf->base = NULL;
368 conf->recbase = NULL;
369 conf->recnum = 0;
370 conf->recidx = 0;
371}
372
373static int
374setstoptime(struct binlog_config *conf)
375{
376 time_t ts;
377
378 ts = time(NULL);
379 conf->stoptime = ts - ts % conf->interval + conf->interval;
380}
381
382static int
383newfile(struct sess *sp, struct binlog_config *conf)
384{
385 int c;
386 void *base;
387
388 setstoptime(conf);
389
390 if (createfile(sp, conf))
391 return -1;
392 if (lseek(conf->fd, conf->size, SEEK_SET) == -1) {
393 binlog_error("seek in log file %s/%s failed: %s",
394 conf->dir, conf->fname, strerror(errno));
395 unlinkat(conf->dd, conf->fname, 0);
396 close(conf->fd);
397 free(conf->fname);
398 reset(conf);
399 return -1;
400 }
401 c = 0;
402 write(conf->fd, &c, 1);
403 base = mmap((caddr_t)0, conf->size,
404 PROT_READ|PROT_WRITE, MAP_SHARED,
405 conf->fd, 0);
406 if (base == MAP_FAILED) {
407 binlog_error("mmap: %s", strerror(errno));
408 unlinkat(conf->dd, conf->fname, 0);
409 close(conf->fd);
410 free(conf->fname);
411 reset(conf);
412 return -1;
413 }
414
415 conf->base = base;
416 memcpy(conf->base->hdr.magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN);
417 conf->base->hdr.version = BINLOG_VERSION;
418 conf->base->hdr.recsize = sizeof(struct binlog_record);
419 conf->base->hdr.recnum = 0;
420
421 conf->recbase = (struct binlog_record *) (conf->base + 1);
422 conf->recnum = binlog_recnum(conf);
423 conf->recidx = 0;
424
425 debug(conf,1,("created new log file %s",conf->fname));
426 return 0;
427}
428
429static void
430closefile(struct sess *sp, struct binlog_config *conf)
431{
432 if (conf->fd == -1)
433 return;
434 debug(conf,1,("closing log file %s",conf->fname));
435 munmap(conf->base, conf->size);
436 if (ftruncate(conf->fd, binlog_size(conf)))
437 binlog_error("error truncating \"%s/%s\": %s",
438 conf->dir, conf->fname, strerror(errno));
439 close(conf->fd);
440 free(conf->fname);
441 reset(conf);
442}
443
444
445void
446vmod_append(struct sess *sp, struct vmod_priv *priv, int nid, int aid)
447{
448 struct binlog_config *conf = priv->priv;
449 time_t ts;
450
451 if (!conf)
452 return;
453
454 ts = time(NULL);
455
456 if (ts >= conf->stoptime) {
457 AZ(pthread_mutex_lock(&conf->mutex));
458 closefile(sp, conf);
459 newfile(sp, conf);
460 AZ(pthread_mutex_unlock(&conf->mutex));
461 }
462 if (conf->fd == -1)
463 return;
464
465 AZ(pthread_mutex_lock(&conf->mutex));
466 if (conf->recidx == conf->recnum) {
467 binlog_error("overflow of %s/%s", conf->dir, conf->fname);
468 } else {
469 struct binlog_record *p = conf->recbase + conf->recidx++;
470 p->nid = nid;
471 p->aid = aid;
472 p->ts = ts;
473 conf->base->hdr.recnum++;
474 }
475 AZ(pthread_mutex_unlock(&conf->mutex));
476}
477
478void
479vmod_sappend(struct sess *sp, struct vmod_priv *priv, const char *nid,
480 const char *aid)
481{
482 vmod_append(sp, priv, nid ? atoi(nid): 0, aid ? atoi(aid) : 0);
483}
484
485void
486vmod_sync(struct sess *sp, struct vmod_priv *priv)
487{
488 struct binlog_config *conf = priv->priv;
489
490 if (!conf)
491 return;
492
493 AZ(pthread_mutex_lock(&conf->mutex));
494 if (conf->base)
495 msync(conf->base, binlog_size(conf), 0);
496 AZ(pthread_mutex_unlock(&conf->mutex));
497 }
498
499void
500vmod_close(struct sess *sp, struct vmod_priv *priv)
501{
502 struct binlog_config *conf = priv->priv;
503
504 if (!conf)
505 return;
506
507 mutex = conf->mutex;
508 AZ(pthread_mutex_lock(&mutex));
509 closefile(sp, conf);
510 close(conf->dd);
511 free(conf->dir);
512 free(conf->pattern);
513 free(conf);
514 AZ(pthread_mutex_unlock(&mutex));
515 }
516
diff --git a/src/binlogcat.c b/src/binlogcat.c
new file mode 100644
index 0000000..c51125b
--- a/dev/null
+++ b/src/binlogcat.c
@@ -0,0 +1,134 @@
1/* This file is part of vmod-binlog
2 Copyright (C) 2013 Sergey Poznyakoff
3
4 Vmod-binlog is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 Vmod-binlog is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with vmod-binlog. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <config.h>
19#include <unistd.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <time.h>
24#include <string.h>
25#include "vmod-binlog.h"
26
27char *progname;
28char *timefmt = "%c";
29int number_option;
30int verbose_option;
31
32void
33catlog(const char *fname)
34{
35 FILE *fp;
36 union binlog_header header;
37 struct binlog_record rec;
38 char timebuf[128];
39 size_t i;
40
41 if (strcmp(fname, "-") == 0)
42 fp = stdin;
43 else {
44 fp = fopen(fname, "r");
45 if (!fp) {
46 fprintf(stderr, "%s: cannot open %s: %s\n",
47 progname, fname, strerror(errno));
48 exit(1);
49 }
50 }
51
52 if (fread(&header, sizeof(header), 1, fp) != 1) {
53 fprintf(stderr, "%s: error reading header of %s: %s\n",
54 progname, fname, strerror(errno));
55 exit(1);
56 }
57
58 if (memcmp(header.hdr.magic, BINLOG_MAGIC_STR, BINLOG_MAGIC_LEN)) {
59 fprintf(stderr, "%s: %s is not a binlog file\n",
60 progname, fname);
61 exit(1);
62 }
63
64 if (header.hdr.version != BINLOG_VERSION) {
65 fprintf(stderr, "%s: %s: unknown version\n",
66 progname, fname);
67 exit(1);
68 }
69
70 if (header.hdr.recsize != sizeof(struct binlog_record)) {
71 fprintf(stderr, "%s: %s: record length mismatch\n",
72 progname, fname);
73 exit(1);
74 }
75
76 if (verbose_option)
77 printf("# %s; %lu records\n", fname, header.hdr.recnum);
78
79 for (i = 0; i < header.hdr.recnum; i++) {
80 if (fread(&rec, sizeof(rec), 1, fp) != 1) {
81 fprintf(stderr, "%s: %s: unexpected eof\n",
82 progname, fname);
83 break;
84 }
85
86 strftime(timebuf, sizeof timebuf, timefmt, localtime(&rec.ts));
87 if (number_option)
88 printf("%lu ", (unsigned long) i);
89 printf("%s %ld %ld\n", timebuf, rec.nid, rec.aid);
90 }
91
92 fclose(fp);
93}
94
95void
96help()
97{
98 printf("usage: %s [-hnv] [t FORMAT] [FILE...]\n");
99}
100
101int
102main(int argc, char **argv)
103{
104 progname = argv[0];
105 int c;
106
107 while ((c = getopt(argc, argv, "ht:nv")) != EOF)
108 switch (c) {
109 case 'h':
110 help();
111 return 0;
112 case 't':
113 timefmt = optarg;
114 break;
115 case 'n':
116 number_option = 1;
117 break;
118 case 'v':
119 verbose_option = 1;
120 break;
121 default:
122 exit(1);
123 }
124
125 argc -= optind;
126 argv += optind;
127
128 if (argc == 0)
129 catlog("-");
130 else while (argc--)
131 catlog(*(argv++));
132 return 0;
133}
134
diff --git a/src/vmod-binlog.h b/src/vmod-binlog.h
new file mode 100644
index 0000000..db6c7f0
--- a/dev/null
+++ b/src/vmod-binlog.h
@@ -0,0 +1,71 @@
1/* This file is part of vmod-binlog
2 Copyright (C) 2013 Sergey Poznyakoff
3
4 Vmod-binlog is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 Vmod-binlog is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with vmod-binlog. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include <stdint.h>
19
20#define SIZE_T_MAX ((size_t)~0)
21
22#ifndef BINLOG_SIZE
23# define BINLOG_SIZE (1024*1024*1024)
24#endif
25
26#ifndef BINLOG_PATTERN
27# define BINLOG_PATTERN "%Y/%m/%d.log"
28#endif
29
30#ifndef BINLOG_INTERVAL
31# define BINLOG_INTERVAL 86400
32#endif
33
34#ifndef BINLOG_UMASK
35# define BINLOG_UMASK 0077
36#endif
37
38struct binlog_record {
39 long nid; /* node ID */
40 long aid; /* article ID */
41 time_t ts; /* timestamp */
42};
43
44#define BINLOG_MAGIC_STR "NXCBINLOG"
45#define BINLOG_MAGIC_LEN (sizeof(BINLOG_MAGIC_STR) - 1)
46#define BINLOG_VERSION 0x00010000UL
47
48struct binlog_file_header {
49 char magic[BINLOG_MAGIC_LEN];
50 char pad[16 - BINLOG_MAGIC_LEN];
51 uint32_t version;
52 size_t recsize;
53 size_t recnum;
54};
55
56#define BINLOG_HEADER_SIZE \
57 ((sizeof(struct binlog_file_header) + sizeof(struct binlog_record) - 1) / \
58 sizeof(struct binlog_record))
59
60union binlog_header {
61 struct binlog_file_header hdr;
62 struct binlog_record pad[BINLOG_HEADER_SIZE];
63};
64
65#define binlog_size(conf) \
66 (sizeof(union binlog_header) + \
67 (conf)->recidx * sizeof(struct binlog_record))
68#define binlog_recnum(conf) \
69 (((conf)->size - sizeof(union binlog_header)) / \
70 sizeof(struct binlog_record))
71
diff --git a/src/vmod.vcc b/src/vmod.vcc
new file mode 100644
index 0000000..def0ec3
--- a/dev/null
+++ b/src/vmod.vcc
@@ -0,0 +1,7 @@
1Module binlog
2Init module_init
3Function VOID init(PRIV_VCL, STRING)
4Function VOID append(PRIV_VCL, INT, INT)
5Function VOID sappend(PRIV_VCL, STRING, STRING)
6Function VOID sync(PRIV_VCL)
7Function VOID close(PRIV_VCL)

Return to:

Send suggestions and report system problems to the System administrator.