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 | ||
ea4e0b71 | 18 | static void opendb(char *path, int pagesize) |
34d725a5 FT |
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 | } | |
ea4e0b71 FT |
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 | } | |
34d725a5 FT |
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); | |
025221eb | 161 | return(rv); |
34d725a5 FT |
162 | } |
163 | ||
164 | static void usage(FILE *out) | |
165 | { | |
ea4e0b71 FT |
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"); | |
34d725a5 FT |
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; | |
ea4e0b71 | 175 | int pagesize; |
34d725a5 FT |
176 | |
177 | dm = ul = 0; | |
178 | name = NULL; | |
ea4e0b71 FT |
179 | pagesize = 0; |
180 | while((c = getopt(argc, argv, "+hvDdn:P:")) >= 0) { | |
34d725a5 FT |
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; | |
ea4e0b71 FT |
194 | case 'P': |
195 | pagesize = atoi(optarg); | |
196 | break; | |
34d725a5 FT |
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++]; | |
ea4e0b71 | 211 | opendb(dbpath, pagesize); |
34d725a5 FT |
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)); | |
e9b9b1bc FT |
215 | db->close(db, 0); |
216 | env->close(env, 0); | |
34d725a5 FT |
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)); | |
e9b9b1bc FT |
260 | db->close(db, 0); |
261 | env->close(env, 0); | |
34d725a5 FT |
262 | return(1); |
263 | } | |
e9b9b1bc FT |
264 | db->close(db, 0); |
265 | env->close(env, 0); | |
34d725a5 FT |
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 | } |