| 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 | } |