Initial commit.
[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
18static 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
49static 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
57static 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
94static 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
122static 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
158static 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
164int 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}