Updated to current mtstdopen interface.
[statserve.git] / statdbput.c
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(rv);
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 }