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