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