Updated to current mtstdopen interface.
[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 dbclose(struct source *src)
107 {
108     struct dbsrc *d = src->pdata;
109     
110     d->db->close(d->db, 0);
111     d->env->close(d->env, 0);
112 }
113
114 static void enverror(const DB_ENV *env, const char *prefix, const char *msg)
115 {
116     flog(LOG_ERR, "dbsource: environment error: %s", msg);
117 }
118
119 struct source *mkdbsrc(char *path, char *envpath)
120 {
121     struct source *src;
122     struct dbsrc *d;
123     char *p;
124     int ret;
125     
126     omalloc(src);
127     src->serve = dbserve;
128     src->idle = dbidle;
129     src->close = dbclose;
130     src->pdata = omalloc(d);
131     if((ret = db_env_create(&d->env, 0)) != 0) {
132         flog(LOG_ERR, "could not create bdb environment: %s", db_strerror(ret));
133         goto fail;
134     }
135     if(envpath) {
136         d->envnm = sstrdup(envpath);
137     } else {
138         d->envnm = sstrdup(path);
139         if((p = strrchr(d->envnm, '/')) == NULL) {
140             free(d->envnm);
141             d->envnm = sstrdup(".");
142         } else {
143             *p = 0;
144         }
145     }
146     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) {
147         flog(LOG_ERR, "could not open bdb environment: %s", db_strerror(ret));
148         goto fail;
149     }
150     d->env->set_lk_detect(d->env, DB_LOCK_RANDOM);
151     d->env->set_errcall(d->env, enverror);
152     if((ret = db_create(&d->db, d->env, 0)) != 0) {
153         flog(LOG_ERR, "could not create bdb database: %s", db_strerror(ret));
154         goto fail;
155     }
156     if(dbpagesize) {
157         if((ret = d->db->set_pagesize(d->db, dbpagesize)) != 0) {
158             flog(LOG_ERR, "could not set bdb page size (to %i): %s", dbpagesize, db_strerror(ret));
159             goto fail;
160         }
161     }
162     if((ret = d->db->open(d->db, NULL, path, NULL, DB_HASH, DB_AUTO_COMMIT | DB_CREATE, 0666)) != 0) {
163         flog(LOG_ERR, "could not open bdb database: %s", db_strerror(ret));
164         goto fail;
165     }
166     return(src);
167     
168 fail:
169     if(d->db)
170         d->db->close(d->db, 0);
171     if(d->env)
172         d->env->close(d->env, 0);
173     free(d);
174     free(src);
175     return(NULL);
176 }