Print error strings when transactions fail in ldb tools
[kai/samba.git] / source4 / lib / ldb / tools / ldbedit.c
index b97c40ff1aebe7f0f9dc8597e01e12b3c68e6f4a..1a684c5c2db4d0c035efd6ffa33d0aaaa536288c 100644 (file)
@@ -10,7 +10,7 @@
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
-   version 2 of the License, or (at your option) any later version.
+   version 3 of the License, or (at your option) any later version.
 
    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,8 +18,7 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */
 
 /*
  *  Author: Andrew Tridgell
  */
 
-#include "includes.h"
+#include "ldb_includes.h"
+#include "tools/cmdline.h"
 
-static int verbose;
+static struct ldb_cmdline *options;
 
 /*
   debug routine 
@@ -46,7 +46,7 @@ static void ldif_write_msg(struct ldb_context *ldb,
 {
        struct ldb_ldif ldif;
        ldif.changetype = changetype;
-       ldif.msg = *msg;
+       ldif.msg = msg;
        ldb_ldif_write_file(ldb, f, &ldif);
 }
 
@@ -58,71 +58,42 @@ static int modify_record(struct ldb_context *ldb,
                         struct ldb_message *msg1,
                         struct ldb_message *msg2)
 {
-       struct ldb_message mod;
-       struct ldb_message_element *el;
-       unsigned int i;
-       int count = 0;
-
-       mod.dn = msg1->dn;
-       mod.num_elements = 0;
-       mod.elements = NULL;
-
-       /* look in msg2 to find elements that need to be added
-          or modified */
-       for (i=0;i<msg2->num_elements;i++) {
-               el = ldb_msg_find_element(msg1, msg2->elements[i].name);
-
-               if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
-                       continue;
-               }
+       struct ldb_message *mod;
 
-               if (ldb_msg_add(ldb, &mod, 
-                               &msg2->elements[i],
-                               el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
-                       return -1;
-               }
-               count++;
+       mod = ldb_msg_diff(ldb, msg1, msg2);
+       if (mod == NULL) {
+               fprintf(stderr, "Failed to calculate message differences\n");
+               return -1;
        }
 
-       /* look in msg1 to find elements that need to be deleted */
-       for (i=0;i<msg1->num_elements;i++) {
-               el = ldb_msg_find_element(msg2, msg1->elements[i].name);
-               if (!el) {
-                       if (ldb_msg_add_empty(ldb, &mod, 
-                                             msg1->elements[i].name,
-                                             LDB_FLAG_MOD_DELETE) != 0) {
-                               return -1;
-                       }
-                       count++;
-               }
+       if (mod->num_elements == 0) {
+               return 0;
        }
 
-       if (mod.num_elements == 0) {
-               return 0;
+       if (options->verbose > 0) {
+               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, mod);
        }
 
-       if (ldb_modify(ldb, &mod) != 0) {
+       if (ldb_modify(ldb, mod) != 0) {
                fprintf(stderr, "failed to modify %s - %s\n", 
-                       msg1->dn, ldb_errstring(ldb));
+                       ldb_dn_get_linearized(msg1->dn), ldb_errstring(ldb));
                return -1;
        }
 
-       if (verbose > 0) {
-               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_MODIFY, &mod);
-       }
-
-       return count;
+       return mod->num_elements;
 }
 
 /*
   find dn in msgs[]
 */
