r5298: - got rid of pstring.h from includes.h. This at least makes it a bit
[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 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/ldb.h"
37 #include "ldb/include/ldb_private.h"
38
39 #ifdef _SAMBA_BUILD_
40 #include "system/filesys.h"
41 #endif
42
43 static int verbose;
44
45 /*
46   debug routine 
47 */
48 static void ldif_write_msg(struct ldb_context *ldb, 
49                            FILE *f, 
50                            enum ldb_changetype changetype,
51                            struct ldb_message *msg)
52 {
53         struct ldb_ldif ldif;
54         ldif.changetype = changetype;
55         ldif.msg = msg;
56         ldb_ldif_write_file(ldb, f, &ldif);
57 }
58
59 /*
60   modify a database record so msg1 becomes msg2
61   returns the number of modified elements
62 */
63 static int modify_record(struct ldb_context *ldb, 
64                          struct ldb_message *msg1,
65                          struct ldb_message *msg2)
66 {
67         struct ldb_message *mod;
68         struct ldb_message_element *el;
69         unsigned int i;
70         int count = 0;
71
72         mod = ldb_msg_new(ldb);
73
74         mod->dn = msg1->dn;
75         mod->num_elements = 0;
76         mod->elements = NULL;
77
78         msg2 = ldb_msg_canonicalize(ldb, msg2);
79         if (msg2 == NULL) {
80                 fprintf(stderr, "Failed to canonicalise msg2\n");
81                 return -1;
82         }
83         
84         /* look in msg2 to find elements that need to be added
85            or modified */
86         for (i=0;i<msg2->num_elements;i++) {
87                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
88
89                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
90                         continue;
91                 }
92
93                 if (ldb_msg_add(ldb, mod, 
94                                 &msg2->elements[i],
95                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
96                         return -1;
97                 }
98                 count++;
99         }
100
101         /* look in msg1 to find elements that need to be deleted */
102         for (i=0;i<msg1->num_elements;i++) {
103                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
104                 if (!el) {
105                         if (ldb_msg_add_empty(ldb, mod, 
106                                               msg1->elements[i].name,
107                                               LDB_FLAG_MOD_DELETE) != 0) {
108                                 return -1;
109                         }
110                         count++;
111                 }
112         }
113
114         if (mod->num_elements == 0) {
115                 return 0;
116         }
117
118         if (ldb_modify(ldb, mod) != 0) {
119                 fprintf(stderr, "failed to modify %s - %s\n", 
120                         msg1->dn, ldb_errstring(ldb));
121                 return -1;
122         }
123
124         if (verbose > 0) {
125                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
126         }
127
128         return count;
129 }
130
131 /*
132   find dn in msgs[]
133 */
134 static struct ldb_message *msg_find(struct ldb_message **msgs, int count,
135                                     const char *dn)
136 {
137         int i;
138         for (i=0;i<count;i++) {
139                 if (ldb_dn_cmp(dn, msgs[i]->dn) == 0) {
140                         return msgs[i];
141                 }
142         }
143         return NULL;
144 }
145
146 /*
147   merge the changes in msgs2 into the messages from msgs1
148 */
149 static int merge_edits(struct ldb_context *ldb,
150                        struct ldb_message **msgs1, int count1,
151                        struct ldb_message **msgs2, int count2)
152 {
153         int i;
154         struct ldb_message *msg;
155         int ret = 0;
156         int adds=0, modifies=0, deletes=0;
157
158         /* do the adds and modifies */
159         for (i=0;i<count2;i++) {
160                 msg = msg_find(msgs1, count1, msgs2[i]->dn);
161                 if (!msg) {
162                         if (ldb_add(ldb, msgs2[i]) != 0) {
163                                 fprintf(stderr, "failed to add %s - %s\n",
164                                         msgs2[i]->dn, ldb_errstring(ldb));
165                                 return -1;
166                         }
167                         if (verbose > 0) {
168                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
169                         }
170                         adds++;
171                 } else {
172                         if (modify_record(ldb, msg, msgs2[i]) > 0) {
173                                 modifies++;
174                         }
175                 }
176         }
177
178         /* do the deletes */
179         for (i=0;i<count1;i++) {
180                 msg = msg_find(msgs2, count2, msgs1[i]->dn);
181                 if (!msg) {
182                         if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
183                                 fprintf(stderr, "failed to delete %s - %s\n",
184                                         msgs1[i]->dn, ldb_errstring(ldb));
185                                 return -1;
186                         }
187                         if (verbose > 0) {
188                                 ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
189                         }
190                         deletes++;
191                 }
192         }
193
194         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
195
196         return ret;
197 }
198
199 /*
200   save a set of messages as ldif to a file
201 */
202 static int save_ldif(struct ldb_context *ldb, 
203                      FILE *f, struct ldb_message **msgs, int count)
204 {
205         int i;
206
207         fprintf(f, "# editing %d records\n", count);
208
209         for (i=0;i<count;i++) {
210                 struct ldb_ldif ldif;
211                 fprintf(f, "# record %d\n", i+1);
212
213                 ldif.changetype = LDB_CHANGETYPE_NONE;
214                 ldif.msg = msgs[i];
215
216                 ldb_ldif_write_file(ldb, f, &ldif);
217         }
218
219         return 0;
220 }
221
222
223 /*
224   edit the ldb search results in msgs using the user selected editor
225 */
226 static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int count1,
227                    const char *editor)
228 {
229         int fd, ret;
230         FILE *f;
231         char template[] = "/tmp/ldbedit.XXXXXX";
232         char *cmd;
233         struct ldb_ldif *ldif;
234         struct ldb_message **msgs2 = NULL;
235         int count2 = 0;
236
237         /* write out the original set of messages to a temporary
238            file */
239         fd = mkstemp(template);
240
241         if (fd == -1) {
242                 perror(template);
243                 return -1;
244         }
245
246         f = fdopen(fd, "r+");
247
248         if (!f) {
249                 perror("fopen");
250                 close(fd);
251                 unlink(template);
252                 return -1;
253         }
254
255         if (save_ldif(ldb, f, msgs1, count1) != 0) {
256                 return -1;
257         }
258
259         fclose(f);
260
261         asprintf(&cmd, "%s %s", editor, template);
262
263         if (!cmd) {
264                 unlink(template);
265                 fprintf(stderr, "out of memory\n");
266                 return -1;
267         }
268
269         /* run the editor */
270         ret = system(cmd);
271         free(cmd);
272
273         if (ret != 0) {
274                 unlink(template);
275                 fprintf(stderr, "edit with %s failed\n", editor);
276                 return -1;
277         }
278
279         /* read the resulting ldif into msgs2 */
280         f = fopen(template, "r");
281         if (!f) {
282                 perror(template);
283                 return -1;
284         }
285
286         while ((ldif = ldb_ldif_read_file(ldb, f))) {
287                 msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
288                 if (!msgs2) {
289                         fprintf(stderr, "out of memory");
290                         return -1;
291                 }
292                 msgs2[count2++] = ldif->msg;
293         }
294
295         fclose(f);
296         unlink(template);
297
298         return merge_edits(ldb, msgs1, count1, msgs2, count2);
299 }
300
301 static void usage(void)
302 {
303         printf("Usage: ldbedit <options> <expression> <attributes ...>\n");
304         printf("Options:\n");
305         printf("  -H ldb_url       choose the database (or $LDB_URL)\n");
306         printf("  -s base|sub|one  choose search scope\n");
307         printf("  -b basedn        choose baseDN\n");
308         printf("  -a               edit all records (expression 'objectclass=*')\n");
309         printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
310         printf("  -v               verbose mode)\n");
311         exit(1);
312 }
313
314  int main(int argc, char * const argv[])
315 {
316         struct ldb_context *ldb;
317         struct ldb_message **msgs;
318         int ret;
319         const char *expression = NULL;
320         const char *ldb_url;
321         const char *basedn = NULL;
322         const char **options = NULL;
323         int ldbopts;
324         int opt;
325         enum ldb_scope scope = LDB_SCOPE_SUBTREE;
326         const char *editor;
327         const char * const * attrs = NULL;
328
329         ldb_url = getenv("LDB_URL");
330
331         /* build the editor command to run -
332            use the same editor priorities as vipw */
333         editor = getenv("VISUAL");
334         if (!editor) {
335                 editor = getenv("EDITOR");
336         }
337         if (!editor) {
338                 editor = "vi";
339         }
340
341         ldbopts = 0;
342         while ((opt = getopt(argc, argv, "hab:e:H:s:vo:")) != EOF) {
343                 switch (opt) {
344                 case 'b':
345                         basedn = optarg;
346                         break;
347
348                 case 'H':
349                         ldb_url = optarg;
350                         break;
351
352                 case 's':
353                         if (strcmp(optarg, "base") == 0) {
354                                 scope = LDB_SCOPE_BASE;
355                         } else if (strcmp(optarg, "sub") == 0) {
356                                 scope = LDB_SCOPE_SUBTREE;
357                         } else if (strcmp(optarg, "one") == 0) {
358                                 scope = LDB_SCOPE_ONELEVEL;
359                         }
360                         break;
361
362                 case 'e':
363                         editor = optarg;
364                         break;
365
366                 case 'a':
367                         expression = "(|(objectclass=*)(dn=*))";
368                         break;
369                         
370                 case 'v':
371                         verbose++;
372                         break;
373
374                 case 'o':
375                         options = ldb_options_parse(options, &ldbopts, optarg);
376                         break;
377
378                 case 'h':
379                 default:
380                         usage();
381                         break;
382                 }
383         }
384
385         if (!ldb_url) {
386                 fprintf(stderr, "You must specify a ldb URL\n\n");
387                 usage();
388         }
389
390         argc -= optind;
391         argv += optind;
392
393         if (!expression) {
394                 if (argc == 0) {
395                         usage();
396                 }
397                 expression = argv[0];
398                 argc--;
399                 argv++;
400         }
401
402         if (argc > 0) {
403                 attrs = (const char * const *)argv;
404         }
405
406         ldb = ldb_connect(ldb_url, 0, options);
407
408         if (!ldb) {
409                 perror("ldb_connect");
410                 exit(1);
411         }
412
413         ldb_set_debug_stderr(ldb);
414
415         ret = ldb_search(ldb, basedn, scope, expression, attrs, &msgs);
416
417         if (ret == -1) {
418                 printf("search failed - %s\n", ldb_errstring(ldb));
419                 exit(1);
420         }
421
422         if (ret == 0) {
423                 printf("no matching records - cannot edit\n");
424                 return 0;
425         }
426
427         do_edit(ldb, msgs, ret, editor);
428
429         if (ret > 0) {
430                 ret = ldb_search_free(ldb, msgs);
431                 if (ret == -1) {
432                         fprintf(stderr, "search_free failed - %s\n", ldb_errstring(ldb));
433                         exit(1);
434                 }
435         }
436
437         ldb_close(ldb);
438         return 0;
439 }