| 1 | #include <stdlib.h> |
| 2 | #include <stdio.h> |
| 3 | #include <unistd.h> |
| 4 | #include <string.h> |
| 5 | #include <errno.h> |
| 6 | #include <db.h> |
| 7 | #include <fcntl.h> |
| 8 | #include <dirent.h> |
| 9 | #include <sys/stat.h> |
| 10 | #include <ashd/utils.h> |
| 11 | |
| 12 | static DB_ENV *env; |
| 13 | static DB *db; |
| 14 | static DB_TXN *txn; |
| 15 | static struct charvbuf files; |
| 16 | static int verbose = 0; |
| 17 | |
| 18 | static void opendb(char *path, int pagesize) |
| 19 | { |
| 20 | char *envpath, *p; |
| 21 | int ret; |
| 22 | |
| 23 | envpath = strdup(path); |
| 24 | if((p = strrchr(envpath, '/')) == NULL) { |
| 25 | free(envpath); |
| 26 | envpath = strdup("."); |
| 27 | } else { |
| 28 | *p = 0; |
| 29 | } |
| 30 | if((ret = db_env_create(&env, 0)) != 0) { |
| 31 | fprintf(stderr, "statdbput: could not create db environment handle: %s\n", db_strerror(ret)); |
| 32 | exit(1); |
| 33 | } |
| 34 | if((ret = env->open(env, envpath, DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN, 0666)) != 0) { |
| 35 | fprintf(stderr, "statdbput: environment %s: %s\n", envpath, db_strerror(ret)); |
| 36 | exit(1); |
| 37 | } |
| 38 | env->set_lk_detect(env, DB_LOCK_RANDOM); |
| 39 | if((ret = db_create(&db, env, 0)) != 0) { |
| 40 | fprintf(stderr, "statdbput: could not create db handle: %s\n", db_strerror(ret)); |
| 41 | exit(1); |
| 42 | } |
| 43 | if(pagesize) { |
| 44 | if((ret = db->set_pagesize(db, pagesize)) != 0) { |
| 45 | fprintf(stderr, "statdbput: could not set db page size (to %i): %s", pagesize, db_strerror(ret)); |
| 46 | exit(1); |
| 47 | } |
| 48 | } |
| 49 | if((ret = db->open(db, NULL, path, NULL, DB_HASH, DB_AUTO_COMMIT | DB_CREATE, 0666)) != 0) { |
| 50 | fprintf(stderr, "statdbput: %s: %s\n", path, db_strerror(ret)); |
| 51 | exit(1); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | static void bufcatbe(struct charbuf *buf, intmax_t num, int nb) |
| 56 | { |
| 57 | if(nb < 1) |
| 58 | return; |
| 59 | bufcatbe(buf, num >> 8, nb - 1); |
| 60 | bufadd(*buf, (uint8_t)(num & 0xff)); |
| 61 | } |
| 62 | |
| 63 | static int dofile2(int fd, char *name, time_t mtime, char *ctype) |
| 64 | { |
| 65 | int ret; |
| 66 | DBT k, v; |
| 67 | struct charbuf buf; |
| 68 | |
| 69 | bufinit(buf); |
| 70 | bufadd(buf, 1); |
| 71 | bufcatbe(&buf, mtime, 8); |
| 72 | bufcatstr2(buf, ctype); |
| 73 | while(1) { |
| 74 | sizebuf(buf, buf.d + 4096); |
| 75 | if((ret = read(fd, buf.b + buf.d, buf.s - buf.d)) < 0) { |
| 76 | fprintf(stderr, "statdbput: %s: %s\n", name, strerror(errno)); |
| 77 | buffree(buf); |
| 78 | return(1); |
| 79 | } |
| 80 | if(ret == 0) |
| 81 | break; |
| 82 | buf.d += ret; |
| 83 | } |
| 84 | k = (DBT){.data = name, .size = strlen(name)}; |
| 85 | v = (DBT){.data = buf.b, .size = buf.d}; |
| 86 | ret = db->put(db, txn, &k, &v, 0); |
| 87 | buffree(buf); |
| 88 | if(ret) { |
| 89 | if(ret == DB_LOCK_DEADLOCK) |
| 90 | return(2); |
| 91 | fprintf(stderr, "statdbput: %s: %s\n", name, db_strerror(ret)); |
| 92 | return(1); |
| 93 | } else { |
| 94 | if(verbose) |
| 95 | fprintf(stderr, "put: %s\n", name); |
| 96 | } |
| 97 | return(0); |
| 98 | } |
| 99 | |
| 100 | static int dofile(char *path, char *name, char *ctype) |
| 101 | { |
| 102 | int fd, ret; |
| 103 | char *p; |
| 104 | struct stat sb; |
| 105 | |
| 106 | if((fd = open(path, O_RDONLY)) < 0) { |
| 107 | fprintf(stderr, "statdbput: %s: %s\n", path, strerror(errno)); |
| 108 | return(1); |
| 109 | } |
| 110 | if(fstat(fd, &sb)) { |
| 111 | fprintf(stderr, "statdbput: %s: %s\n", path, strerror(errno)); |
| 112 | close(fd); |
| 113 | return(1); |
| 114 | } |
| 115 | if(name == NULL) { |
| 116 | if((p = strrchr(path, '/')) != NULL) |
| 117 | name = p + 1; |
| 118 | else |
| 119 | name = path; |
| 120 | } |
| 121 | ret = dofile2(fd, name, sb.st_mtime, ctype); |
| 122 | close(fd); |
| 123 | if(!ret) |
| 124 | bufadd(files, sstrdup(path)); |
| 125 | return(ret); |
| 126 | } |
| 127 | |
| 128 | static int dodir(char *path, char *ctype) |
| 129 | { |
| 130 | int rv, ret; |
| 131 | DIR *dir; |
| 132 | struct stat sb; |
| 133 | struct dirent *dent; |
| 134 | struct charbuf fnbuf; |
| 135 | |
| 136 | if((dir = opendir(path)) == NULL) { |
| 137 | fprintf(stderr, "statdbput: %s: %s\n", path, strerror(errno)); |
| 138 | return(1); |
| 139 | } |
| 140 | rv = 0; |
| 141 | bufinit(fnbuf); |
| 142 | while((dent = readdir(dir)) != NULL) { |
| 143 | fnbuf.d = 0; |
| 144 | bprintf(&fnbuf, "%s/%s", path, dent->d_name); |
| 145 | bufadd(fnbuf, 0); |
| 146 | if(stat(fnbuf.b, &sb)) { |
| 147 | fprintf(stderr, "statdbput: %s: %s\n", fnbuf.b, strerror(errno)); |
| 148 | rv = 1; |
| 149 | continue; |
| 150 | } |
| 151 | if(S_ISREG(sb.st_mode)) { |
| 152 | ret = dofile(fnbuf.b, NULL, ctype); |
| 153 | if(ret == 2) |
| 154 | return(2); |
| 155 | else if(ret == 1) |
| 156 | rv = 1; |
| 157 | } |
| 158 | } |
| 159 | buffree(fnbuf); |
| 160 | closedir(dir); |
| 161 | return(0); |
| 162 | } |
| 163 | |
| 164 | static void usage(FILE *out) |
| 165 | { |
| 166 | fprintf(out, "usage: statdbput [-hvD] [-P PAGESIZE] [-n NAME] DB CONTENT-TYPE {FILE|-}...\n"); |
| 167 | fprintf(out, " statdbput [-hvD] [-P PAGESIZE] [-d] DB CONTENT-TYPE DIR...\n"); |
| 168 | } |
| 169 | |
| 170 | int main(int argc, char **argv) |
| 171 | { |
| 172 | int c, rv, ret; |
| 173 | int dm, ul, i, a; |
| 174 | char *name, *ctype, *dbpath; |
| 175 | int pagesize; |
| 176 | |
| 177 | dm = ul = 0; |
| 178 | name = NULL; |
| 179 | pagesize = 0; |
| 180 | while((c = getopt(argc, argv, "+hvDdn:P:")) >= 0) { |
| 181 | switch(c) { |
| 182 | case 'd': |
| 183 | dm = 1; |
| 184 | break; |
| 185 | case 'D': |
| 186 | ul = 1; |
| 187 | break; |
| 188 | case 'v': |
| 189 | verbose++; |
| 190 | break; |
| 191 | case 'n': |
| 192 | name = optarg; |
| 193 | break; |
| 194 | case 'P': |
| 195 | pagesize = atoi(optarg); |
| 196 | break; |
| 197 | case 'h': |
| 198 | usage(stdout); |
| 199 | return(0); |
| 200 | default: |
| 201 | usage(stderr); |
| 202 | return(1); |
| 203 | } |
| 204 | } |
| 205 | if(optind > argc - 3) { |
| 206 | usage(stderr); |
| 207 | return(1); |
| 208 | } |
| 209 | dbpath = argv[optind++]; |
| 210 | ctype = argv[optind++]; |
| 211 | opendb(dbpath, pagesize); |
| 212 | while(1) { |
| 213 | if((ret = env->txn_begin(env, NULL, &txn, 0)) != 0) { |
| 214 | fprintf(stderr, "statdbput: could not begin transaction in %s: %s\n", dbpath, db_strerror(ret)); |
| 215 | db->close(db, 0); |
| 216 | env->close(env, 0); |
| 217 | return(1); |
| 218 | } |
| 219 | rv = 0; |
| 220 | a = optind; |
| 221 | if(dm) { |
| 222 | for(a = optind; a < argc; a++) { |
| 223 | ret = dodir(argv[a], ctype); |
| 224 | if(ret == 2) |
| 225 | break; |
| 226 | else if(ret) |
| 227 | rv = 1; |
| 228 | } |
| 229 | } else { |
| 230 | for(a = optind; a < argc; a++) { |
| 231 | if(!strcmp(argv[a], "-")) { |
| 232 | if(name == NULL) { |
| 233 | fprintf(stderr, "statdbput: must give -n when putting stdin\n"); |
| 234 | ret = 1; |
| 235 | } else { |
| 236 | ret = dofile2(0, name, time(NULL), ctype); |
| 237 | } |
| 238 | } else { |
| 239 | ret = dofile(argv[a], name, ctype); |
| 240 | } |
| 241 | if(ret == 2) |
| 242 | break; |
| 243 | else if(ret) |
| 244 | rv = 1; |
| 245 | } |
| 246 | } |
| 247 | if(ret == 2) { |
| 248 | if(verbose) |
| 249 | fprintf(stderr, "deadlocked, restarting and trying again\n"); |
| 250 | for(i = 0; i < files.d; i++) |
| 251 | free(files.b[i]); |
| 252 | files.d = 0; |
| 253 | txn->abort(txn); |
| 254 | continue; |
| 255 | } |
| 256 | break; |
| 257 | } |
| 258 | if((ret = txn->commit(txn, 0)) != 0) { |
| 259 | fprintf(stderr, "statdbput: could not commit transaction in %s: %s\n", dbpath, db_strerror(ret)); |
| 260 | db->close(db, 0); |
| 261 | env->close(env, 0); |
| 262 | return(1); |
| 263 | } |
| 264 | db->close(db, 0); |
| 265 | env->close(env, 0); |
| 266 | if(ul) { |
| 267 | for(i = 0; i < files.d; i++) { |
| 268 | if(verbose) |
| 269 | fprintf(stderr, "unlink %s\n", files.b[i]); |
| 270 | if(unlink(files.b[i])) |
| 271 | fprintf(stderr, "statdbput: unlink %s: %s\n", files.b[i], strerror(errno)); |
| 272 | } |
| 273 | } |
| 274 | return(rv); |
| 275 | } |