ldb: Add ldbdump, based on tdbdump
[samba.git] / lib / ldb / tools / ldbdump.c
1 /*
2    Unix SMB/CIFS implementation.
3    simple ldb tdb dump util
4    Copyright (C) Andrew Tridgell              2001
5    Copyright (C) Andrew Bartlett              2012
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/locale.h"
23 #include "system/time.h"
24 #include "system/filesys.h"
25 #include "system/wait.h"
26 #include <tdb.h>
27 #include <ldb.h>
28 #include "../ldb_tdb/ldb_tdb.h"
29
30 static struct ldb_context *ldb;
31 bool show_index = false;
32 bool validate_contents = false;
33
34 static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
35 {
36         int ret, i, j;
37         struct ldb_dn *dn = state;
38         struct ldb_message *msg = talloc_zero(NULL, struct ldb_message);
39         struct ldb_ldif ldif = {
40                 .msg = msg,
41                 .changetype = LDB_CHANGETYPE_NONE
42         };
43         if (!msg) {
44                 return -1;
45         }
46         ret = ltdb_unpack_data(ldb, &dbuf, msg);
47         if (ret != 0) {
48                 fprintf(stderr, "Failed to parse record %*.*s as an LDB record\n", (int)key.dsize, (int)key.dsize, (char *)key.dptr);
49                 TALLOC_FREE(msg);
50                 return 0;
51         }
52
53         if (dn && ldb_dn_compare(msg->dn, dn) != 0) {
54                 TALLOC_FREE(msg);
55                 return 0;
56         }
57
58         if (!show_index && ldb_dn_is_special(msg->dn)) {
59                 const char *dn_lin = ldb_dn_get_linearized(msg->dn);
60                 if ((strcmp(dn_lin, LTDB_BASEINFO) == 0) || (strncmp(dn_lin, LTDB_INDEX ":", strlen( LTDB_INDEX ":")) == 0)) {
61                         TALLOC_FREE(msg);
62                         return 0;
63                 }
64         }
65
66         if (!validate_contents || ldb_dn_is_special(msg->dn)) {
67                 ldb_ldif_write_file(ldb, stdout, &ldif);
68                 TALLOC_FREE(msg);
69                 return 0;
70         }
71
72         for (i=0;i<msg->num_elements;i++) {
73                 const struct ldb_schema_attribute *a;
74
75                 a = ldb_schema_attribute_by_name(ldb, msg->elements[i].name);
76                 for (j=0;j<msg->elements[i].num_values;j++) {
77                         struct ldb_val v;
78                         ret = a->syntax->ldif_write_fn(ldb, msg, &msg->elements[i].values[j], &v);
79                         if (ret != 0) {
80                                 v = msg->elements[i].values[j];
81                                 if (ldb_should_b64_encode(ldb, &v)) {
82                                         v.data = (uint8_t *)ldb_base64_encode(ldb, (char *)v.data, v.length);
83                                         v.length = strlen((char *)v.data);
84                                 }
85                                 fprintf(stderr, "On %s element %s value %d (%*.*s) failed to convert to LDIF correctly, skipping possibly corrupt record\n",
86                                         ldb_dn_get_linearized(msg->dn),
87                                         msg->elements[i].name,
88                                         j, (int)v.length, (int)v.length,
89                                         v.data);
90                                 TALLOC_FREE(msg);
91                                 return 0;
92                         }
93                 }
94         }
95         ldb_ldif_write_file(ldb, stdout, &ldif);
96         TALLOC_FREE(msg);
97
98         return 0;
99 }
100
101 static void log_stderr(struct tdb_context *tdb, enum tdb_debug_level level,
102                        const char *fmt, ...)
103 {
104         va_list ap;
105         const char *name = tdb_name(tdb);
106         const char *prefix = "";
107
108         if (!name)
109                 name = "unnamed";
110
111         switch (level) {
112         case TDB_DEBUG_ERROR:
113                 prefix = "ERROR: ";
114                 break;
115         case TDB_DEBUG_WARNING:
116                 prefix = "WARNING: ";
117                 break;
118         case TDB_DEBUG_TRACE:
119                 return;
120
121         default:
122         case TDB_DEBUG_FATAL:
123                 prefix = "FATAL: ";
124                 break;
125         }
126
127         va_start(ap, fmt);
128         fprintf(stderr, "tdb(%s): %s", name, prefix);
129         vfprintf(stderr, fmt, ap);
130         va_end(ap);
131 }
132
133 static void emergency_walk(TDB_DATA key, TDB_DATA dbuf, void *keyname)
134 {
135         traverse_fn(NULL, key, dbuf, keyname);
136 }
137
138 static int dump_tdb(const char *fname, struct ldb_dn *dn, bool emergency)
139 {
140         TDB_CONTEXT *tdb;
141         struct tdb_logging_context logfn = { log_stderr };
142
143         tdb = tdb_open_ex(fname, 0, 0, O_RDONLY, 0, &logfn, NULL);
144         if (!tdb) {
145                 fprintf(stderr, "Failed to open %s\n", fname);
146                 return 1;
147         }
148
149         if (emergency) {
150                 return tdb_rescue(tdb, emergency_walk, dn) == 0;
151         }
152         return tdb_traverse(tdb, traverse_fn, dn) == -1 ? 1 : 0;
153 }
154
155 static void usage( void)
156 {
157         printf( "Usage: tdbdump [options] <filename>\n\n");
158         printf( "   -h          this help message\n");
159         printf( "   -d DN       dumps DN only\n");
160         printf( "   -e          emergency dump, for corrupt databases\n");
161         printf( "   -i          include index and @BASEINFO records in dump\n");
162         printf( "   -c          validate contents of the records\n");
163 }
164
165  int main(int argc, char *argv[])
166 {
167         bool emergency = false;
168         int c, rc;
169         char *fname;
170         struct ldb_dn *dn = NULL;
171
172         ldb = ldb_init(NULL, NULL);
173         if (ldb == NULL) {
174                 fprintf(stderr, "ldb: ldb_init failed()");
175                 exit(1);
176         }
177
178         rc = ldb_modules_hook(ldb, LDB_MODULE_HOOK_CMDLINE_PRECONNECT);
179         if (rc != LDB_SUCCESS) {
180                 fprintf(stderr, "ldb: failed to run preconnect hooks (needed to get Samba LDIF handlers): %s\n", ldb_strerror(rc));
181                 exit(1);
182         }
183
184         if (argc < 2) {
185                 printf("Usage: ldbdump <fname>\n");
186                 exit(1);
187         }
188
189         while ((c = getopt( argc, argv, "hd:ec")) != -1) {
190                 switch (c) {
191                 case 'h':
192                         usage();
193                         exit( 0);
194                 case 'd':
195                         dn = ldb_dn_new(ldb, ldb, optarg);
196                         if (!dn) {
197                                 fprintf(stderr, "ldb failed to parse %s as a DN\n", optarg);
198                                 exit(1);
199                         }
200                         break;
201                 case 'e':
202                         emergency = true;
203                         break;
204                 case 'i':
205                         show_index = true;
206                         break;
207                 case 'c':
208                         validate_contents = true;
209                         break;
210                 default:
211                         usage();
212                         exit( 1);
213                 }
214         }
215
216         fname = argv[optind];
217
218         return dump_tdb(fname, dn, emergency);
219 }