summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/tools/dbscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/tools/dbscan.c')
-rw-r--r--ldap/servers/slapd/tools/dbscan.c800
1 files changed, 800 insertions, 0 deletions
diff --git a/ldap/servers/slapd/tools/dbscan.c b/ldap/servers/slapd/tools/dbscan.c
new file mode 100644
index 00000000..e8a96572
--- /dev/null
+++ b/ldap/servers/slapd/tools/dbscan.c
@@ -0,0 +1,800 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright Copyright 2002 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * small program to scan a Directory Server db file and dump the contents
+ *
+ * TODO: indirect indexes
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+#include <string.h>
+#include "db.h"
+
+#ifdef _WIN32
+#include <windows.h>
+extern int getopt();
+extern char *optarg;
+typedef unsigned char uint8_t;
+#else
+#include <netinet/in.h>
+#include <inttypes.h>
+#endif
+
+/* stolen from slapi-plugin.h */
+#define SLAPI_OPERATION_BIND 0x00000001UL
+#define SLAPI_OPERATION_UNBIND 0x00000002UL
+#define SLAPI_OPERATION_SEARCH 0x00000004UL
+#define SLAPI_OPERATION_MODIFY 0x00000008UL
+#define SLAPI_OPERATION_ADD 0x00000010UL
+#define SLAPI_OPERATION_DELETE 0x00000020UL
+#define SLAPI_OPERATION_MODDN 0x00000040UL
+#define SLAPI_OPERATION_MODRDN SLAPI_OPERATION_MODDN
+#define SLAPI_OPERATION_COMPARE 0x00000080UL
+#define SLAPI_OPERATION_ABANDON 0x00000100UL
+#define SLAPI_OPERATION_EXTENDED 0x00000200UL
+#define SLAPI_OPERATION_ANY 0xFFFFFFFFUL
+#define SLAPI_OPERATION_NONE 0x00000000UL
+
+#if defined(linux)
+#include <getopt.h>
+#endif
+
+typedef u_int32_t ID;
+
+typedef unsigned int uint32;
+
+typedef struct {
+ uint32 max;
+ uint32 used;
+ uint32 id[1];
+} IDL;
+
+/** db_printf - functioning same as printf but a place for manipluating output.
+*/
+void db_printf(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+}
+
+void db_printfln(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ vfprintf(stdout, "\n", NULL);
+}
+
+#ifdef DB26
+#define db_strerror strerror
+#endif
+
+int MAX_BUFFER = 4096;
+
+
+static IDL *idl_make(DBT *data)
+{
+ IDL *idl = NULL, *xidl;
+
+ if (data->size < 2*sizeof(uint32)) {
+ idl = (IDL *)malloc(sizeof(IDL) + 64*sizeof(uint32));
+ if (! idl)
+ return NULL;
+ idl->max = 64;
+ idl->used = 1;
+ idl->id[0] = *(uint32 *)(data->data);
+ return idl;
+ }
+
+ xidl = (IDL *)(data->data);
+ idl = (IDL *)malloc(data->size);
+ if (! idl)
+ return NULL;
+
+ memcpy(idl, xidl, data->size);
+ return idl;
+}
+
+static void idl_free(IDL *idl)
+{
+ idl->max = 0;
+ idl->used = 0;
+ free(idl);
+}
+
+static IDL *idl_append(IDL *idl, uint32 id)
+{
+ if (idl->used >= idl->max) {
+ /* must grow */
+ idl->max *= 2;
+ idl = realloc(idl, sizeof(IDL) + idl->max * sizeof(uint32));
+ if (! idl)
+ return NULL;
+ }
+ idl->id[idl->used++] = id;
+ return idl;
+}
+
+
+/* format a string for easy printing */
+#define FMT_LF_OK 1
+#define FMT_SP_OK 2
+static char *format_raw(unsigned char *s, int len, int flags)
+{
+ static unsigned char buf[BUFSIZ]; /* not intended to be threadsafe */
+ static char hex[] = "0123456789ABCDEF";
+ unsigned char *p, *o;
+ int i;
+
+ for (p = s, o = buf, i = 0; i < len; p++, i++) {
+ if ((*p == '%') || (*p <= ' ') || (*p >= 126)) {
+ /* index keys are stored with their trailing NUL */
+ if ((*p == 0) && (i == len-1))
+ continue;
+ if ((flags & FMT_LF_OK) && (*p == '\n')) {
+ *o++ = '\n';
+ *o++ = '\t';
+ } else if ((flags && FMT_SP_OK) && (*p == ' ')) {
+ *o++ = ' ';
+ } else {
+ *o++ = '%';
+ *o++ = hex[*p / 16];
+ *o++ = hex[*p % 16];
+ }
+ } else {
+ *o++ = *p;
+ }
+ if (o-buf > BUFSIZ-5) {
+ /* out of space */
+ strcpy(o, " ...");
+ i = len;
+ }
+ }
+ *o = 0;
+ return (char *)buf;
+}
+
+static char *format(unsigned char *s, int len)
+{
+ return format_raw(s, len, 0);
+}
+
+static char *format_entry(unsigned char *s, int len)
+{
+ return format_raw(s, len, FMT_LF_OK | FMT_SP_OK);
+}
+
+static char *idl_format(IDL *idl)
+{
+ static char *buf = NULL;
+ uint32 i;
+
+ if (buf == NULL) {
+ buf = (char *)malloc(MAX_BUFFER);
+ if (buf == NULL)
+ return "?";
+ }
+
+ buf[0] = 0;
+ for (i = 0; i < idl->used; i++) {
+ sprintf((char *)buf + strlen(buf), "%d ", idl->id[i]);
+
+ if (strlen(buf) > (size_t)MAX_BUFFER-20) {
+ strcat(buf, "...");
+ return (char *)buf;
+ }
+ }
+ return (char *)buf;
+}
+
+
+/*** Copied from cl5_api.c: _cl5ReadString ***/
+void _cl5ReadString (char **str, char **buff)
+{
+ if (str)
+ {
+ int len = strlen (*buff);
+
+ if (len)
+ {
+ *str = strdup(*buff);
+ (*buff) += len + 1;
+ }
+ else /* just null char - skip it */
+ {
+ *str = NULL;
+ (*buff) ++;
+ }
+ }
+ else /* just skip this string */
+ {
+ (*buff) += strlen (*buff) + 1;
+ }
+}
+
+/** print_attr - print attribute name followed by one value.
+ assume the value stored as null terminated string.
+*/
+void print_attr(char *attrname, char **buff)
+{
+ char *val = NULL;
+
+ _cl5ReadString(&val, buff);
+ if(attrname != NULL || val != NULL) {
+ db_printf("\t");
+ }
+
+ if(attrname) {
+ db_printf("%s: ", attrname);
+ }
+ if(val != NULL) {
+ db_printf("%s\n", val);
+ free(val);
+ }
+}
+
+/*** Copied from cl5_api.c: _cl5ReadMods ***/
+/* mods format:
+ -----------
+ <4 byte mods count><mod1><mod2>...
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte count>
+ {<4 byte size><value1><4 byte size><value2>... ||
+ <null terminated str1> <null terminated str2>...}
+ */
+void _cl5ReadMod(char **buff);
+
+void _cl5ReadMods(char **buff)
+{
+ char *pos = *buff;
+ uint32 i;
+ uint32 mod_count;
+
+ /* need to copy first, to skirt around alignment problems on certain
+ architectures */
+ memcpy((char *)&mod_count, *buff, sizeof(mod_count));
+ mod_count = ntohl(mod_count);
+ pos += sizeof (mod_count);
+
+
+ for (i = 0; i < mod_count; i++)
+ {
+ _cl5ReadMod (&pos);
+ }
+
+ *buff = pos;
+}
+
+
+/** print_ber_attr - print one line of attribute, the value was stored
+ in ber format, length followed by string.
+*/
+void print_ber_attr(char* attrname, char** buff)
+{
+ char *val = NULL;
+ uint32 bv_len;
+
+ memcpy((char *)&bv_len, *buff, sizeof(bv_len));
+ bv_len = ntohl(bv_len);
+ *buff += sizeof (uint32);
+ if (bv_len > 0) {
+
+ db_printf("\t\t");
+
+ if(attrname != NULL) {
+ db_printf("%s: ", attrname);
+ }
+
+ val = malloc(bv_len + 1);
+ memcpy (val, *buff, bv_len);
+ val[bv_len] = 0;
+ *buff += bv_len;
+ db_printf("%s\n", val);
+ free(val);
+ }
+}
+
+static ID id_stored_to_internal(char* b)
+{
+ ID i;
+ i = (ID)b[3] & 0x000000ff;
+ i |= (((ID)b[2]) << 8) & 0x0000ff00;
+ i |= (((ID)b[1]) << 16) & 0x00ff0000;
+ i |= ((ID)b[0]) << 24;
+ return i;
+}
+
+void _cl5ReadMod(char **buff)
+{
+ char *pos = *buff;
+ uint32 i;
+ uint32 val_count;
+ char *type = NULL;
+ int op;
+
+ op = (*pos) & 0x000000FF;
+ pos ++;
+ _cl5ReadString (&type, &pos);
+
+ /* need to do the copy first, to skirt around alignment problems on
+ certain architectures */
+ memcpy((char *)&val_count, pos, sizeof(val_count));
+ val_count = ntohl(val_count);
+ pos += sizeof (uint32);
+
+ for (i = 0; i < val_count; i++)
+ {
+ print_ber_attr(type, &pos);
+ }
+
+ (*buff) = pos;
+ free(type);
+}
+
+/*
+ *** Copied from cl5_api:cl5DBData2Entry ***
+ Data in db format:
+ ------------------
+ <1 byte version><1 byte change_type><sizeof time_t time><null terminated dbid>
+ <null terminated csn><null terminated uniqueid><null terminated targetdn>
+ [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte value count>
+ <4 byte value size><value1><4 byte value size><value2>
+*/
+void print_changelog(unsigned char *data, int len)
+{
+ uint8_t version;
+ unsigned long operation_type;
+ char *pos = (char *)data;
+ time_t thetime;
+ uint32 replgen;
+
+ /* read byte of version */
+ version = *((uint8_t *)pos);
+ if (version != 5)
+ {
+ db_printf("Invalid changelog db version %i\nWorks for version 5 only.", version);
+ exit(1);
+ }
+ pos += sizeof(version);
+
+ /* read change type */
+ operation_type = (unsigned long)(*(uint8_t *)pos);
+ pos ++;
+
+ /* need to do the copy first, to skirt around alignment problems on
+ certain architectures */
+ memcpy((char *)&thetime, pos, sizeof(thetime));
+ replgen = ntohl((uint32)(thetime));
+ pos += sizeof (time_t);
+ db_printf("\treplgen: %ld %s", replgen, ctime((time_t *)&replgen));
+
+ /* read csn */
+ print_attr("csn", &pos);
+ /* read UniqueID */
+ print_attr("uniqueid", &pos);
+
+ /* figure out what else we need to read depending on the operation type */
+ switch (operation_type)
+ {
+ case SLAPI_OPERATION_ADD:
+ print_attr("parentuniqueid", &pos);
+ print_attr("dn", &pos);
+ /* convert mods to entry */
+ db_printf("\toperation: add\n");
+ _cl5ReadMods(&pos);
+ break;
+
+ case SLAPI_OPERATION_MODIFY:
+ print_attr("dn", &pos);
+ db_printf("\toperation: modify\n");
+ _cl5ReadMods(&pos);
+ break;
+
+ case SLAPI_OPERATION_MODRDN:
+ print_attr("dn", &pos);
+ print_attr("newrdn", &pos);
+ pos ++;
+ print_attr("dn", &pos);
+ print_attr("uniqueid", &pos);
+ db_printf("\toperation: modrdn\n");
+ _cl5ReadMods(&pos);
+ break;
+
+ case SLAPI_OPERATION_DELETE:
+ print_attr("dn", &pos);
+ db_printf("\toperation: delete\n");
+ break;
+
+ default:
+ db_printf("Failed to format entry\n");
+ break;
+ }
+}
+
+int indexfile = 0, entryfile = 0, changelogfile = 0;
+int lengths_only = 0;
+uint32 min_display = 0;
+int show_recno = 0;
+int show_cnt = 0;
+int verbose = 0;
+long pres_cnt = 0;
+long eq_cnt = 0;
+long app_cnt = 0;
+long sub_cnt = 0;
+long match_cnt = 0;
+long ind_cnt = 0;
+long allids_cnt = 0;
+long other_cnt = 0;
+
+
+static void display_item(DBC *cursor, DBT *key, DBT *data)
+{
+ IDL *idl;
+ int ret = 0;
+
+ if (indexfile) {
+ idl = idl_make(data);
+ if (idl == NULL) {
+ printf("\t(illegal idl)\n");
+ return;
+ }
+ if (show_recno) {
+ cursor->c_get(cursor, key, data, DB_GET_RECNO);
+ printf("[%5d] ", *(db_recno_t *)(data->data));
+ }
+
+ /* fetch all other id's too */
+ while (ret == 0) {
+ ret = cursor->c_get(cursor, key, data, DB_NEXT_DUP);
+ if (ret == 0)
+ idl = idl_append(idl, *(uint32 *)(data->data));
+ }
+ if (ret == DB_NOTFOUND)
+ ret = 0;
+ if (ret != 0) {
+ printf("Failure while looping dupes: %s\n",
+ db_strerror(ret));
+ exit(1);
+ }
+
+ if (idl->max == 0) {
+ /* allids */
+ if ( allids_cnt == 0 && show_cnt) {
+ printf("The following index keys reached allids:\n");
+ }
+ printf("%-40s(allids)\n", format(key->data, key->size));
+ allids_cnt++;
+ } else {
+ if (lengths_only) {
+ if (idl->used >= min_display)
+ printf("%-40s%d\n",
+ format(key->data, key->size), idl->used);
+ } else if (!show_cnt) {
+ printf("%s\n", format(key->data, key->size));
+ printf("\t%s\n", idl_format(idl));
+ }
+ }
+
+ if ( show_cnt ) {
+ char firstchar;
+
+ firstchar = ((char*)key->data)[0];
+
+ switch ( firstchar ) {
+ case '+':
+ pres_cnt += idl->used;
+ break;
+
+ case '=':
+ eq_cnt += idl->used;
+ break;
+
+ case '~':
+ app_cnt += idl->used;
+ break;
+
+ case '*':
+ sub_cnt += idl->used;
+ break;
+
+ case ':':
+ match_cnt += idl->used;
+ break;
+
+ case '\\':
+ ind_cnt += idl->used;
+ break;
+
+ default:
+ other_cnt += idl->used;
+ break;
+ }
+ }
+ idl_free(idl);
+ return;
+ }
+
+ if (changelogfile) {
+ /* changelog db file */
+ printf("\ndbid: %s\n", format(key->data, key->size));
+ print_changelog(data->data, data->size);
+ return;
+ }
+
+ if (entryfile) {
+ /* id2entry file */
+ ID entry_id = id_stored_to_internal(key->data);
+ printf("id %d\n", entry_id);
+ printf("\t%s\n", format_entry(data->data, data->size));
+ } else {
+ /* user didn't tell us what kind of file, dump it raw */
+ printf("%s\n", format(key->data, key->size));
+ printf("\t%s\n", format(data->data, data->size));
+ }
+}
+
+
+static void usage(char *argv0)
+{
+ printf("\n%s - scan a db file and dump the contents\n", argv0);
+ printf(" -f <filename> specify db file\n");
+ printf(" -i dump as an index file\n");
+ printf(" -e dump as an entry (id2entry) file\n");
+#ifndef DB26
+ printf(" -c dump as a changelog file\n");
+#endif
+ printf(" -l <size> max length of dumped id list (default %d)\n",
+ MAX_BUFFER);
+ printf(" -n display idl lengths only (not contents)\n");
+ printf(" -G <n> (when used with -n) only display index entries with\n");
+ printf(" more than <n> ids\n");
+ printf(" -r show libdb record numbers, too\n");
+ printf(" -k <key> lookup only a specific key\n");
+ printf(" -K <entry_id> lookup only a specific entry id\n");
+ printf(" -s Summary of index counts\n");
+ printf("\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ DB_ENV *env = NULL;
+ DB *db = NULL;
+ DBC *cursor = NULL;
+ char *filename = NULL;
+ DBT key = {0}, data = {0};
+ int ret;
+ char *find_key = NULL;
+ uint32 entry_id = -1;
+ int c;
+
+#ifndef DB26
+ while ((c = getopt(argc, argv, "f:iecl:nG:srk:K:hv")) != EOF) {
+#else
+ while ((c = getopt(argc, argv, "f:iel:nG:srk:K:h")) != EOF) {
+#endif
+ switch (c) {
+ case 'f':
+ filename = optarg;
+ break;
+ case 'i':
+ indexfile = 1;
+ break;
+ case 'e':
+ entryfile = 1;
+ break;
+ case 'c':
+ changelogfile = 1;
+ break;
+ case 'l':
+ MAX_BUFFER = atoi(optarg);
+ break;
+ case 'n':
+ lengths_only = 1;
+ break;
+ case 'G':
+ min_display = atoi(optarg)+1;
+ break;
+ case 'r':
+ show_recno = 1;
+ break;
+ case 's':
+ show_cnt = 1;
+ break;
+ case 'k':
+ find_key = optarg;
+ break;
+ case 'K':
+ entry_id = (uint32)atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ }
+ }
+
+ if(filename == NULL) {
+ usage(argv[0]);
+ }
+
+
+#ifdef DB26
+ env = (DB_ENV *)calloc(sizeof(DB_ENV), 1);
+ if (! env) {
+ printf("Can't create dbenv: %s\n", strerror(ret));
+ exit(1);
+ }
+ ret = db_appinit(NULL, NULL, env, DB_CREATE);
+ if (ret != 0) {
+ printf("Can't init db26: %s\n", db_strerror(ret));
+ exit(1);
+ }
+#else
+ ret = db_env_create(&env, 0);
+ if (ret != 0) {
+ printf("Can't create dbenv: %s\n", db_strerror(ret));
+ exit(1);
+ }
+ ret = env->open(env, NULL, DB_CREATE|DB_INIT_MPOOL|DB_PRIVATE, 0);
+ if (ret != 0) {
+ printf("Can't open dbenv: %s\n", db_strerror(ret));
+ exit(1);
+ }
+#endif
+
+#ifdef DB26
+ ret = db_open(filename, DB_UNKNOWN, 0, 0, env, NULL, &db);
+ if (ret != 0) {
+ printf("Can't open db2 file '%s': %s\n", filename, db_strerror(ret));
+ exit(1);
+ }
+#else
+ ret = db_create(&db, env, 0);
+ if (ret != 0) {
+ printf("Can't create db handle: %d\n", ret);
+ exit(1);
+ }
+ ret = db->open(db, NULL, filename, NULL, DB_UNKNOWN, DB_RDONLY, 0);
+ if (ret != 0) {
+ printf("Can't open db file '%s': %s\n", filename, db_strerror(ret));
+ exit(1);
+ }
+#endif
+
+ /* cursor through the db */
+
+ ret = db->cursor(db, NULL, &cursor, 0);
+ if (ret != 0) {
+ printf("Can't create db cursor: %s\n", db_strerror(ret));
+ exit(1);
+ }
+ ret = cursor->c_get(cursor, &key, &data, DB_FIRST);
+ if (ret == DB_NOTFOUND) {
+ printf("Empty database!\n");
+ exit(0);
+ }
+ if (ret != 0) {
+ printf("Can't get first cursor: %s\n", db_strerror(ret));
+ exit(1);
+ }
+
+ if (find_key) {
+ key.size = strlen(find_key)+1;
+ key.data = find_key;
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ /* could be a key that doesn't have the trailing null? */
+ key.size--;
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ printf("Can't find key '%s'\n", find_key);
+ exit(1);
+ }
+ }
+ ret = cursor->c_get(cursor, &key, &data, DB_SET);
+ if (ret != 0) {
+ printf("Can't set cursor to returned item: %s\n",
+ db_strerror(ret));
+ exit(1);
+ }
+ display_item(cursor, &key, &data);
+ key.size = 0;
+ key.data = NULL;
+ } else if (entry_id != -1) {
+ key.size = sizeof(entry_id);
+ key.data = &entry_id;
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ printf("Can't set cursor to returned item: %s\n",
+ db_strerror(ret));
+ exit(1);
+ }
+ display_item(cursor, &key, &data);
+ key.size = 0;
+ key.data = NULL;
+ } else {
+ while (ret == 0) {
+ /* display */
+ display_item(cursor, &key, &data);
+
+ ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
+ if ((ret != 0) && (ret != DB_NOTFOUND)) {
+ printf("Bizarre error: %s\n", db_strerror(ret));
+ exit(1);
+ }
+ }
+ }
+
+ ret = cursor->c_close(cursor);
+ if (ret != 0) {
+ printf("Can't close the cursor (?!): %s\n", db_strerror(ret));
+ exit(1);
+ }
+
+ ret = db->close(db, 0);
+ if (ret != 0) {
+ printf("Unable to close db file: %s\n", db_strerror(ret));
+ exit(1);
+ }
+
+ if ( show_cnt ) {
+
+ if ( allids_cnt > 0 ) {
+ printf("Index keys that reached ALLIDs threshold: %ld\n", allids_cnt);
+ }
+
+ if ( pres_cnt > 0 ) {
+ printf("Presence index keys: %ld\n", pres_cnt);
+ }
+
+ if ( eq_cnt > 0 ) {
+ printf("Equality index keys: %ld\n", eq_cnt);
+ }
+
+ if ( app_cnt > 0 ) {
+ printf("Approximate index keys: %ld\n", app_cnt);
+ }
+
+ if ( sub_cnt > 0 ) {
+ printf("Substring index keys: %ld\n", sub_cnt);
+ }
+
+ if ( match_cnt > 0 ) {
+ printf("Match index keys: %ld\n", match_cnt);
+ }
+
+ if ( ind_cnt > 0 ) {
+ printf("Indirect index keys: %ld\n", ind_cnt);
+ }
+
+ if ( other_cnt > 0 ) {
+ printf("This file contains %ld number of unknown type ( possible corruption)\n",other_cnt);
+ }
+
+ }
+#ifndef DB26
+ ret = env->close(env, 0);
+ if (ret != 0) {
+ printf("Unable to shutdown libdb: %s\n", db_strerror(ret));
+ exit(1);
+ }
+#endif
+
+ return 0;
+}