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