r380: make sure that ldbedit -a works with all tdb and LDAP backends
[nivanova/samba-autobuild/.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
37 /*
38   modify a database record so msg1 becomes msg2
39   returns the number of modified elements
40 */
41 static int modify_record(struct ldb_context *ldb, 
42                          struct ldb_message *msg1,
43                          struct ldb_message *msg2)
44 {
45         struct ldb_message mod;
46         struct ldb_message_element *el;
47         int i;
48         int count = 0;
49
50         mod.dn = msg1->dn;
51         mod.num_elements = 0;
52         mod.elements = NULL;
53
54         /* look in msg2 to find elements that need to be added
55            or modified */
56         for (i=0;i<msg2->num_elements;i++) {
57                 el = ldb_msg_find_element(msg1, msg2->elements[i].name);
58
59                 if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
60                         continue;
61                 }
62
63                 if (ldb_msg_add(&mod, 
64                                 &msg2->elements[i],
65                                 el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
66                         return -1;
67                 }
68                 count++;
69         }
70
71         /* look in msg1 to find elements that need to be deleted */
72         for (i=0;i<msg1->num_elements;i++) {
73                 el = ldb_msg_find_element(msg2, msg1->elements[i].name);
74                 if (!el) {
75                         if (ldb_msg_add_empty(&mod, 
76                                               msg1->elements[i].name,
77                                               LDB_FLAG_MOD_DELETE) != 0) {
78                                 return -1;
79                         }
80                         count++;
81                 }
82         }
83
84         if (mod.num_elements == 0) {
85                 return 0;
86         }
87
88         if (ldb_modify(ldb, &mod) != 0) {
89                 fprintf(stderr, "failed to modify %s - %s\n", 
90                         msg1->dn, ldb_errstring(ldb));
91                 return -1;
92         }
93
94         return count;
95 }
96
97 /*
98   find dn in msgs[]
99 */
100 static struct ldb_message *msg_find(struct ldb_message **msgs, int count,
101                                     const char *dn)
102 {
103         int i;
104         for (i=0;i<count;i++) {
105                 if (strcmp(dn, msgs[i]->dn) == 0) {
106                         return msgs[i];
107                 }
108         }
109         return NULL;
110 }
111
112 /*
113   merge the changes in msgs2 into the messages from msgs1
114 */
115 static int merge_edits(struct ldb_context *ldb,
116                        struct ldb_message **msgs1, int count1,
117                        struct ldb_message **msgs2, int count2)
118 {
119         int i;
120         struct ldb_message *msg;
121         int ret = 0;
122         int adds=0, modifies=0, deletes=0;
123
124         /* do the adds and modifies */
125         for (i=0;i<count2;i++) {
126                 msg = msg_find(msgs1, count1, msgs2[i]->dn);
127                 if (!msg) {
128                         if (ldb_add(ldb, msgs2[i]) != 0) {
129                                 fprintf(stderr, "failed to add %s - %s\n",
130                                         msgs2[i]->dn, ldb_errstring(ldb));
131                                 return -1;
132                         }
133                         adds++;
134                 } else {
135                         if (modify_record(ldb, msg, msgs2[i]) > 0) {
136                                 modifies++;
137                         }
138                 }
139         }
140
141         /* do the deletes */
142         for (i=0;i<count1;i++) {
143                 msg = msg_find(msgs2, count2, msgs1[i]->dn);
144                 if (!msg) {
145                         if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
146                                 fprintf(stderr, "failed to delete %s - %s\n",
147                                         msgs1[i]->dn, ldb_errstring(ldb));
148                                 return -1;
149                         }
150                         deletes++;
151                 }
152         }
153
154         printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
155
156         return ret;
157 }
158
159 /*
160   save a set of messages as ldif to a file
161 */
162 static int save_ldif(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                 ldif_write_file(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 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(template);
199
200         if (fd == -1) {
201                 perror(template);
202                 return -1;
203         }
204
205         f = fdopen(fd, "r+");
206
207         if (!f) {
208                 perror("fopen");
209                 close(fd);
210                 unlink(template);
211                 return -1;
212         }
213
214         if (save_ldif(f, msgs1, count1) != 0) {
215                 return -1;
216         }
217
218         fclose(f);
219
220         asprintf(&cmd, "%s %s", editor, template);
221
222         if (!cmd) {
223                 unlink(template);
224                 fprintf(stderr, "out of memory\n");
225                 return -1;
226         }
227
228         /* run the editor */
229         ret = system(cmd);
230         free(cmd);
231
232         if (ret != 0) {
233                 unlink(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(template, "r");
240         if (!f) {
241                 perror(template);
242                 return -1;
243         }
244
245         while ((ldif = ldif_read_file(f))) {
246                 msgs2 = realloc_p(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(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>\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         exit(1);
270 }
271
272  int main(int argc, char * const argv[])
273 {
274         struct ldb_context *ldb;
275         struct ldb_message **msgs;
276         int ret;
277         const char *expression = NULL;
278         const char *ldb_url;
279         const char *basedn = NULL;
280         int opt;
281         enum ldb_scope scope = LDB_SCOPE_SUBTREE;
282         const char *editor;
283
284         ldb_url = getenv("LDB_URL");
285
286         /* build the editor command to run -
287            use the same editor priorities as vipw */
288         editor = getenv("VISUAL");
289         if (!editor) {
290                 editor = getenv("EDITOR");
291         }
292         if (!editor) {
293                 editor = "vi";
294         }
295
296         while ((opt = getopt(argc, argv, "hab:e:H:s:")) != EOF) {
297                 switch (opt) {
298                 case 'b':
299                         basedn = optarg;
300                         break;
301
302                 case 'H':
303                         ldb_url = optarg;
304                         break;
305
306                 case 's':
307                         if (strcmp(optarg, "base") == 0) {
308                                 scope = LDB_SCOPE_BASE;
309                         } else if (strcmp(optarg, "sub") == 0) {
310                                 scope = LDB_SCOPE_SUBTREE;
311                         } else if (strcmp(optarg, "one") == 0) {
312                                 scope = LDB_SCOPE_ONELEVEL;
313                         }
314                         break;
315
316                 case 'e':
317                         editor = optarg;
318                         break;
319
320                 case 'a':
321                         expression = "(|(objectclass=*)(dn=*))";
322                         break;
323                         
324                 case 'h':
325                 default:
326                         usage();
327                         break;
328                 }
329         }
330
331         if (!ldb_url) {
332                 fprintf(stderr, "You must specify a ldb URL\n\n");
333                 usage();
334         }
335
336         argc -= optind;
337         argv += optind;
338
339         if (!expression) {
340                 if (argc == 0) {
341                         usage();
342                 }
343                 expression = argv[0];
344         }
345
346         ldb = ldb_connect(ldb_url, 0, NULL);
347
348         if (!ldb) {
349                 perror("ldb_connect");
350                 exit(1);
351         }
352
353         ret = ldb_search(ldb, basedn, scope, expression, NULL, &msgs);
354
355         if (ret == -1) {
356                 printf("search failed - %s\n", ldb_errstring(ldb));
357                 exit(1);
358         }
359
360         if (ret == 0) {
361                 printf("no matching records - cannot edit\n");
362                 return 0;
363         }
364
365         do_edit(ldb, msgs, ret, editor);
366
367         if (ret > 0) {
368                 ret = ldb_search_free(ldb, msgs);
369                 if (ret == -1) {
370                         fprintf(stderr, "search_free failed - %s\n", ldb_errstring(ldb));
371                         exit(1);
372                 }
373         }
374
375         ldb_close(ldb);
376         return 0;
377 }