-static struct ldb_message *msg_find(struct ldb_message **msgs, int count,
-                                   const char *dn)
+static struct ldb_message *msg_find(struct ldb_context *ldb,
+                                   struct ldb_message **msgs,
+                                   int count,
+                                   struct ldb_dn *dn)
 {
        int i;
        for (i=0;i<count;i++) {
-               if (ldb_dn_cmp(dn, msgs[i]->dn) == 0) {
+               if (ldb_dn_compare(dn, msgs[i]->dn) == 0) {
                        return msgs[i];
                }
        }
@@ -141,18 +112,24 @@ static int merge_edits(struct ldb_context *ldb,
        int ret = 0;
        int adds=0, modifies=0, deletes=0;
 
+       if (ldb_transaction_start(ldb) != 0) {
+               fprintf(stderr, "Failed to start transaction: %s\n", ldb_errstring(ldb));
+               return -1;
+       }
+
        /* do the adds and modifies */
        for (i=0;i<count2;i++) {
-               msg = msg_find(msgs1, count1, msgs2[i]->dn);
+               msg = msg_find(ldb, msgs1, count1, msgs2[i]->dn);
                if (!msg) {
+                       if (options->verbose > 0) {
+                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
+                       }
                        if (ldb_add(ldb, msgs2[i]) != 0) {
                                fprintf(stderr, "failed to add %s - %s\n",
-                                       msgs2[i]->dn, ldb_errstring(ldb));
+                                       ldb_dn_get_linearized(msgs2[i]->dn),
+                                       ldb_errstring(ldb));
                                return -1;
                        }
-                       if (verbose > 0) {
-                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_ADD, msgs2[i]);
-                       }
                        adds++;
                } else {
                        if (modify_record(ldb, msg, msgs2[i]) > 0) {
@@ -163,20 +140,26 @@ static int merge_edits(struct ldb_context *ldb,
 
        /* do the deletes */
        for (i=0;i<count1;i++) {
-               msg = msg_find(msgs2, count2, msgs1[i]->dn);
+               msg = msg_find(ldb, msgs2, count2, msgs1[i]->dn);
                if (!msg) {
+                       if (options->verbose > 0) {
+                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
+                       }
                        if (ldb_delete(ldb, msgs1[i]->dn) != 0) {
                                fprintf(stderr, "failed to delete %s - %s\n",
-                                       msgs1[i]->dn, ldb_errstring(ldb));
+                                       ldb_dn_get_linearized(msgs1[i]->dn),
+                                       ldb_errstring(ldb));
                                return -1;
                        }
-                       if (verbose > 0) {
-                               ldif_write_msg(ldb, stdout, LDB_CHANGETYPE_DELETE, msgs1[i]);
-                       }
                        deletes++;
                }
        }
 
+       if (ldb_transaction_commit(ldb) != 0) {
+               fprintf(stderr, "Failed to commit transaction: %s\n", ldb_errstring(ldb));
+               return -1;
+       }
+
        printf("# %d adds  %d modifies  %d deletes\n", adds, modifies, deletes);
 
        return ret;
@@ -197,7 +180,7 @@ static int save_ldif(struct ldb_context *ldb,
                fprintf(f, "# record %d\n", i+1);
 
                ldif.changetype = LDB_CHANGETYPE_NONE;
-               ldif.msg = *msgs[i];
+               ldif.msg = msgs[i];
 
                ldb_ldif_write_file(ldb, f, &ldif);
        }
@@ -214,7 +197,7 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
 {
        int fd, ret;
        FILE *f;
-       char template[] = "/tmp/ldbedit.XXXXXX";
+       char file_template[] = "/tmp/ldbedit.XXXXXX";
        char *cmd;
        struct ldb_ldif *ldif;
        struct ldb_message **msgs2 = NULL;
@@ -222,10 +205,10 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
 
        /* write out the original set of messages to a temporary
           file */
-       fd = mkstemp(template);
+       fd = mkstemp(file_template);
 
        if (fd == -1) {
-               perror(template);
+               perror(file_template);
                return -1;
        }
 
@@ -234,7 +217,7 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
        if (!f) {
                perror("fopen");
                close(fd);
-               unlink(template);
+               unlink(file_template);
                return -1;
        }
 
@@ -244,42 +227,42 @@ static int do_edit(struct ldb_context *ldb, struct ldb_message **msgs1, int coun
 
        fclose(f);
 
-       asprintf(&cmd, "%s %s", editor, template);
+       cmd = talloc_asprintf(ldb, "%s %s", editor, file_template);
 
        if (!cmd) {
-               unlink(template);
+               unlink(file_template);
                fprintf(stderr, "out of memory\n");
                return -1;
        }
 
        /* run the editor */
        ret = system(cmd);
-       free(cmd);
+       talloc_free(cmd);
 
        if (ret != 0) {
-               unlink(template);
+               unlink(file_template);
                fprintf(stderr, "edit with %s failed\n", editor);
                return -1;
        }
 
        /* read the resulting ldif into msgs2 */
-       f = fopen(template, "r");
+       f = fopen(file_template, "r");
        if (!f) {
-               perror(template);
+               perror(file_template);
                return -1;
        }
 
        while ((ldif = ldb_ldif_read_file(ldb, f))) {
-               msgs2 = ldb_realloc_p(ldb, msgs2, struct ldb_message *, count2+1);
+               msgs2 = talloc_realloc(ldb, msgs2, struct ldb_message *, count2+1);
                if (!msgs2) {
                        fprintf(stderr, "out of memory");
                        return -1;
                }
-               msgs2[count2++] = &ldif->msg;
+               msgs2[count2++] = ldif->msg;
        }
 
        fclose(f);
-       unlink(template);
+       unlink(file_template);
 
        return merge_edits(ldb, msgs1, count1, msgs2, count2);
 }
@@ -293,125 +276,64 @@ static void usage(void)
        printf("  -b basedn        choose baseDN\n");
        printf("  -a               edit all records (expression 'objectclass=*')\n");
        printf("  -e editor        choose editor (or $VISUAL or $EDITOR)\n");
+       printf("  -v               verbose mode\n");
        exit(1);
 }
 
- int main(int argc, char * const argv[])
+int main(int argc, const char **argv)
 {
        struct ldb_context *ldb;
-       struct ldb_message **msgs;
+       struct ldb_result *result = NULL;
+       struct ldb_dn *basedn = NULL;
        int ret;
-       const char *expression = NULL;
-       const char *ldb_url;
-       const char *basedn = NULL;
-       int opt;
-       enum ldb_scope scope = LDB_SCOPE_SUBTREE;
-       const char *editor;
+       const char *expression = "(|(objectClass=*)(distinguishedName=*))";
        const char * const * attrs = NULL;
 
-       ldb_url = getenv("LDB_URL");
+       ldb = ldb_init(NULL, NULL);
 
-       /* build the editor command to run -
-          use the same editor priorities as vipw */
-       editor = getenv("VISUAL");
-       if (!editor) {
-               editor = getenv("EDITOR");
-       }
-       if (!editor) {
-               editor = "vi";
-       }
+       options = ldb_cmdline_process(ldb, argc, argv, usage);
 
-       while ((opt = getopt(argc, argv, "hab:e:H:s:v")) != EOF) {
-               switch (opt) {
-               case 'b':
-                       basedn = optarg;
-                       break;
-
-               case 'H':
-                       ldb_url = optarg;
-                       break;
-
-               case 's':
-                       if (strcmp(optarg, "base") == 0) {
-                               scope = LDB_SCOPE_BASE;
-                       } else if (strcmp(optarg, "sub") == 0) {
-                               scope = LDB_SCOPE_SUBTREE;
-                       } else if (strcmp(optarg, "one") == 0) {
-                               scope = LDB_SCOPE_ONELEVEL;
-                       }
-                       break;
-
-               case 'e':
-                       editor = optarg;
-                       break;
-
-               case 'a':
-                       expression = "(|(objectclass=*)(dn=*))";
-                       break;
-                       
-               case 'v':
-                       verbose++;
-                       break;
-
-               case 'h':
-               default:
-                       usage();
-                       break;
-               }
+       /* the check for '=' is for compatibility with ldapsearch */
+       if (options->argc > 0 && 
+           strchr(options->argv[0], '=')) {
+               expression = options->argv[0];
+               options->argv++;
+               options->argc--;
        }
 
-       if (!ldb_url) {
-               fprintf(stderr, "You must specify a ldb URL\n\n");
-               usage();
+       if (options->argc > 0) {
+               attrs = (const char * const *)(options->argv);
        }
 
-       argc -= optind;
-       argv += optind;
-
-       if (!expression) {
-               if (argc == 0) {
-                       usage();
+       if (options->basedn != NULL) {
+               basedn = ldb_dn_new(ldb, ldb, options->basedn);
+               if ( ! ldb_dn_validate(basedn)) {
+                       printf("Invalid Base DN format\n");
+                       exit(1);
                }
-               expression = argv[0];
-               argc--;
-               argv++;
-       }
-
-       if (argc > 0) {
-               attrs = (const char * const *)argv;
        }
 
-       ldb = ldb_connect(ldb_url, 0, NULL);
-
-       if (!ldb) {
-               perror("ldb_connect");
-               exit(1);
-       }
-
-       ldb_set_debug_stderr(ldb);
-
-       ret = ldb_search(ldb, basedn, scope, expression, attrs, &msgs);
-
-       if (ret == -1) {
+       ret = ldb_search(ldb, ldb, &result, basedn, options->scope, attrs, "%s", expression);
+       if (ret != LDB_SUCCESS) {
                printf("search failed - %s\n", ldb_errstring(ldb));
                exit(1);
        }
 
-       if (ret == 0) {
+       if (result->count == 0) {
                printf("no matching records - cannot edit\n");
                return 0;
        }
 
-       do_edit(ldb, msgs, ret, editor);
+       do_edit(ldb, result->msgs, result->count, options->editor);
 
-       if (ret > 0) {
-               ret = ldb_search_free(ldb, msgs);
+       if (result) {
+               ret = talloc_free(result);
                if (ret == -1) {
-                       fprintf(stderr, "search_free failed - %s\n", ldb_errstring(ldb));
+                       fprintf(stderr, "talloc_free failed\n");
                        exit(1);
                }
        }
 
-       ldb_close(ldb);
+       talloc_free(ldb);
        return 0;
 }