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