s4-ldb: convert existing ldb tools to use new command line hooks
[metze/samba/wip.git] / source4 / lib / ldb / tools / ldbedit.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Tridgell  2004
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Lesser General Public
12    License as published by the Free Software Foundation; either
13    version 3 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Lesser General Public License for more details.
19
20    You should have received a copy of the GNU Lesser General Public
21    License along with this library; if not, see <http://www.gnu.org/licenses/>.
22 */
23
24 /*
25  *  Name: ldb
26  *
27  *  Component: ldbedit
28  *
29  *  Description: utility for ldb database editing
30  *
31  *  Author: Andrew Tridgell
32  */
33
34 #include "ldb_includes.h"
35 #include "system/filesys.h"
36 #include "ldb.h"
37 #include "tools/cmdline.h"
38 #include "tools/ldbutil.h"
39
40 static struct ldb_cmdline *options;
41
42 /*
43   debug routine
44 */
45 static void ldif_write_msg(struct ldb_context *ldb,
46                            FILE *f,
47                            enum ldb_changetype changetype,
48                            struct ldb_message *msg)
49 {
50         struct ldb_ldif ldif;
51         ldif.changetype = changetype;
52         ldif.msg = msg;
53         ldb_ldif_write_file(ldb, f, &ldif);
54 }
55
56 /*
57   modify a database record so msg1 becomes msg2
58   returns the number of modified elements
59 */
60 static int modify_record(struct ldb_context *ldb,
61                          struct ldb_message *msg1,
62                          struct ldb_message *msg2,
63                          struct ldb_control **req_ctrls)
64 {
65         int ret;
66         struct ldb_message *mod;
67
68         if (ldb_msg_difference(ldb, ldb, msg1, msg2, &mod) != LDB_SUCCESS) {
69                 fprintf(stderr, "Failed to calculate message differences\n");
70                 return -1;
71         }
72
73         ret = mod->num_elements;
74         if (ret == 0) {
75                 goto done;
76         }
77
78         if (options->verbose > 0) {
79                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
80         }
81
82         if (ldb_modify_ctrl(ldb, mod, req_ctrls) != 0) {
83                 fprintf(stderr, "failed to modify %s - %s\n",
84                         ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb));
85                 ret = -1;
86                 goto done;
87         }
88
89 done:
90         talloc_free(mod);
91         return ret;
92 }
93
94 /*
95   find dn in msgs[]
96 */
97 static struct ldb_message *msg_find(struct ldb_context *ldb,
98                                     struct ldb_message **msgs,
99                                     unsigned int count,
100                                     struct ldb_dn *dn)
101 {
102         unsigned int i;
103         for (i=0;i<count;i++) {
104                 if (ldb_dn_compare(dn, msgs[i]->dn) == 0) {
105                         return msgs[i];
106                 }
107         }
108         return NULL;
109 }
110
111 /*
112   merge the changes in msgs2 into the messages from msgs1
113 */
114 static int merge_edits(struct ldb_context *ldb,
115                        struct ldb_message **msgs1, unsigned int count1,
116                        struct ldb_message **msgs2, unsigned int count2)
117 {
118         unsigned int i;
119         struct ldb_message *msg;
120         int ret = 0;
121         int adds=0, modifies=0, deletes=0;
122         struct ldb_control **req_ctrls = ldb_parse_control_strings(ldb, ldb, (const char **)options->controls);
123         if (options->controls != NULL && req_ctrls == NULL) {
124                 fprintf(stderr, "parsing controls failed: %s\n", ldb_errstring(ldb));
125                 return -1;
126         }
127
128         if (ldb_transaction_start(ldb) != 0) {
129                 fprintf(stderr, "Failed to start transaction: %s\n", ldb_errstring(ldb));
130                 return -1;
131         }
132
133         /* do the adds and modifies */
134         for (i=0;i<count2;i++) {
135                 msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
136                 if (!msg) {
137                         if (options->verbose > 0) {
138                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
139                         }
140                         if (ldb_add_ctrl(ldb, msgs2[i], req_ctrls) != 0) {
141                                 fprintf(stderr, "failed to add %s - %s\n",
142                                         ldb_dn_get_linearized(msgs2[i]->dn),
143                                         ldb_errstring(ldb));
144                                 ldb_transaction_cancel(ldb);
145                                 return -1;
146                         }
147                         adds++;
148                 } else {
149                         if (modify_record(ldb, msg, msgs2[i], req_ctrls) > 0) {
150                                 modifies++;
151                         }
152                 }
153         }
154
155         /* do the deletes */
156         for (i=0;i<count1;i++) {
157                 msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
158                 if (!msg) {
159                         if (options->verbose > 0) {
160                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
161                         }
162                         if (ldb_delete_ctrl(ldb, msgs1[i]->dn, req_ctrls) != 0) {
163                                 fprintf(stderr, "failed to delete %s - %s\n",
164                                         ldb_dn_get_linearized(msgs1[i]->dn),
165                                         ldb_errstring(ldb));
166                                 ldb_transaction_cancel(ldb);
167                                 return -1;
168                         }
169                         deletes++;
170                 }
171         }
172
173         if (ldb_transaction_commit(ldb) != 0) {
174                 fprintf(stderr, "Failed to commit transaction: %s\n", ldb_errstring(ldb));
175                 return -1;
176         }
177
178         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
179
180         return ret;
181 }
182
183 /*
184   save a set of messages as ldif to a file
185 */
186 static int save_ldif(struct ldb_context *ldb,
187                      FILE *f, struct ldb_message **msgs, unsigned int count)
188 {
189         unsigned int i;
190
191         fprintf(f, "# editing %d records\n", count);
192
193         for (i=0;i<count;i++) {
194                 struct ldb_ldif ldif;
195                 fprintf(f, "# record %d\n", i+1);
196
197                 ldif.changetype = LDB_CHANGETYPE_NONE;
198                 ldif.msg = msgs[i];
199
200                 ldb_ldif_write_file(ldb, f, &ldif);
201         }
202
203         return 0;
204 }
205
206
207 /*
208   edit the ldb search results in msgs using the user selected editor
209 */
210 static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1,
211                    unsigned int count1, const char *editor)
212 {
213         int fd, ret;
214         FILE *f;
215         char file_template[] = "/tmp/ldbedit.XXXXXX";
216         char *cmd;
217         struct ldb_ldif *ldif;
218         struct ldb_message **msgs2 = NULL;
219         unsigned int count2 = 0;
220
221         /* write out the original set of messages to a temporary
222            file */
223         fd = mkstemp(file_template);
224
225         if (fd == -1) {
226                 perror(file_template);
227                 return -1;
228         }
229
230         f = fdopen(fd, "r+");
231
232         if (!f) {
233                 perror("fopen");
234                 close(fd);
235                 unlink(file_template);
236                 return -1;
237         }
238
239         if (save_ldif(ldb, f, msgs1, count1) != 0) {
240                 return -1;
241         }
242
243         fclose(f);
244
245         cmd = talloc_asprintf(ldb, "%s %s", editor, file_template);
246
247         if (!cmd) {
248                 unlink(file_template);
249                 fprintf(stderr, "out of memory\n");
250                 return -1;
251         }
252
253         /* run the editor */
254         ret = system(cmd);
255         talloc_free(cmd);
256
257         if (ret != 0) {
258                 unlink(file_template);
259                 fprintf(stderr, "edit with %s failed\n", editor);
260                 return -1;
261         }
262
263         /* read the resulting ldif into msgs2 */
264         f = fopen(file_template, "r");
265         if (!f) {
266                 perror(file_template);
267                 return -1;
268         }
269
270         while ((ldif = ldb_ldif_read_file(ldb, f))) {
271                 msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
272                 if (!msgs2) {
273                         fprintf(stderr, "out of memory");
274                         return -1;
275                 }
276                 msgs2[count2++] = ldif->msg;
277         }
278
279         fclose(f);
280         unlink(file_template);
281
282         return merge_edits(ldb, msgs1, count1, msgs2, count2);
283 }
284
285 static void usage(struct ldb_context *ldb)
286 {
287         printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
288         ldb_cmdline_help(ldb, "ldbedit", stdout);
289         exit(1);
290 }
291
292 int main(int argc, const char **argv)
293 {
294         struct ldb_context *ldb;
295         struct ldb_result *result = NULL;
296         struct ldb_dn *basedn = NULL;
297         int ret;
298         const char *expression = "(|(objectClass=*)(distinguishedName=*))";
299         const char * const * attrs = NULL;
300         TALLOC_CTX *mem_ctx = talloc_new(NULL);
301
302         ldb = ldb_init(mem_ctx, NULL);
303
304         options = ldb_cmdline_process(ldb, argc, argv, usage);
305
306         /* the check for '=' is for compatibility with ldapsearch */
307         if (options->argc > 0 &&
308             strchr(options->argv[0], '=')) {
309                 expression = options->argv[0];
310                 options->argv++;
311                 options->argc--;
312         }
313
314         if (options->argc > 0) {
315                 attrs = (const char * const *)(options->argv);
316         }
317
318         if (options->basedn != NULL) {
319                 basedn = ldb_dn_new(ldb, ldb, options->basedn);
320                 if ( ! ldb_dn_validate(basedn)) {
321                         printf("Invalid Base DN format\n");
322                         exit(1);
323                 }
324         }
325
326         ret = ldb_search(ldb, ldb, &result, basedn, options->scope, attrs, "%s", expression);
327         if (ret != LDB_SUCCESS) {
328                 printf("search failed - %s\n", ldb_errstring(ldb));
329                 exit(1);
330         }
331
332         if (result->count == 0) {
333                 printf("no matching records - cannot edit\n");
334                 return 0;
335         }
336
337         do_edit(ldb, result->msgs, result->count, options->editor);
338
339         if (result) {
340                 ret = talloc_free(result);
341                 if (ret == -1) {
342                         fprintf(stderr, "talloc_free failed\n");
343                         exit(1);
344                 }
345         }
346
347         talloc_free(mem_ctx);
348
349         return 0;
350 }