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