Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-gmake3
[kai/samba.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 "tools/cmdline.h"
36
37 static struct ldb_cmdline *options;
38
39 /*
40   debug routine 
41 */
42 static void ldif_write_msg(struct ldb_context *ldb, 
43                            FILE *f, 
44                            enum ldb_changetype changetype,
45                            struct ldb_message *msg)
46 {
47         struct ldb_ldif ldif;
48         ldif.changetype = changetype;
49         ldif.msg = msg;
50         ldb_ldif_write_file(ldb, f, &ldif);
51 }
52
53 /*
54   modify a database record so msg1 becomes msg2
55   returns the number of modified elements
56 */
57 static int modify_record(struct ldb_context *ldb, 
58                          struct ldb_message *msg1,
59                          struct ldb_message *msg2)
60 {
61         struct ldb_message *mod;
62
63         mod = ldb_msg_diff(ldb, msg1, msg2);
64         if (mod == NULL) {
65                 fprintf(stderr, "Failed to calculate message differences\n");
66                 return -1;
67         }
68
69         if (mod->num_elements == 0) {
70                 return 0;
71         }
72
73         if (options->verbose > 0) {
74                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
75         }
76
77         if (ldb_modify(ldb, mod) != 0) {
78                 fprintf(stderr, "failed to modify %s - %s\n", 
79                         ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb));
80                 return -1;
81         }
82
83         return mod->num_elements;
84 }
85
86 /*
87   find dn in msgs[]
88 */
89 static struct ldb_message *msg_find(struct ldb_context *ldb,
90                                     struct ldb_message **msgs,
91                                     int count,
92                                     struct ldb_dn *dn)
93 {
94         int i;
95         for (i=0;i<count;i++) {
96                 if (ldb_dn_compare(dn, msgs[i]->dn) == 0) {
97                         return msgs[i];
98                 }
99         }
100         return NULL;
101 }
102
103 /*
104   merge the changes in msgs2 into the messages from msgs1
105 */
106 static int merge_edits(struct ldb_context *ldb,
107                        struct ldb_message **msgs1, int count1,
108                        struct ldb_message **msgs2, int count2)
109 {
110         int i;
111         struct ldb_message *msg;
112         int ret = 0;
113         int adds=0, modifies=0, deletes=0;
114
115         /* do the adds and modifies */
116         for (i=0;i<count2;i++) {
117                 msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
118                 if (!msg) {
119                         if (options->verbose > 0) {
120                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
121                         }
122                         if (ldb_add(ldb, msgs2[i]) != 0) {
123                                 fprintf(stderr, "failed to add %s - %s\n",
124                                         ldb_dn_get_linearized(msgs2[i]->dn),
125                                         ldb_errstring(ldb));
126                                 return -1;
127                         }
128                         adds++;
129                 } else {
130                         if (modify_record(ldb, msg, msgs2[i]) > 0) {
131                                 modifies++;
132                         }
133                 }
134         }
135
136         /* do the deletes */
137         for (i=0;i<count1;i++) {
138                 msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
139                 if (!msg) {
140                         if (options->verbose > 0) {
141                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
142                         }
143                         if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
144                                 fprintf(stderr, "failed to delete %s - %s\n",
145                                         ldb_dn_get_linearized(msgs1[i]->dn),
146                                         ldb_errstring(ldb));
147                                 return -1;
148                         }
149                         deletes++;
150                 }
151         }
152
153         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
154
155         return ret;
156 }
157
158 /*
159   save a set of messages as ldif to a file
160 */
161 static int save_ldif(struct ldb_context *ldb, 
162                      FILE *f, struct ldb_message **msgs, int count)
163 {
164         int i;
165
166         fprintf(f, "# editing %d records\n", count);
167
168         for (i=0;i<count;i++) {
169                 struct ldb_ldif ldif;
170                 fprintf(f, "# record %d\n", i+1);
171
172                 ldif.changetype = LDB_CHANGETYPE_NONE;
173                 ldif.msg = msgs[i];
174
175                 ldb_ldif_write_file(ldb, f, &ldif);
176         }
177
178         return 0;
179 }
180
181
182 /*
183   edit the ldb search results in msgs using the user selected editor
184 */
185 static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
186                    const char *editor)
187 {
188         int fd, ret;
189         FILE *f;
190         char file_template[] = "/tmp/ldbedit.XXXXXX";
191         char *cmd;
192         struct ldb_ldif *ldif;
193         struct ldb_message **msgs2 = NULL;
194         int count2 = 0;
195
196         /* write out the original set of messages to a temporary
197            file */
198         fd = mkstemp(file_template);
199
200         if (fd == -1) {
201                 perror(file_template);
202                 return -1;
203         }
204
205         f = fdopen(fd, "r+");
206
207         if (!f) {
208                 perror("fopen");
209                 close(fd);
210                 unlink(file_template);
211                 return -1;
212         }
213
214         if (save_ldif(ldb, f, msgs1, count1) != 0) {
215                 return -1;
216         }
217
218         fclose(f);
219
220         cmd = talloc_asprintf(ldb, "%s %s", editor, file_template);
221
222         if (!cmd) {
223                 unlink(file_template);
224                 fprintf(stderr, "out of memory\n");
225                 return -1;
226         }
227
228         /* run the editor */
229         ret = system(cmd);
230         talloc_free(cmd);
231
232         if (ret != 0) {
233                 unlink(file_template);
234                 fprintf(stderr, "edit with %s failed\n", editor);
235                 return -1;
236         }
237
238         /* read the resulting ldif into msgs2 */
239         f = fopen(file_template, "r");
240         if (!f) {
241                 perror(file_template);
242                 return -1;
243         }
244
245         while ((ldif = ldb_ldif_read_file(ldb, f))) {
246                 msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
247                 if (!msgs2) {
248                         fprintf(stderr, "out of memory");
249                         return -1;
250                 }
251                 msgs2[count2++] = ldif->msg;
252         }
253
254         fclose(f);
255         unlink(file_template);
256
257         return merge_edits(ldb, msgs1, count1, msgs2, count2);
258 }
259
260 static void usage(void)
261 {
262         printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
263         printf("Options:\n");
264         printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
265         printf("  -s base|sub|one  choose search scope\n");
266         printf("  -b basedn        choose baseDN\n");
267         printf("  -a               edit all records (expression 'objectclass=*')\n");
268         printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
269         printf("  -v               verbose mode\n");
270         exit(1);
271 }
272
273 int main(int argc, const char **argv)
274 {
275         struct ldb_context *ldb;
276         struct ldb_result *result = NULL;
277         struct ldb_dn *basedn = NULL;
278         int ret;
279         const char *expression = "(|(objectClass=*)(distinguishedName=*))";
280         const char * const * attrs = NULL;
281
282         ldb = ldb_init(NULL);
283
284         options = ldb_cmdline_process(ldb, argc, argv, usage);
285
286         /* the check for '=' is for compatibility with ldapsearch */
287         if (options->argc > 0 && 
288             strchr(options->argv[0], '=')) {
289                 expression = options->argv[0];
290                 options->argv++;
291                 options->argc--;
292         }
293
294         if (options->argc > 0) {
295                 attrs = (const char * const *)(options->argv);
296         }
297
298         if (options->basedn != NULL) {
299                 basedn = ldb_dn_new(ldb, ldb, options->basedn);
300                 if ( ! ldb_dn_validate(basedn)) {
301                         printf("Invalid Base DN format\n");
302                         exit(1);
303                 }
304         }
305
306         ret = ldb_search(ldb, basedn, options->scope, expression, attrs, &result);
307         if (ret != LDB_SUCCESS) {
308                 printf("search failed - %s\n", ldb_errstring(ldb));
309                 exit(1);
310         }
311
312         if (result->count == 0) {
313                 printf("no matching records - cannot edit\n");
314                 return 0;
315         }
316
317         do_edit(ldb, result->msgs, result->count, options->editor);
318
319         if (result) {
320                 ret = talloc_free(result);
321                 if (ret == -1) {
322                         fprintf(stderr, "talloc_free failed\n");
323                         exit(1);
324                 }
325         }
326
327         talloc_free(ldb);
328         return 0;
329 }