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