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