d33480ce8fe59645e05f04432eff07713dca2f65
[samba.git] / ctdb / tools / ltdbtool.c
1 /*
2  * ctdb local tdb tool
3  *
4  * Copyright (C) Gregor Beck 2011
5  * Copyright (C) Michael Adam 2011
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "replace.h"
22 #include "system/filesys.h"
23 #include "system/network.h"
24 #include "system/locale.h"
25
26 #include <tdb.h>
27
28 #include "protocol/protocol.h"
29
30 enum {
31         MAX_HEADER_SIZE=24,
32         OUT_MODE = S_IRUSR | S_IWUSR,
33         OUT_FLAGS = O_EXCL|O_CREAT|O_RDWR,
34 };
35
36 union  ltdb_header {
37         struct ctdb_ltdb_header hdr;
38         uint32_t uints[MAX_HEADER_SIZE/4];
39 };
40
41 static const union ltdb_header DEFAULT_HDR = {
42         .hdr = {
43                 .dmaster = -1,
44         }
45 };
46
47 static int help(const char* cmd)
48 {
49         fprintf(stdout, ""
50 "Usage: %s [options] <command>\n"
51 "\n"
52 "Options:\n"
53 "   -s {0|32|64}    specify how to determine the ctdb record header size\n"
54 "                   for the input database:\n"
55 "                   0: no ctdb header\n"
56 "                   32: ctdb header size of a 32 bit system (20 bytes)\n"
57 "                   64: ctdb header size of a 64 bit system (24 bytes)\n"
58 "                   default: 32 or 64 depending on the system architecture\n"
59 "\n"
60 "   -S <num>        the number of bytes to interpret as ctdb record header\n"
61 "                   for the input database (beware!)\n"
62 "\n"
63 "   -o {0|32|64}    specify how to determine the ctdb record header size\n"
64 "                   for the output database\n"
65 "                   0: no ctdb header\n"
66 "                   32: ctdb header size of a 32 bit system (20 bytes)\n"
67 "                   64: ctdb header size of a 64 bit system (24 bytes)\n"
68 "                   default: 32 or 64 depending on the system architecture\n"
69 "\n"
70 "   -O <num>        the number of bytes to interpret as ctdb record header\n"
71 "                   for the output database (beware!)\n"
72 "\n"
73 "   -e              Include empty records, defaults to off\n"
74 "\n"
75 "   -p              print header (for the dump command), defaults to off\n"
76 "\n"
77 "   -h              print this help\n"
78 "\n"
79 "Commands:\n"
80 "  help                         print this help\n"
81 "  dump <db>                    dump the db to stdout\n"
82 "  convert <in_db> <out_db>     convert the db\n\n", cmd);
83         return 0;
84 }
85
86 static int usage(const char* cmd)
87 {
88         fprintf(stderr,
89                 "Usage: %s dump [-e] [-p] [-s{0|32|64}] <idb>\n"
90                 "       %s convert [-e] [-s{0|32|64}] [-o{0|32|64}] <idb> <odb>\n"
91                 "       %s {help|-h}\n"
92                 , cmd, cmd, cmd);
93         return -1;
94 }
95
96 static int
97 ltdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT*, TDB_DATA, TDB_DATA,
98                                           struct ctdb_ltdb_header*, void *),
99               void *state, int hsize, bool skip_empty);
100
101 struct write_record_ctx {
102         TDB_CONTEXT* tdb;
103         size_t hsize;
104         int tdb_store_flags;
105 };
106
107 static int
108 write_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
109              struct ctdb_ltdb_header* hdr,
110              void* write_record_ctx);
111
112
113 struct dump_record_ctx {
114         FILE* file;
115         void (*print_data)(FILE*, TDB_DATA);
116         void (*dump_header)(struct dump_record_ctx*, struct ctdb_ltdb_header*);
117 };
118
119 static int dump_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
120                        struct ctdb_ltdb_header* hdr,
121                        void* dump_record_ctx);
122 static void print_data_tdbdump(FILE* file, TDB_DATA data);
123 static void dump_header_full(struct dump_record_ctx*, struct ctdb_ltdb_header*);
124 static void dump_header_nop(struct dump_record_ctx* c,
125                             struct ctdb_ltdb_header* h)
126 {}
127
128 static int dump_db(const char* iname, FILE* ofile, int hsize, bool dump_header,
129                    bool empty)
130 {
131         int ret = -1;
132         TDB_CONTEXT* idb = tdb_open(iname, 0, TDB_DEFAULT, O_RDONLY, 0);
133         if (!idb) {
134                 perror("tdbopen in");
135         } else {
136                 struct dump_record_ctx dump_ctx = {
137                         .file = ofile,
138                         .print_data =  &print_data_tdbdump,
139                         .dump_header = dump_header ? &dump_header_full
140                                                    : &dump_header_nop,
141                 };
142                 ret = ltdb_traverse(idb, &dump_record, &dump_ctx, hsize, !empty);
143                 tdb_close(idb);
144         }
145         return ret;
146 }
147
148 static int conv_db(const char* iname, const char* oname, size_t isize,
149                    size_t osize, bool keep_empty)
150 {
151         int ret = -1;
152         TDB_CONTEXT* idb = tdb_open(iname, 0, TDB_DEFAULT, O_RDONLY, 0);
153         if (!idb) {
154                 perror("tdbopen in");
155         } else {
156                 TDB_CONTEXT* odb = tdb_open(oname, 0, TDB_DEFAULT, OUT_FLAGS, OUT_MODE);
157                 if (!odb) {
158                         perror("tdbopen out");
159                 } else {
160                         struct write_record_ctx ctx = {
161                                 .tdb = odb,
162                                 .hsize = osize,
163                                 .tdb_store_flags = TDB_REPLACE,
164                         };
165                         ret = ltdb_traverse(idb, &write_record, &ctx, isize, !keep_empty);
166                         tdb_close(odb);
167                 }
168                 tdb_close(idb);
169         }
170         return ret;
171 }
172
173 static bool parse_size(size_t* size, const char* arg, bool raw) {
174         long val;
175         errno = 0;
176         val = strtol(arg, (char **) NULL, 10);
177         if (errno != 0) {
178                 return false;
179         }
180         if (!raw) {
181                 switch(val) {
182                 case 0:
183                         break;
184                 case 32:
185                         val = 20;
186                         break;
187                 case 64:
188                         val = 24;
189                         break;
190                 default:
191                         return false;
192                 }
193         }
194         *size = MIN(val, MAX_HEADER_SIZE);
195         return true;
196 }
197
198
199 int main(int argc, char* argv[])
200 {
201         size_t isize = sizeof(struct ctdb_ltdb_header);
202         size_t osize = sizeof(struct ctdb_ltdb_header);
203         bool print_header = false;
204         bool keep_empty = false;
205         int opt;
206         const char *cmd, *idb, *odb;
207
208         while ((opt = getopt(argc, argv, "s:o:S:O:phe")) != -1) {
209                 switch (opt) {
210                 case 's':
211                 case 'S':
212                         if (!parse_size(&isize, optarg, isupper(opt))) {
213                                 return usage(argv[0]);
214                         }
215                         break;
216                 case 'o':
217                 case 'O':
218                         if (!parse_size(&osize, optarg, isupper(opt))) {
219                                 return usage(argv[0]);
220                         }
221                         break;
222                 case 'p':
223                         print_header = true;
224                         break;
225                 case 'e':
226                         keep_empty = true;
227                         break;
228                 case 'h':
229                         return help(argv[0]);
230                 default:
231                         return usage(argv[0]);
232                 }
233         }
234
235         if (argc - optind < 1) {
236                 return usage(argv[0]);
237         }
238
239         cmd = argv[optind];
240
241         if (strcmp(cmd, "help") == 0) {
242                 return help(argv[0]);
243         }
244         else if (strcmp(cmd, "dump") == 0) {
245                 int ret;
246                 if (argc - optind != 2) {
247                         return usage(argv[0]);
248                 }
249                 idb = argv[optind+1];
250                 ret = dump_db(idb, stdout, isize, print_header, keep_empty);
251                 return (ret >= 0) ? 0 : ret;
252         }
253         else if (strcmp(cmd, "convert") == 0) {
254                 int ret;
255                 if (argc - optind != 3) {
256                         return usage(argv[0]);
257                 }
258                 idb = argv[optind+1];
259                 odb = argv[optind+2];
260                 ret = conv_db(idb, odb, isize, osize, keep_empty);
261                 return (ret >= 0) ? 0 : ret;
262         }
263
264         return usage(argv[0]);
265 }
266
267 struct ltdb_traverse_ctx {
268         int (*fn)(TDB_CONTEXT*,TDB_DATA,TDB_DATA,struct ctdb_ltdb_header*,void *);
269         void* state;
270         size_t hsize;
271         bool skip_empty;
272         int nempty;
273 };
274
275 static int
276 ltdb_traverse_fn(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
277                  void* ltdb_traverse_ctx)
278 {
279         struct ltdb_traverse_ctx* ctx =
280                 (struct ltdb_traverse_ctx*)ltdb_traverse_ctx;
281         union ltdb_header hdr = DEFAULT_HDR;
282
283         const size_t hsize = MIN(sizeof(hdr), ctx->hsize);
284         if (val.dsize < hsize) {
285                 fprintf(stderr, "Value too short to contain a ctdb header: ");
286                 print_data_tdbdump(stderr, key);
287                 fprintf(stderr, " = ");
288                 print_data_tdbdump(stderr, val);
289                 fputc('\n', stderr);
290                 return -1;
291         }
292         if (val.dsize == hsize && ctx->skip_empty) {
293                 ctx->nempty++;
294                 return 0;
295         }
296
297         memcpy(&hdr, val.dptr, hsize);
298
299         if (hdr.uints[5] != 0) {
300                 fprintf(stderr, "Warning: header padding isn't zero! Wrong header size?\n");
301         }
302         val.dptr += ctx->hsize;
303         val.dsize -= ctx->hsize;
304         return ctx->fn(tdb, key, val, &hdr.hdr, ctx->state);
305 }
306
307 static int ltdb_traverse(TDB_CONTEXT *tdb,
308                          int (*fn)(TDB_CONTEXT*, TDB_DATA, TDB_DATA,
309                                    struct ctdb_ltdb_header*, void *),
310                          void *state, int hsize, bool skip_empty)
311 {
312         struct ltdb_traverse_ctx ctx = {
313                 .fn = fn,
314                 .state = state,
315                 .hsize = hsize < 0 ? sizeof(struct ctdb_ltdb_header) : hsize,
316                 .skip_empty = skip_empty,
317                 .nempty = 0,
318         };
319         int ret = tdb_traverse(tdb, &ltdb_traverse_fn, &ctx);
320
321         return (ret < 0) ? ret : (ret - ctx.nempty);
322 }
323
324 static int write_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
325                         struct ctdb_ltdb_header* hdr,
326                         void* write_record_ctx)
327 {
328         struct write_record_ctx* ctx
329                 = (struct write_record_ctx*)write_record_ctx;
330         int ret;
331
332         if (ctx->hsize == 0) {
333                 ret = tdb_store(ctx->tdb, key, val, ctx->tdb_store_flags);
334         } else {
335                 TDB_DATA rec[2];
336
337                 rec[0].dsize = ctx->hsize;
338                 rec[0].dptr = (uint8_t *)hdr;
339
340                 rec[1].dsize = val.dsize;
341                 rec[1].dptr = val.dptr;
342
343                 ret = tdb_storev(ctx->tdb, key, rec, 2, ctx->tdb_store_flags);
344         }
345
346         if (ret == -1) {
347                 fprintf(stderr, "tdb_store: %s\n", tdb_errorstr(ctx->tdb));
348                 return -1;
349         }
350
351         return 0;
352 }
353
354 static int dump_record(TDB_CONTEXT* tdb, TDB_DATA key, TDB_DATA val,
355                        struct ctdb_ltdb_header* hdr,
356                        void* dump_record_ctx)
357 {
358         struct dump_record_ctx* ctx = (struct dump_record_ctx*)dump_record_ctx;
359
360         fprintf(ctx->file, "{\nkey(%d) = ", (int)key.dsize);
361         ctx->print_data(ctx->file, key);
362         fputc('\n', ctx->file);
363         ctx->dump_header(ctx, hdr);
364         fprintf(ctx->file, "data(%d) = ", (int)val.dsize);
365         ctx->print_data(ctx->file, val);
366         fprintf(ctx->file, "\n}\n");
367         return 0;
368 }
369
370 static void dump_header_full(struct dump_record_ctx* c,
371                              struct ctdb_ltdb_header* h)
372 {
373         fprintf(c->file, "dmaster: %d\nrsn: %llu\nflags: 0x%X\n",
374                 (int)h->dmaster,
375                 (unsigned long long)h->rsn, h->flags);
376 }
377
378 static void print_data_tdbdump(FILE* file, TDB_DATA data)
379 {
380         unsigned char *ptr = data.dptr;
381         fputc('"', file);
382         while (data.dsize--) {
383                 if (isprint(*ptr) && !strchr("\"\\", *ptr)) {
384                         fputc(*ptr, file);
385                 } else {
386                         fprintf(file, "\\%02X", *ptr);
387                 }
388                 ptr++;
389         }
390         fputc('"',file);
391 }
392