Updated to current mtstdopen interface.
[statserve.git] / statdbput.c
CommitLineData
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
12static DB_ENV *env;
13static DB *db;
14static DB_TXN *txn;
15static struct charvbuf files;
16static int verbose = 0;
17
ea4e0b71 18static 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
55static 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
63static 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
100static 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
128static 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
164static 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
170int 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}