Added the ability to specify database page size.
[statserve.git] / dbsrc.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <db.h>
4 #include <string.h>
5 #include <ashd/utils.h>
6 #include <ashd/log.h>
7 #include <time.h>
8
9 #include "statserve.h"
10
11 struct dbsrc {
12     char *envnm;
13     DB_ENV *env;
14     DB *db;
15     time_t lastcp, lastar;
16 };
17
18 int dbpagesize = 0;
19
20 static struct fileinfo dbserve(struct source *src, char *nm)
21 {
22     struct dbsrc *d = src->pdata;
23     int i, ret, ver;
24     DBT k, v;
25     size_t sz;
26     void *hp, *p;
27     struct fileinfo retf;
28     
29     memset(&k, 0, sizeof(k));
30     memset(&v, 0, sizeof(v));
31     k.size = strlen(k.data = nm);
32     v.flags = DB_DBT_MALLOC;
33     do {
34         ret = d->db->get(d->db, NULL, &k, &v, 0);
35     } while(ret == DB_LOCK_DEADLOCK);
36     if(ret == 0) {
37         hp = v.data;
38         sz = v.size;
39         if(sz < 1)
40             goto corrupt;
41         ver = *(uint8_t *)hp;
42         hp++; sz--;
43         if(ver == 1) {
44             if(sz < 8)
45                 goto corrupt;
46             memset(&retf, 0, sizeof(retf));
47             for(i = 0, retf.mtime = 0; i < 8; i++) {
48                 retf.mtime = (retf.mtime << 8) | (*(uint8_t *)hp);
49                 hp++; sz--;
50             }
51             if(retf.mtime < 0)
52                 goto corrupt;
53             if((p = memchr(hp, 0, sz)) == NULL)
54                 goto corrupt;
55             p++;
56             if(p - hp > 64)
57                 goto corrupt;
58             strcpy(retf.ctype, hp);
59             sz -= p - hp; hp = p;
60             retf.data = memcpy(smalloc(retf.sz = sz), hp, sz);
61             free(v.data);
62             return(retf);
63         } else {
64             goto corrupt;
65         }
66     } else if(ret == DB_NOTFOUND) {
67         return((struct fileinfo){});
68     } else {
69         flog(LOG_ERR, "could not read value of %s in %s: %s", nm, d->envnm, db_strerror(ret));
70         return((struct fileinfo){});
71     }
72     goto out;
73
74 corrupt:
75     flog(LOG_ERR, "entry for %s in %s is corrupted", nm, d->envnm);
76     free(v.data);
77     return((struct fileinfo){});
78 out:;
79 }
80
81 static void dbidle(struct source *src)
82 {
83     struct dbsrc *d = src->pdata;
84     time_t now;
85     int ret;
86     char **files;
87     
88     now = time(NULL);
89     if(now - d->lastcp > 1800) {
90         d->lastcp = now;
91         if((ret = d->env->txn_checkpoint(d->env, 5000, 0, 0)) != 0) {
92             flog(LOG_ERR, "could not make db checkpoint in %s: %s", d->envnm, db_strerror(ret));
93         }
94     }
95     if(now - d->lastar > 7200) {
96         d->lastar = now;
97         files = NULL;
98         if((ret = d->env->log_archive(d->env, &files, DB_ARCH_REMOVE)) != 0) {
99             flog(LOG_ERR, "could not archive log files in %s: %s", d->envnm, db_strerror(ret));
100         }
101         if(files != NULL)
102             free(files);
103     }
104 }
105
106 static void enverror(const DB_ENV *env, const char *prefix, const char *msg)
107 {
108     flog(LOG_ERR, "dbsource: environment error: %s", msg);
109 }
110
111 struct source *mkdbsrc(char *path, char *envpath)
112 {
113     struct source *src;
114     struct dbsrc *d;
115     char *p;
116     int ret;
117     
118     omalloc(src);
119     src->serve = dbserve;
120     src->idle = dbidle;
121     src->pdata = omalloc(d);
122     if((ret = db_env_create(&d->env, 0)) != 0) {
123         flog(LOG_ERR, "could not create bdb environment: %s", db_strerror(ret));
124         exit(1);
125     }
126     if(envpath) {
127         d->envnm = sstrdup(envpath);
128     } else {
129         d->envnm = sstrdup(path);
130         if((p = strrchr(d->envnm, '/')) == NULL) {
131             free(d->envnm);
132             d->envnm = sstrdup(".");
133         } else {
134             *p = 0;
135         }
136     }
137     if((ret = d->env->open(d->env, d->envnm, DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN, 0666)) != 0) {
138         flog(LOG_ERR, "could not open bdb environment: %s", db_strerror(ret));
139         exit(1);
140     }
141     d->env->set_lk_detect(d->env, DB_LOCK_RANDOM);
142     d->env->set_errcall(d->env, enverror);
143     if((ret = db_create(&d->db, d->env, 0)) != 0) {
144         flog(LOG_ERR, "could not create bdb database: %s", db_strerror(ret));
145         exit(1);
146     }
147     if(dbpagesize) {
148         if((ret = d->db->set_pagesize(d->db, dbpagesize)) != 0) {
149             flog(LOG_ERR, "could not set bdb page size (to %i): %s", dbpagesize, db_strerror(ret));
150             exit(1);
151         }
152     }
153     if((ret = d->db->open(d->db, NULL, path, NULL, DB_HASH, DB_AUTO_COMMIT | DB_CREATE, 0666)) != 0) {
154         flog(LOG_ERR, "could not open bdb database: %s", db_strerror(ret));
155         exit(1);
156     }
157     return(src);
158 }