diff --git a/management/univention-directory-listener/debian/control b/management/univention-directory-listener/debian/control index 069dc6a..59ad765 100644 --- a/management/univention-directory-listener/debian/control +++ b/management/univention-directory-listener/debian/control @@ -7,6 +7,7 @@ Build-Depends: debhelper (>= 9), ucslint-univention, libssl-dev, libldap2-dev, + liblmdb-dev, libdb3-dev, python-all-dev, libunivention-debug-dev (>= 0.8), @@ -26,7 +27,8 @@ Depends: ${misc:Depends}, ${shlibs:Depends}, univention-runit, python2.7-univention, python2.7-univention-debug, - python-univention-lib (>= 1.0.25-1) + python-univention-lib (>= 1.0.25-1), + lmdb-utils Conflicts: univention-ldap-listener (<< 2.2.2) Description: UCS - Directory Listener UCS Directory Listener is a client for the UCS diff --git a/management/univention-directory-listener/debian/rules b/management/univention-directory-listener/debian/rules index 1112e52..a4c8a33 100755 --- a/management/univention-directory-listener/debian/rules +++ b/management/univention-directory-listener/debian/rules @@ -50,6 +50,7 @@ override_dh_install: mv debian/univention-directory-listener/usr/sbin/listener debian/univention-directory-listener/usr/sbin/univention-directory-listener mv debian/univention-directory-listener/usr/sbin/dump debian/univention-directory-listener/usr/sbin/univention-directory-listener-dump mv debian/univention-directory-listener/usr/sbin/verify debian/univention-directory-listener/usr/sbin/univention-directory-listener-verify + mv debian/univention-directory-listener/usr/sbin/convert debian/univention-directory-listener/usr/sbin/univention-directory-listener-convert mv debian/univention-directory-listener/usr/sbin/listener-ctrl debian/univention-directory-listener/usr/sbin/univention-directory-listener-ctrl override_dh_installinit: diff --git a/management/univention-directory-listener/debian/univention-directory-listener.install b/management/univention-directory-listener/debian/univention-directory-listener.install index ee9481e..e20cca7 100644 --- a/management/univention-directory-listener/debian/univention-directory-listener.install +++ b/management/univention-directory-listener/debian/univention-directory-listener.install @@ -1,6 +1,7 @@ src/listener usr/sbin/ src/dump usr/sbin/ src/verify usr/sbin/ +src/convert usr/sbin/ src/listener-ctrl usr/sbin/ python/get_notifier_id.py usr/share/univention-directory-listener/ python/ldap_server.py usr/lib/univention-directory-listener/system/ diff --git a/management/univention-directory-listener/debian/univention-directory-listener.postinst b/management/univention-directory-listener/debian/univention-directory-listener.postinst index 50b0f56..a4eb71e 100644 --- a/management/univention-directory-listener/debian/univention-directory-listener.postinst +++ b/management/univention-directory-listener/debian/univention-directory-listener.postinst @@ -67,6 +67,32 @@ fi call_joinscript 03univention-directory-listener.inst +convert_to_lmdb() { + if [ ! -f /var/lib/univention-directory-listener/cache/data.mdb ]; then + if [ ! -d /var/lib/univention-directory-listener/cache ]; then + mkdir /var/lib/univention-directory-listener/cache + fi + if [ -f /var/lib/univention-directory-listener/cache.db ]; then + running=1 + /etc/init.d/univention-directory-listener status >/dev/null \ + && running=1 || running=0 + + if [ "$running" -eq 1 ]; then + /etc/init.d/univention-directory-listener stop + fi + + univention-directory-listener-convert \ + /var/lib/univention-directory-listener/cache.db + + chown -R listener.nogroup \ + /var/lib/univention-directory-listener/cache + + if [ "$running" -eq 1 ]; then + /etc/init.d/univention-directory-listener start + fi + fi + fi +} if [ "$1" = "configure" -a -n "$2" ]; then if dpkg --compare-versions "$2" lt 6.0.7 && dpkg --compare-versions "$2" gt 6.0.0; then @@ -76,6 +102,11 @@ if [ "$1" = "configure" -a -n "$2" ]; then mv /var/lib/univention-directory-listener/__* /var/univention-backup/listener-cache-ucs_3.0-ms1/ mv /var/lib/univention-directory-listener/log.* /var/univention-backup/listener-cache-ucs_3.0-ms1/ fi + + if dpkg --compare-versions "$2" lt 11.0.0-7; then + convert_to_lmdb + fi + /etc/init.d/univention-directory-listener crestart fi diff --git a/management/univention-directory-listener/src/Makefile b/management/univention-directory-listener/src/Makefile index 09a56ae..9fc1940 100644 --- a/management/univention-directory-listener/src/Makefile +++ b/management/univention-directory-listener/src/Makefile @@ -31,13 +31,12 @@ # CC ?= gcc -DB_LDLIBS := -ldb3 -DB_CFLAGS := -I/usr/include/db3 -DWITH_DB3 -DB_OBJS := cache.o cache_entry.o cache_lowlevel.o base64.o filter.o +DB_LDLIBS := -llmdb +DB_OBJS := cache.o cache_dn.o cache_entry.o cache_lowlevel.o base64.o filter.o LDAP_LDLIBS := -lldap -llber -CFLAGS += -Wall -Werror -D_FILE_OFFSET_BITS=64 $(DB_CFLAGS) +CFLAGS += -Wall -Werror -D_FILE_OFFSET_BITS=64 LDLIBS := -luniventiondebug -luniventionconfig -licuuc LISTENER_LDLIBS := -luniventionpolicy $(LDAP_LDLIBS) -lpython2.7 $(DB_LDLIBS) LISTENER_OBJS := main.o notifier.o transfile.o handlers.o change.o network.o signals.o select_server.o utils.o $(DB_OBJS) @@ -46,9 +45,11 @@ DUMP_OBJS := dump.o dump_signals.o utils.o $(DB_OBJS) DEMO_OBJS := demo.o network.o utils.o VERIFY_LDLIBS := $(LDAP_LDLIBS) $(DB_LDLIBS) VERIFY_OBJS := verify.o dump_signals.o utils.o $(DB_OBJS) +CONVERT_LDLIBS := $(LDAP_LDLIBS) $(DB_LDLIBS) -ldb3 +CONVERT_OBJS := convert.o cache_bdb.o dump_signals.o utils.o $(DB_OBJS) .PHONY: all -all: listener dump verify +all: listener dump verify convert listener: $(LISTENER_OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(LISTENER_LDLIBS) @@ -62,6 +63,12 @@ demo: $(DEMO_OBJS) verify: $(VERIFY_OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(VERIFY_LDLIBS) +convert: $(CONVERT_OBJS) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CONVERT_LDLIBS) + +cache_bdb.o: cache_bdb.c + $(CC) $(CFLAGS) -I/usr/include/db3 -DWITH_DB3 -c $^ + .PHONY: clean clean: - $(RM) *.o listener dump demo verify *.db3 *.db42 + $(RM) *.o listener dump demo verify convert *.db3 *.db42 diff --git a/management/univention-directory-listener/src/README b/management/univention-directory-listener/src/README index f330001..412c521 100644 --- a/management/univention-directory-listener/src/README +++ b/management/univention-directory-listener/src/README @@ -24,9 +24,17 @@ References +-- network.c +-- verify.o + +-- cache_bdb.c + +-- cache_lowlevel.c + +-- cache_entry.c + ++-- convert.o +-- cache.c +-- cache_lowlevel.c +-- cache_entry.c + +-- cache_bdb.c + +-- cache_lowlevel.c + +-- cache_entry.c Files ----- @@ -49,6 +57,7 @@ notifier.c The name notifier might be misleading. The main function "notifier_listener" uses the listener network API (network.c) to receive updates from a notifier and calls the "change" functions. signals.c Signal handlers are initialized and defined here. +cache_bdb.c legacy bdb version of cache.c, required for convert. main.c The listener daemon demo.c Demo program for the notifier client API. diff --git a/management/univention-directory-listener/src/cache.c b/management/univention-directory-listener/src/cache.c index a6a2ab7..df66d01 100644 --- a/management/univention-directory-listener/src/cache.c +++ b/management/univention-directory-listener/src/cache.c @@ -67,15 +67,17 @@ #include #include #include -#include +#include #include #include +#include #include #include #include "common.h" #include "cache.h" +#include "cache_dn.h" #include "cache_lowlevel.h" #include "cache_entry.h" #include "network.h" @@ -83,18 +85,19 @@ #include "filter.h" #include "utils.h" -#define MASTER_KEY "__master__" -#define MASTER_KEY_SIZE (sizeof MASTER_KEY) +// #define MASTER_KEY "__master__" +static size_t MASTER_KEY=0; +#define MASTER_KEY_SIZE (sizeof(DNID)) char *cache_dir = "/var/lib/univention-directory-listener"; char *ldap_dir = "/var/lib/univention-ldap"; CacheMasterEntry cache_master_entry; -DB *dbp; -#ifdef WITH_DB42 -DB_ENV *dbenvp; -#endif +MDB_env *env; +MDB_dbi id2dn; +MDB_dbi id2entry; +int mdb_readonly = 0; static FILE *lock_fp=NULL; static struct filter cache_filter; @@ -110,17 +113,19 @@ static void setup_cache_filter(void) { } } -#ifdef WITH_DB42 -static void cache_panic_call(DB_ENV *dbenvp, int errval) -{ - exit(1); +/* +int mdb_message_func(const char *msg, void *ctx) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "%s\n", msg); + return MDB_SUCCESS; } -#endif +*/ -static void cache_error_message(const char *errpfx, char *msg) +void cache_error_message(int rv, char *msg) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "database error: %s", msg); + "%s: failed: %s (%d)\n", + msg, mdb_strerror(rv), rv); } int cache_lock(void) @@ -130,7 +135,7 @@ int cache_lock(void) assert(!lock_fp); - rv = snprintf(lock_file, PATH_MAX, "%s/cache.db.lock", cache_dir); + rv = snprintf(lock_file, PATH_MAX, "%s/cache.lock", cache_dir); if (rv < 0 || rv >= PATH_MAX) abort(); @@ -152,66 +157,101 @@ int cache_lock(void) return fd; } -int cache_init(void) +int cache_init(int mdb_flags) { int rv; - char file[PATH_MAX]; + char mdb_dir[PATH_MAX]; + MDB_txn *cache_init_txn; + int mdb_dbi_flags = MDB_INTEGERKEY; + + if ((mdb_flags & MDB_RDONLY) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "cache_init: MDB_RDONLY"); + mdb_readonly = MDB_RDONLY; + } else { + mdb_dbi_flags |= MDB_CREATE; + } - snprintf(file, PATH_MAX, "%s/cache.db", cache_dir); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_init: start"); -#ifdef WITH_DB42 - if ((rv = db_env_create(&dbenvp, 0)) != 0) { + snprintf(mdb_dir, PATH_MAX, "%s/cache", cache_dir); + + rv = mdb_env_create(&env); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "creating database environment failed"); + "cache_init: creating environment handle failed"); + cache_error_message(rv, "cache_init: mdb_env_create"); return rv; } - dbenvp->set_errcall(dbenvp, cache_error_message); - dbenvp->set_paniccall(dbenvp, cache_panic_call); - if ((rv = dbenvp->open(dbenvp, cache_dir, DB_CREATE | DB_INIT_MPOOL | - /*DB_INIT_LOCK | */DB_INIT_LOG | DB_INIT_TXN | - DB_RECOVER, 0600)) != 0) { + + rv = mdb_env_set_mapsize(env, 1992294400); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "opening database environment failed"); - dbenvp->err(dbenvp, rv, "%s", "environment"); + "cache_init: setting mdb mapsize failed"); + cache_error_message(rv, "cache_init: mdb_env_set_mapsize"); return rv; } - if ((rv = db_create(&dbp, dbenvp, 0)) != 0) { + + rv = mdb_env_set_maxdbs(env, 2); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "creating database handle failed"); + "cache_init: setting mdb maxdbs failed"); + cache_error_message(rv, "cache_init: mdb_env_set_maxdbs"); return rv; } - if ((rv = dbp->open(dbp, NULL, "cache.db", NULL, DB_BTREE, - DB_CREATE | DB_CHKSUM | DB_AUTO_COMMIT, - 0600)) != 0) { + + rv = mdb_env_open(env, mdb_dir, 0, 0600); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "opening database failed"); - dbp->err(dbp, rv, "open"); - // FIXME: free dbp + "cache_init: opening database failed"); + cache_error_message(rv, "cache_init: mdb_env_open"); return rv; } -#else - if ((rv = db_create(&dbp, NULL, 0)) != 0) { - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "creating database handle failed"); + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_init: Transaction begin"); + + rv = mdb_txn_begin(env, NULL, mdb_readonly, &cache_init_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_init: mdb_txn_begin"); + mdb_env_close(env); return rv; } - if ((rv = dbp->open(dbp, file, NULL, DB_BTREE, DB_CREATE, 0600)) != 0) { - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "opening database failed"); - dbp->err(dbp, rv, "open"); - // FIXME: free dbp + + rv = dntree_init(&id2dn, cache_init_txn, mdb_flags); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "dntree_init: mdb_open"); + mdb_txn_abort(cache_init_txn); + mdb_env_close(env); return rv; } - dbp->set_errcall(dbp, cache_error_message); -#endif - setup_cache_filter(); - return 0; -} -void cache_sync(void) { - if (!INIT_ONLY && dbp) { - dbp->sync(dbp, 0); + rv = mdb_dbi_open(cache_init_txn, "id2entry", mdb_dbi_flags, &id2entry); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_init: mdb_open"); + mdb_txn_abort(cache_init_txn); + mdb_dbi_close(env, id2dn); + mdb_env_close(env); + return rv; } + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_init: Transaction commit"); + + rv = mdb_txn_commit(cache_init_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_init: mdb_txn_commit"); + mdb_dbi_close(env, id2dn); + mdb_dbi_close(env, id2entry); + mdb_env_close(env); + return rv; + } + + setup_cache_filter(); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_init: end"); + return 0; } int cache_set_schema_id(const NotifierID value) @@ -219,7 +259,8 @@ int cache_set_schema_id(const NotifierID value) int rv, fd, len; char file[PATH_MAX], buf[15]; - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "Set Schema ID to %ld", value); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, + "Set Schema ID to %ld", value); len = snprintf(buf, sizeof buf, "%ld", value); if (len < 0 || len >= sizeof buf) return len; @@ -299,83 +340,124 @@ int cache_get_int(char *key, NotifierID *value, const long def) int cache_get_master_entry(CacheMasterEntry *master_entry) { - DBT key, data; - int rv; + int rv; + MDB_txn *read_txn; + MDB_val key, data; + + memset(&key, 0, sizeof(MDB_val)); + memset(&data, 0, sizeof(MDB_val)); - memset(&key, 0, sizeof(DBT)); - memset(&data, 0, sizeof(DBT)); + key.mv_data=&MASTER_KEY; + key.mv_size=MASTER_KEY_SIZE; - key.data=MASTER_KEY; - key.size=MASTER_KEY_SIZE; - data.flags = DB_DBT_REALLOC; + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_master_entry: Transaction begin"); - if ((rv=dbp->get(dbp, NULL, &key, &data, 0)) == DB_NOTFOUND) + rv = mdb_txn_begin(env, NULL, MDB_RDONLY, &read_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_get_master_entry: mdb_txn_begin"); return rv; - else if (rv != 0) { + } + + rv = mdb_get(read_txn, id2entry, &key, &data); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_master_entry: got data"); + if (rv == MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_master_entry: Transaction abort"); + mdb_txn_abort(read_txn); + return rv; + } else if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "reading master entry from database failed"); - dbp->err(dbp, rv, "get"); + "cache_get_master_entry: reading master entry from database failed"); + cache_error_message(rv, "cache_get_master_entry: mdb_get"); + mdb_txn_abort(read_txn); return rv; } - if (data.size != sizeof(CacheMasterEntry)) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_master_entry: Transaction abort"); + mdb_txn_abort(read_txn); + + if (data.mv_size != sizeof(CacheMasterEntry)) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "master entry has unexpected length"); + "cache_get_master_entry: master entry has unexpected length"); return 1; } - memcpy(master_entry, data.data, sizeof(CacheMasterEntry)); - free(data.data); + memcpy(master_entry, data.mv_data, sizeof(CacheMasterEntry)); - return 0; + return MDB_SUCCESS; } -int cache_update_master_entry(CacheMasterEntry *master_entry, DB_TXN *dbtxnp) +/* The dbtxnp argument is only used when WITH_DB42 is defined - useless? */ +int cache_update_master_entry(CacheMasterEntry *master_entry, MDB_txn *dbtxnp) { - DBT key, data; - int rv; - int flags; - - memset(&key, 0, sizeof(DBT)); - memset(&data, 0, sizeof(DBT)); + int rv; + MDB_txn *write_txn = dbtxnp; + MDB_val key, data; - key.data=MASTER_KEY; - key.size=MASTER_KEY_SIZE; + memset(&key, 0, sizeof(MDB_val)); + memset(&data, 0, sizeof(MDB_val)); - data.data=(void*)master_entry; - data.size=sizeof(CacheMasterEntry); + key.mv_data=&MASTER_KEY; + key.mv_size=MASTER_KEY_SIZE; -#ifdef WITH_DB42 - if (dbtxnp == NULL) - flags = DB_AUTO_COMMIT; - else -#endif - flags = 0; + data.mv_data=(void*)master_entry; + data.mv_size=sizeof(CacheMasterEntry); - if ((rv=dbp->put(dbp, dbtxnp, &key, &data, flags)) != 0) { + if (!dbtxnp) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_master_entry: Transaction begin"); + } + if (!dbtxnp + && (rv = mdb_txn_begin(env, NULL, 0, &write_txn)) != MDB_SUCCESS) { + cache_error_message(rv, "cache_update_master_entry: mdb_txn_begin"); + return rv; + } + rv = mdb_put(write_txn, id2entry, &key, &data, 0); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "storing master entry in database failed"); - dbp->err(dbp, rv, "put"); + "cache_update_master_entry: storing master entry in database failed"); + cache_error_message(rv, "cache_update_master_entry: mdb_put"); + mdb_txn_abort(write_txn); + return rv; + } + if (!dbtxnp) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_master_entry: Transaction commit"); + } + if (!dbtxnp + && (rv = mdb_txn_commit(write_txn)) != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_update_master_entry: storing master entry in database failed"); + cache_error_message(rv, "cache_update_master_entry: mdb_txn_commit"); return rv; } - cache_sync(); - - return 0; + return MDB_SUCCESS; } -DB_TXN* cache_new_transaction(NotifierID id, char *dn) +MDB_txn *cache_new_transaction(NotifierID id, char *dn) { #ifdef WITH_DB42 - DB_TXN *dbtxnp; + int rv; + MDB_txn *write_txn; CacheMasterEntry master_entry; NotifierID *old_id; - dbenvp->txn_begin(dbenvp, NULL, &dbtxnp, 0); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_new_transaction: Transaction begin"); + rv = mdb_txn_begin(env, NULL, 0, &write_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_new_transaction: mdb_txn_begin"); + return NULL; + } if (id != 0) { - if (cache_get_master_entry(&master_entry) != 0) { - dbtxnp->abort(dbtxnp); + rv = cache_get_master_entry(&master_entry); + if (rv != MDB_SUCCESS) { + mdb_txn_abort(write_txn); return NULL; } @@ -386,82 +468,123 @@ DB_TXN* cache_new_transaction(NotifierID id, char *dn) if (*old_id >= id) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "New ID (%ld) is not greater than old" - " ID (%ld): %s", id, *old_id, dn); - dbtxnp->abort(dbtxnp); + "New ID (%ld) is not greater than old" + " ID (%ld): %s", id, *old_id, dn); + mdb_txn_abort(write_txn); return NULL; } else *old_id = id; - if (cache_update_master_entry(&master_entry, dbtxnp) != 0) { - dbtxnp->abort(dbtxnp); + rv = cache_update_master_entry(&master_entry, write_txn); + if (rv != MDB_SUCCESS) { + mdb_txn_abort(write_txn); return NULL; } } - return dbtxnp; + return write_txn; #else return NULL; #endif } - /* XXX: The NotifierID is passed for future use. Once the journal is implemented, entries other than the most recent one can be returned. At the moment, the id parameters for cache_update_entry, and cache_delete_entry do nothing (at least if WITH_DB42 is undefined) */ -inline int cache_update_entry(NotifierID id, char *dn, CacheEntry *entry) +static inline int cache_update_entry_in_transaction(NotifierID id, char *dn, CacheEntry *entry, MDB_cursor **id2dn_cursor_pp) { - DBT key, data; - DB_TXN *dbtxnp; - int rv = 0; + int rv; + DNID dnid; + MDB_txn *write_txn; + MDB_val key, data; + u_int32_t tmp_size = 0; + + memset(&data, 0, sizeof(MDB_val)); + rv = unparse_entry(&data.mv_data, &tmp_size, entry); + data.mv_size = tmp_size; + if (rv != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_update_entry: unparsing entry failed"); + return rv; + } - memset(&key, 0, sizeof(DBT)); - memset(&data, 0, sizeof(DBT)); + signals_block(); + + rv = dntree_get_id4dn(*id2dn_cursor_pp, dn, &dnid, true); + if (rv != MDB_SUCCESS) { + signals_unblock(); + return rv; + } + + key.mv_data = &dnid; + key.mv_size = sizeof(DNID); - if ((rv=unparse_entry(&data.data, &data.size, entry)) != 0) { + write_txn = mdb_cursor_txn(*id2dn_cursor_pp); + rv = mdb_put(write_txn, id2entry, &key, &data, 0); + if (rv != MDB_SUCCESS) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "unparsing entry failed"); + "cache_update_entry: storing entry in database failed: %s", dn); + cache_error_message(rv, "cache_update_entry: mdb_put"); + signals_unblock(); return rv; } + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "put %zu bytes for %s", data.mv_size, dn); + signals_unblock(); + + return rv; +} + +inline int cache_update_entry(NotifierID id, char *dn, CacheEntry *entry) +{ + int rv; + MDB_txn *write_txn; + MDB_cursor *id2dn_write_cursor_p; - signals_block(); #ifdef WITH_DB42 - dbtxnp = cache_new_transaction(id, dn); - if (dbtxnp == NULL) { - signals_unblock(); - free(data.data); + write_txn = cache_new_transaction(id, dn); + if (write_txn == NULL) { return 1; } #else - dbtxnp = NULL; + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_entry: Transaction begin"); + rv = mdb_txn_begin(env, NULL, 0, &write_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_update_entry: mdb_txn_begin"); + return rv; + } #endif - key.data=dn; - key.size=strlen(dn)+1; - - if ((rv=dbp->put(dbp, dbtxnp, &key, &data, 0)) != 0) { - signals_unblock(); - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "storing entry in database failed: %s", dn); - dbp->err(dbp, rv, "put"); -#ifdef WITH_DB42 - dbtxnp->abort(dbtxnp); -#endif - free(data.data); + rv = mdb_cursor_open(write_txn, id2dn, &id2dn_write_cursor_p); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_update_entry: mdb_cursor_open"); return rv; } - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "put %d bytes for %s", data.size, dn); + rv = cache_update_entry_in_transaction(id, dn, entry, &id2dn_write_cursor_p); -#ifdef WITH_DB42 - dbtxnp->commit(dbtxnp, 0); -#endif - cache_sync(); - signals_unblock(); + mdb_cursor_close(id2dn_write_cursor_p); + + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_entry: Transaction abort"); + mdb_txn_abort(write_txn); + return rv; + } + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_entry: Transaction commit"); + rv = mdb_txn_commit(write_txn); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_update_entry: storing updated entry in database failed"); + cache_error_message(rv, "cache_update_entry: mdb_txn_commit"); + return rv; + } - free(data.data); return rv; } @@ -482,40 +605,91 @@ int cache_update_entry_lower(NotifierID id, char *dn, CacheEntry *entry) return rv; } -int cache_delete_entry(NotifierID id, char *dn) +static inline int cache_delete_entry_in_transaction(NotifierID id, char *dn, MDB_cursor **id2dn_cursor_pp) { - DB_TXN *dbtxnp; - DBT key; - int rv; + int rv; + DNID dnid; + MDB_txn *write_txn; + MDB_val key; + + signals_block(); - memset(&key, 0, sizeof(DBT)); + rv = dntree_get_id4dn(*id2dn_cursor_pp, dn, &dnid, false); + if (rv != MDB_SUCCESS) { + signals_unblock(); + return rv; + } - key.data=dn; - key.size=strlen(dn)+1; + key.mv_data = &dnid; + key.mv_size = sizeof(DNID); - signals_block(); -#ifdef WITH_DB42 - dbtxnp = cache_new_transaction(id, dn); - if (dbtxnp == NULL) { + write_txn = mdb_cursor_txn(*id2dn_cursor_pp); + rv = mdb_del(write_txn, id2entry, &key, 0); + if (rv != MDB_SUCCESS && rv != MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_delete_entry: removing from database failed: %s", dn); + cache_error_message(rv, "cache_delete_entry: mdb_del"); signals_unblock(); - return 1; + return rv; } -#else - dbtxnp = NULL; -#endif - if ((rv=dbp->del(dbp, dbtxnp, &key, 0)) != 0 && rv != DB_NOTFOUND) { + rv = dntree_del_id(*id2dn_cursor_pp, dnid); + if (rv != MDB_SUCCESS) { signals_unblock(); - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "removing from database failed: %s", dn); - dbp->err(dbp, rv, "del"); + return rv; } + signals_unblock(); + + return rv; +} + +int cache_delete_entry(NotifierID id, char *dn) +{ + int rv; + MDB_txn *write_txn; + MDB_cursor *id2dn_write_cursor_p; + #ifdef WITH_DB42 - dbtxnp->commit(dbtxnp, 0); + write_txn = cache_new_transaction(id, dn); + if (write_txn == NULL) { + return 1; + } +#else + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_delete_entry: Transaction begin"); + rv = mdb_txn_begin(env, NULL, 0, &write_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_delete_entry: mdb_txn_begin"); + return rv; + } #endif - cache_sync(); - signals_unblock(); + + rv = mdb_cursor_open(write_txn, id2dn, &id2dn_write_cursor_p); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_delete_entry: mdb_cursor_open"); + return rv; + } + + rv = cache_delete_entry_in_transaction(id, dn, &id2dn_write_cursor_p); + + mdb_cursor_close(id2dn_write_cursor_p); + + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_update_entry: Transaction abort"); + mdb_txn_abort(write_txn); + return rv; + } + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_delete_entry: Transaction commit"); + rv = mdb_txn_commit(write_txn); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_delete_entry: storing entry removal from database failed"); + cache_error_message(rv, "cache_delete_entry: mdb_txn_commit"); + } return rv; } @@ -528,7 +702,7 @@ int cache_delete_entry_lower_upper(NotifierID id, char *dn) // convert to a lowercase dn lower_dn = lower_utf8(dn); - rv=cache_delete_entry(id, lower_dn); + rv = cache_delete_entry(id, lower_dn); if (strcmp(dn, lower_dn) != 0) { mixedcase = true; // try again with original dn @@ -536,60 +710,91 @@ int cache_delete_entry_lower_upper(NotifierID id, char *dn) } free(lower_dn); - if ( mixedcase ) { + if (mixedcase) { return rv?rv2:rv; // if rv was bad (!=0) return rv2, otherwise return rv } else { return rv; } } -int cache_update_or_deleteifunused_entry(NotifierID id, char *dn, CacheEntry *entry) +int cache_update_or_deleteifunused_entry(NotifierID id, char *dn, CacheEntry *entry, MDB_cursor **id2dn_cursor_pp) { if (entry->module_count == 0) - return cache_delete_entry(id, dn); + return cache_delete_entry_in_transaction(id, dn, id2dn_cursor_pp); else - return cache_update_entry(id, dn, entry); + return cache_update_entry_in_transaction(id, dn, entry, id2dn_cursor_pp); } int cache_get_entry(char *dn, CacheEntry *entry) { - DBT key, data; - int rv = 0; - - memset(&key, 0, sizeof(DBT)); - memset(&data, 0, sizeof(DBT)); + int rv; + DNID dnid; + MDB_txn *read_txn; + MDB_cursor *id2dn_read_cursor_p; + MDB_val key, data; + + memset(&key, 0, sizeof(MDB_val)); + memset(&data, 0, sizeof(MDB_val)); memset(entry, 0, sizeof(CacheEntry)); - key.data=dn; - key.size=strlen(dn)+1; - data.flags = DB_DBT_REALLOC; + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_entry: Transaction begin"); - signals_block(); - rv=dbp->get(dbp, NULL, &key, &data, 0); - signals_unblock(); + rv = mdb_txn_begin(env, NULL, MDB_RDONLY, &read_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_get_entry: mdb_txn_begin"); + return rv; + } - if (rv != 0 && rv != DB_NOTFOUND) { - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "reading %s from database failed", dn); - dbp->err(dbp, rv, "get"); + rv = mdb_cursor_open(read_txn, id2dn, &id2dn_read_cursor_p); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_get_entry: mdb_cursor_open"); + mdb_txn_abort(read_txn); return rv; - } else if (rv == DB_NOTFOUND) { - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "no cache entry found for %s", - dn); + } + + rv = dntree_get_id4dn(id2dn_read_cursor_p, dn, &dnid, false); + mdb_cursor_close(id2dn_read_cursor_p); + if (rv == MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_entry: Transaction abort"); + mdb_txn_abort(read_txn); return rv; } - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "got %d bytes for %s", - data.size, dn); + key.mv_data = &dnid; + key.mv_size = sizeof(DNID); + + // signals_block(); // TODO: Is this really required? + rv = mdb_get(read_txn, id2entry, &key, &data); + // signals_unblock(); + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_get_entry: Transaction abort"); + mdb_txn_abort(read_txn); - if ((rv=parse_entry(data.data, data.size, entry)) != 0) { + if (rv == MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "got %zu bytes for %s", data.mv_size, dn); + } else if (rv == MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "cache_get_entry: no cache entry found for %s", dn); + return rv; + } else { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "reading %s from database failed", dn); + cache_error_message(rv, "cache_get_entry: mdb_get"); + return rv; + } + + assert(data.mv_size <= UINT32_MAX); + rv = parse_entry(data.mv_data, data.mv_size, entry); + if (rv != 0) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "parsing entry failed"); - free(data.data); + "cache_get_entry: parsing entry failed"); exit(1); } - free(data.data); return rv; } @@ -606,7 +811,7 @@ int cache_get_entry_lower_upper(char *dn, CacheEntry *entry) } rv = cache_get_entry(lower_dn, entry); - if (rv == DB_NOTFOUND && mixedcase ) { + if (rv == MDB_NOTFOUND && mixedcase ) { // try again with original dn rv = cache_get_entry(dn, entry); } @@ -615,108 +820,198 @@ int cache_get_entry_lower_upper(char *dn, CacheEntry *entry) return rv; } -int cache_first_entry(DBC **cur, char **dn, CacheEntry *entry) +int cache_first_entry(MDB_cursor **id2entry_read_cursor_pp, MDB_cursor **id2dn_read_cursor_pp, char **dn, CacheEntry *entry) { + MDB_txn *read_txn; int rv; - if ((rv=dbp->cursor(dbp, NULL, cur, 0)) != 0) { - dbp->err(dbp, rv, "cursor"); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_first_entry: Transaction begin"); + + rv = mdb_txn_begin(env, NULL, mdb_readonly, &read_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_first_entry: mdb_txn_begin"); + return rv; + } + + rv = mdb_cursor_open(read_txn, id2entry, id2entry_read_cursor_pp); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_first_entry: mdb_cursor_open"); + mdb_txn_abort(read_txn); return rv; } - return cache_next_entry(cur, dn, entry); + rv = mdb_cursor_open(read_txn, id2dn, id2dn_read_cursor_pp); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_first_entry: mdb_cursor_open"); + mdb_txn_abort(read_txn); + return rv; + } + + /* + // mdb_reader_list(env, &mdb_message_func, NULL); + MDB_envinfo stat; + MDB_env *env = mdb_txn_env(read_txn); + mdb_env_info(env, &stat); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "LAST COMMITTED TXN: %zu", stat.me_last_txnid); + */ + + return cache_next_entry(id2entry_read_cursor_pp, id2dn_read_cursor_pp, dn, entry); } int cache_print_entries(char *dn) { - DBT key, data; - DBC *cur; - memset(&key, 0, sizeof(DBT)); - memset(&data, 0, sizeof(DBT)); - key.data = strdup(dn); - key.size = strlen(dn)+1; - key.flags = DB_DBT_REALLOC; - data.flags = DB_DBT_REALLOC; - - dbp->cursor(dbp, NULL, &cur, 0); - cur->c_get(cur, &key, &data, DB_FIRST); - do { - printf("%s\n", (char*)key.data); - } while (cur->c_get(cur, &key, &data, DB_NEXT) == 0); + int rv; + DNID dnid; + MDB_txn *read_txn; + MDB_cursor *id2dn_read_cursor_p; + MDB_cursor *id2entry_read_cursor_p; + MDB_val key, data; + + memset(&key, 0, sizeof(MDB_val)); + memset(&data, 0, sizeof(MDB_val)); + + rv = mdb_txn_begin(env, NULL, MDB_RDONLY, &read_txn); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_print_entries: mdb_txn_begin"); + return rv; + } - cur->c_close(cur); - free(key.data); - free(data.data); - return 0; + rv = mdb_cursor_open(read_txn, id2dn, &id2dn_read_cursor_p); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_print_entries: mdb_cursor_open"); + mdb_txn_abort(read_txn); + return rv; + } + + // rv = cache_dntree_get_id(id2dn_read_cursor_p, dn, &dnid, false); + rv = dntree_get_id4dn(id2dn_read_cursor_p, dn, &dnid, false); + mdb_cursor_close(id2dn_read_cursor_p); + if (rv == MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_print_entries: Transaction abort"); + mdb_txn_abort(read_txn); + return rv; + } + + key.mv_data = &dnid; + key.mv_size = sizeof(DNID); + + rv = mdb_cursor_open(read_txn, id2entry, &id2entry_read_cursor_p); + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_print_entries: mdb_cursor_open"); + mdb_txn_abort(read_txn); + return rv; + } + rv = mdb_cursor_get(id2entry_read_cursor_p, &key, &data, MDB_FIRST); + do { + if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_print_entries: mdb_cursor_get"); + mdb_txn_abort(read_txn); + return rv; + } + printf("%s\n", dn); + printf("%s\n", (char*)data.mv_data); //TODO: Is that what we want? + } while ((rv = mdb_cursor_get(id2entry_read_cursor_p, &key, &data, MDB_NEXT))); + + mdb_cursor_close(id2entry_read_cursor_p); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_print_entries: Transaction abort"); + mdb_txn_abort(read_txn); + return MDB_SUCCESS; } -int cache_next_entry(DBC **cur, char **dn, CacheEntry *entry) +int cache_next_entry(MDB_cursor **id2entry_read_cursor_pp, MDB_cursor **id2dn_read_cursor_pp, char **dn, CacheEntry *entry) { - DBT key, data; + MDB_val key, data; + DNID dnid; int rv; - memset(&key, 0, sizeof(DBT)); - key.flags = DB_DBT_REALLOC; - memset(&data, 0, sizeof(DBT)); - data.flags = DB_DBT_REALLOC; + memset(&key, 0, sizeof(MDB_val)); + memset(&data, 0, sizeof(MDB_val)); - if ((rv=(*cur)->c_get(*cur, &key, &data, DB_NEXT)) == DB_NOTFOUND) { + /* Get the next entry data */ + rv = mdb_cursor_get(*id2entry_read_cursor_pp, &key, &data, MDB_NEXT); + if (rv == MDB_NOTFOUND) { return rv; - } else if (rv != 0) { - dbp->err(dbp, rv, "c_get"); + } else if (rv != MDB_SUCCESS) { + cache_error_message(rv, "cache_next_entry: mdb_cursor_get"); return rv; } - /* skip master entry */ - if (strcmp(key.data, MASTER_KEY) == 0) { - free(key.data); - free(data.data); - return cache_next_entry(cur, dn, entry); + // skip root node + dnid = * (DNID *) key.mv_data; + if (dnid == MASTER_KEY) { + return cache_next_entry(id2entry_read_cursor_pp, id2dn_read_cursor_pp, dn, entry); } - if (*dn) - free(*dn); - *dn = strdup(key.data); - - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "got %d bytes", data.size); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "got %zu bytes", data.mv_size); - if ((rv=parse_entry(data.data, data.size, entry)) != 0) { + assert(data.mv_size <= UINT32_MAX); + rv = parse_entry(data.mv_data, (u_int32_t) data.mv_size, entry); + if (rv != 0) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "parsing entry failed: %s", *dn); - printf("%d\n", data.size); - free(key.data); - free(data.data); + "cache_next_entry: parsing entry failed: %s", *dn); + printf("%zu\n", data.mv_size); return rv; } - free(key.data); - free(data.data); + /* Get the corresponding dn */ + if (*dn) { + free(*dn); + *dn = NULL; + } + + rv = dntree_lookup_dn4id(*id2dn_read_cursor_pp, dnid, dn); + + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_next_entry: DB corruption, DN entry for id %d not found", + *(int*)key.mv_data); + cache_error_message(rv, "cache_next_entry: mdb_get"); + return rv; + } return 0; } -int cache_free_cursor(DBC *cur) +int cache_free_cursor(MDB_cursor *id2entry_read_cursor_pp, MDB_cursor *id2dn_read_cursor_pp) { - return cur->c_close(cur); + int rv = 0; + MDB_txn *read_txn; + + /* + // mdb_reader_list(env, &mdb_message_func, NULL); + MDB_envinfo stat; + MDB_env *env = mdb_txn_env(read_txn); + mdb_env_info(env, &stat); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "LAST COMMITTED TXN: %zu", stat.me_last_txnid); + */ + + read_txn = mdb_cursor_txn(id2entry_read_cursor_pp); + mdb_cursor_close(id2entry_read_cursor_pp); + mdb_cursor_close(id2dn_read_cursor_pp); + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, + "cache_free_cursor: Transaction commit"); + rv = mdb_txn_commit(read_txn); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "cache_free_cursor: Transation commit failed"); + cache_error_message(rv, "cache_free_cursor: mdb_txn_commit"); + } + return rv; } -int cache_close(void) +void cache_close(void) { - int rv = 0; + mdb_close(env, id2dn); + mdb_close(env, id2entry); + mdb_env_close(env); - if (dbp && (rv = dbp->close(dbp, 0)) != 0) { - dbp->err(dbp, rv, "close"); - } - dbp = NULL; -#ifdef WITH_DB42 - if ((rv = dbenvp->close(dbenvp, 0)) != 0) { - univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, - "closing database environment failed"); - } -#endif if (lock_fp != NULL) { fclose(lock_fp); lock_fp = NULL; } - return rv; } diff --git a/management/univention-directory-listener/src/cache.h b/management/univention-directory-listener/src/cache.h index 82003c9..ca51c3e 100644 --- a/management/univention-directory-listener/src/cache.h +++ b/management/univention-directory-listener/src/cache.h @@ -33,7 +33,7 @@ #ifndef _CACHE_H_ #define _CACHE_H_ -#include +#include #include "network.h" #include "cache_entry.h" @@ -49,11 +49,11 @@ typedef struct _CacheMasterEntry { extern CacheMasterEntry cache_master_entry; int cache_lock(void); -int cache_init (void); +int cache_init (int mdb_flags); void cache_sync(void); int cache_get_master_entry (CacheMasterEntry *master_entry); int cache_update_master_entry (CacheMasterEntry *master_entry, - DB_TXN *dptxnp); + MDB_txn *dptxnp); int cache_update_entry (NotifierID id, char *dn, CacheEntry *entry); @@ -66,21 +66,25 @@ int cache_delete_entry_lower_upper (NotifierID id, char *dn); int cache_update_or_deleteifunused_entry (NotifierID id, char *dn, - CacheEntry *entry); + CacheEntry *entry, + MDB_cursor **cur); int cache_get_entry( char *dn, CacheEntry *entry); int cache_get_entry_lower_upper( char *dn, CacheEntry *entry); -int cache_first_entry (DBC **cur, +int cache_first_entry (MDB_cursor **cur, + MDB_cursor **cur_dn, char **dn, CacheEntry *entry); -int cache_next_entry (DBC **cur, +int cache_next_entry (MDB_cursor **cur, + MDB_cursor **cur_dn, char **dn, CacheEntry *entry); -int cache_free_cursor (DBC *cur); -int cache_close (void); +int cache_free_cursor (MDB_cursor *cur, + MDB_cursor *cur_dn); +void cache_close (void); /* deprecated with DB42*/ int cache_set_int (char *key, diff --git a/management/univention-directory-listener/src/cache_bdb.c b/management/univention-directory-listener/src/cache_bdb.c new file mode 100644 index 0000000..b6d383a --- /dev/null +++ b/management/univention-directory-listener/src/cache_bdb.c @@ -0,0 +1,722 @@ +/* + * Univention Directory Listener + * ldap listener caching system + * + * Copyright 2004-2017 Univention GmbH + * + * http://www.univention.de/ + * + * All rights reserved. + * + * The source code of this program is made available + * under the terms of the GNU Affero General Public License version 3 + * (GNU AGPL V3) as published by the Free Software Foundation. + * + * Binary versions of this program provided by Univention to you as + * well as other copyrighted, protected or trademarked materials like + * Logos, graphics, fonts, specific documentations and configurations, + * cryptographic keys etc. are subject to a license agreement between + * you and Univention and not subject to the GNU AGPL V3. + * + * In the case you use this program under the terms of the GNU AGPL V3, + * the program is provided in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License with the Debian GNU/Linux or Univention distribution in file + * /usr/share/common-licenses/AGPL-3; if not, see + * . + */ + +/* How it works: + + LDAP entries are cached here. If a modification takes place, + the new LDAP entry is compared with the cache entry, and both, + old and new entries, are passed to the handler modules. + + Berkeley DB provides a way to store and receive a chunk of data + with a given key. However, we're not working with cache entries + as chunks of data later, but use C structures. So we'll need to + convert the C structure we can work with to chunks of data we + can store in BDB, and the other way around. + + We have decided on a pretty low level, but straightforward + binary format for the chunk of data. For manual error recovery, a + text-based format like LDIF might have been easier. + + The function unparse_entry converts a C structure entry to a data + chunk, parse_entry does the opposite. + + To convert the entry, unparse_entry walks through all values of + all attributes, as well as through the list of modules registered + with it, and write a block for each (attribute, value) pair or + (, module) [write_header]. Each block starts with an entry_header + structure that specified the lengths of the following data. +*/ + + +#define _GNU_SOURCE /* for strndup */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" +#include "cache_bdb.h" +#include "cache_lowlevel.h" +#include "cache_entry.h" +#include "network.h" +#include "signals.h" +#include "filter.h" +#include "utils.h" + +#define MASTER_KEY "__master__" +#define MASTER_KEY_SIZE (sizeof MASTER_KEY) + +char *bdb_cache_dir = "/var/lib/univention-directory-listener"; +char *bdb_ldap_dir = "/var/lib/univention-ldap"; + +BdbCacheMasterEntry bdb_cache_master_entry; + +DB *dbp; +#ifdef WITH_DB42 +DB_ENV *dbenvp; +#endif +static FILE *lock_fp=NULL; + +static struct filter cache_filter; +static struct filter *cache_filters[] = {&cache_filter, NULL}; + +static void setup_cache_filter(void) { + FREE(cache_filter.filter); + FREE(cache_filter.base); + cache_filter.filter = univention_config_get_string("listener/cache/filter"); + if (cache_filter.filter && cache_filter.filter[0]) { + cache_filter.base = univention_config_get_string("ldap/base"); + cache_filter.scope = LDAP_SCOPE_SUBTREE; + } +} + +#ifdef WITH_DB42 +static void bdb_cache_panic_call(DB_ENV *dbenvp, int errval) +{ + exit(1); +} +#endif + +static void bdb_cache_error_message(const char *errpfx, char *msg) +{ + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "database error: %s", msg); +} + +int bdb_cache_lock(void) +{ + int rv, fd; + char lock_file[PATH_MAX]; + + assert(!lock_fp); + + rv = snprintf(lock_file, PATH_MAX, "%s/cache.db.lock", bdb_cache_dir); + if (rv < 0 || rv >= PATH_MAX) + abort(); + + if ((lock_fp = fopen(lock_file, "a+e")) == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "Could not open lock file [%s]", lock_file); + exit(EXIT_FAILURE); + } + fd = fileno(lock_fp); + + if (lockf(fd, F_TLOCK, 0) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "Could not get lock for database [%s]; " + "Is another listener processs running?\n", + lock_file); + exit(EXIT_FAILURE); + } + + return fd; +} + +int bdb_cache_init(void) +{ + int rv; + char file[PATH_MAX]; + + snprintf(file, PATH_MAX, "%s/cache.db", bdb_cache_dir); + +#ifdef WITH_DB42 + if ((rv = db_env_create(&dbenvp, 0)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "creating database environment failed"); + return rv; + } + dbenvp->set_errcall(dbenvp, bdb_cache_error_message); + dbenvp->set_paniccall(dbenvp, bdb_cache_panic_call); + if ((rv = dbenvp->open(dbenvp, bdb_cache_dir, DB_CREATE | DB_INIT_MPOOL | + /*DB_INIT_LOCK | */DB_INIT_LOG | DB_INIT_TXN | + DB_RECOVER, 0600)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "opening database environment failed"); + dbenvp->err(dbenvp, rv, "%s", "environment"); + return rv; + } + if ((rv = db_create(&dbp, dbenvp, 0)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "creating database handle failed"); + return rv; + } + if ((rv = dbp->open(dbp, NULL, "cache.db", NULL, DB_BTREE, + DB_CREATE | DB_CHKSUM | DB_AUTO_COMMIT, + 0600)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "opening database failed"); + dbp->err(dbp, rv, "open"); + // FIXME: free dbp + return rv; + } +#else + if ((rv = db_create(&dbp, NULL, 0)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "creating database handle failed"); + return rv; + } + if ((rv = dbp->open(dbp, file, NULL, DB_BTREE, DB_CREATE, 0600)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "opening database failed"); + dbp->err(dbp, rv, "open"); + // FIXME: free dbp + return rv; + } + dbp->set_errcall(dbp, bdb_cache_error_message); +#endif + setup_cache_filter(); + return 0; +} + +void bdb_cache_sync(void) { + if (dbp) { + dbp->sync(dbp, 0); + } +} + +int bdb_cache_set_schema_id(const NotifierID value) +{ + int rv, fd, len; + char file[PATH_MAX], buf[15]; + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "Set Schema ID to %ld", value); + len = snprintf(buf, sizeof buf, "%ld", value); + if (len < 0 || len >= sizeof buf) + return len; + + rv = snprintf(file, PATH_MAX, "%s/schema/id/id", bdb_ldap_dir); + if (rv < 0 || rv >= PATH_MAX) + return rv; + fd = open(file, O_WRONLY | O_CREAT, 0644); + if (fd < 0) + return fd; + rv = write(fd, buf, len); + if (rv != len) { + close(fd); + return 1; + } + rv = ftruncate(fd, len); + if (rv != 0) + return rv; + rv = close(fd); + if (rv != 0) + return rv; + return 0; +} + +int bdb_cache_get_schema_id(NotifierID *value, const long def) +{ + FILE *fp; + char file[PATH_MAX]; + int rv; + + *value = def; + + snprintf(file, PATH_MAX, "%s/schema/id/id", bdb_ldap_dir); + if ((fp = fopen(file, "r")) == NULL) + return 1; + rv = fscanf(fp, "%ld", value); + return fclose(fp) || (rv != 1); +} + +int bdb_cache_set_int(char *key, const NotifierID value) +{ + int rv; + FILE *fp; + char file[PATH_MAX], tmpfile[PATH_MAX]; + + rv = snprintf(tmpfile, PATH_MAX, "%s/%s.tmp", bdb_cache_dir, key); + if (rv < 0 || rv >= PATH_MAX) + return rv; + if ((fp = fopen(tmpfile, "w")) == NULL) + abort_io("open", tmpfile); + fprintf(fp, "%ld", value); + rv = fclose(fp); + if (rv != 0) + abort_io("close", tmpfile); + + rv = snprintf(file, PATH_MAX, "%s/%s", bdb_cache_dir, key); + if (rv < 0 || rv >= PATH_MAX) + return rv; + rv = rename(tmpfile, file); + return rv; +} + +int bdb_cache_get_int(char *key, NotifierID *value, const long def) +{ + FILE *fp; + char file[PATH_MAX]; + int rv; + + *value = def; + + snprintf(file, PATH_MAX, "%s/%s", bdb_cache_dir, key); + if ((fp = fopen(file, "r")) == NULL) + return 1; + rv = fscanf(fp, "%ld", value); + return fclose(fp) || (rv != 1); +} + +int bdb_cache_get_master_entry(BdbCacheMasterEntry *master_entry) +{ + DBT key, data; + int rv; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data=MASTER_KEY; + key.size=MASTER_KEY_SIZE; + data.flags = DB_DBT_REALLOC; + + if ((rv=dbp->get(dbp, NULL, &key, &data, 0)) == DB_NOTFOUND) + return rv; + else if (rv != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "reading master entry from database failed"); + dbp->err(dbp, rv, "get"); + return rv; + } + + if (data.size != sizeof(BdbCacheMasterEntry)) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "master entry has unexpected length"); + return 1; + } + + memcpy(master_entry, data.data, sizeof(BdbCacheMasterEntry)); + free(data.data); + + return 0; +} + +int bdb_cache_update_master_entry(BdbCacheMasterEntry *master_entry, DB_TXN *dbtxnp) +{ + DBT key, data; + int rv; + int flags; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data=MASTER_KEY; + key.size=MASTER_KEY_SIZE; + + data.data=(void*)master_entry; + data.size=sizeof(BdbCacheMasterEntry); + +#ifdef WITH_DB42 + if (dbtxnp == NULL) + flags = DB_AUTO_COMMIT; + else +#endif + flags = 0; + + if ((rv=dbp->put(dbp, dbtxnp, &key, &data, flags)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "storing master entry in database failed"); + dbp->err(dbp, rv, "put"); + return rv; + } + + bdb_cache_sync(); + + return 0; +} + +DB_TXN* bdb_cache_new_transaction(NotifierID id, char *dn) +{ +#ifdef WITH_DB42 + DB_TXN *dbtxnp; + BdbCacheMasterEntry master_entry; + NotifierID *old_id; + + dbenvp->txn_begin(dbenvp, NULL, &dbtxnp, 0); + + if (id != 0) { + if (bdb_cache_get_master_entry(&master_entry) != 0) { + dbtxnp->abort(dbtxnp); + return NULL; + } + + if (strcmp(dn, "cn=Subschema") == 0) + old_id = &master_entry.schema_id; + else + old_id = &master_entry.id; + + if (*old_id >= id) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "New ID (%ld) is not greater than old" + " ID (%ld): %s", id, *old_id, dn); + dbtxnp->abort(dbtxnp); + return NULL; + } else + *old_id = id; + + if (bdb_cache_update_master_entry(&master_entry, dbtxnp) != 0) { + dbtxnp->abort(dbtxnp); + return NULL; + } + } + + return dbtxnp; +#else + return NULL; +#endif +} + + +/* XXX: The NotifierID is passed for future use. Once the journal is + implemented, entries other than the most recent one can be returned. + At the moment, the id parameters for bdb_cache_update_entry, and + bdb_cache_delete_entry do nothing (at least if WITH_DB42 is undefined) */ +inline int bdb_cache_update_entry(NotifierID id, char *dn, CacheEntry *entry) +{ + DBT key, data; + DB_TXN *dbtxnp; + int rv = 0; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + if ((rv=unparse_entry(&data.data, &data.size, entry)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "unparsing entry failed"); + return rv; + } + + + signals_block(); +#ifdef WITH_DB42 + dbtxnp = bdb_cache_new_transaction(id, dn); + if (dbtxnp == NULL) { + signals_unblock(); + free(data.data); + return 1; + } +#else + dbtxnp = NULL; +#endif + + key.data=dn; + key.size=strlen(dn)+1; + + if ((rv=dbp->put(dbp, dbtxnp, &key, &data, 0)) != 0) { + signals_unblock(); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "storing entry in database failed: %s", dn); + dbp->err(dbp, rv, "put"); +#ifdef WITH_DB42 + dbtxnp->abort(dbtxnp); +#endif + free(data.data); + return rv; + } + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "put %d bytes for %s", data.size, dn); + + +#ifdef WITH_DB42 + dbtxnp->commit(dbtxnp, 0); +#endif + bdb_cache_sync(); + signals_unblock(); + + free(data.data); + return rv; +} + +int bdb_cache_update_entry_lower(NotifierID id, char *dn, CacheEntry *entry) +{ + char *lower_dn; + int rv = 0; + + if (cache_filter.filter && cache_entry_ldap_filter_match(cache_filters, dn, entry)) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "Not caching %s, filtered out.", dn); + return rv; + } + + lower_dn = lower_utf8(dn); + rv = bdb_cache_update_entry(id, lower_dn, entry); + + free(lower_dn); + return rv; +} + +int bdb_cache_delete_entry(NotifierID id, char *dn) +{ + DB_TXN *dbtxnp; + DBT key; + int rv; + + memset(&key, 0, sizeof(DBT)); + + key.data=dn; + key.size=strlen(dn)+1; + + signals_block(); +#ifdef WITH_DB42 + dbtxnp = bdb_cache_new_transaction(id, dn); + if (dbtxnp == NULL) { + signals_unblock(); + return 1; + } +#else + dbtxnp = NULL; +#endif + + if ((rv=dbp->del(dbp, dbtxnp, &key, 0)) != 0 && rv != DB_NOTFOUND) { + signals_unblock(); + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "removing from database failed: %s", dn); + dbp->err(dbp, rv, "del"); + } + +#ifdef WITH_DB42 + dbtxnp->commit(dbtxnp, 0); +#endif + bdb_cache_sync(); + signals_unblock(); + + return rv; +} + +int bdb_cache_delete_entry_lower_upper(NotifierID id, char *dn) +{ + char *lower_dn; + bool mixedcase = false; + int rv, rv2; + + // convert to a lowercase dn + lower_dn = lower_utf8(dn); + rv=bdb_cache_delete_entry(id, lower_dn); + if (strcmp(dn, lower_dn) != 0) { + mixedcase = true; + // try again with original dn + rv2=bdb_cache_delete_entry(id, dn); + } + + free(lower_dn); + if ( mixedcase ) { + return rv?rv2:rv; // if rv was bad (!=0) return rv2, otherwise return rv + } else { + return rv; + } +} + +int bdb_cache_update_or_deleteifunused_entry(NotifierID id, char *dn, CacheEntry *entry) +{ + if (entry->module_count == 0) + return bdb_cache_delete_entry(id, dn); + else + return bdb_cache_update_entry(id, dn, entry); +} + +int bdb_cache_get_entry(char *dn, CacheEntry *entry) +{ + DBT key, data; + int rv = 0; + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + memset(entry, 0, sizeof(CacheEntry)); + + key.data=dn; + key.size=strlen(dn)+1; + data.flags = DB_DBT_REALLOC; + + signals_block(); + rv=dbp->get(dbp, NULL, &key, &data, 0); + signals_unblock(); + + if (rv != 0 && rv != DB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "reading %s from database failed", dn); + dbp->err(dbp, rv, "get"); + return rv; + } else if (rv == DB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "no cache entry found for %s", + dn); + return rv; + } + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "got %d bytes for %s", + data.size, dn); + + if ((rv=parse_entry(data.data, data.size, entry)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "parsing entry failed"); + free(data.data); + exit(1); + } + + free(data.data); + return rv; +} + +int bdb_cache_get_entry_lower_upper(char *dn, CacheEntry *entry) +{ + char *lower_dn; + bool mixedcase = false; + int rv; + + // convert to a lowercase dn + lower_dn = lower_utf8(dn); + if (strcmp(dn, lower_dn) != 0) { + mixedcase = true; + } + + rv = bdb_cache_get_entry(lower_dn, entry); + if (rv == DB_NOTFOUND && mixedcase ) { + // try again with original dn + rv = bdb_cache_get_entry(dn, entry); + } + + free(lower_dn); + return rv; +} + +int bdb_cache_first_entry(DBC **cur, char **dn, CacheEntry *entry) +{ + int rv; + + if ((rv=dbp->cursor(dbp, NULL, cur, 0)) != 0) { + dbp->err(dbp, rv, "cursor"); + return rv; + } + + return bdb_cache_next_entry(cur, dn, entry); +} + +int bdb_cache_print_entries(char *dn) +{ + DBT key, data; + DBC *cur; + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.data = strdup(dn); + key.size = strlen(dn)+1; + key.flags = DB_DBT_REALLOC; + data.flags = DB_DBT_REALLOC; + + dbp->cursor(dbp, NULL, &cur, 0); + cur->c_get(cur, &key, &data, DB_FIRST); + do { + printf("%s\n", (char*)key.data); + } while (cur->c_get(cur, &key, &data, DB_NEXT) == 0); + + cur->c_close(cur); + free(key.data); + free(data.data); + return 0; +} + +int bdb_cache_next_entry(DBC **cur, char **dn, CacheEntry *entry) +{ + DBT key, data; + int rv; + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_REALLOC; + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_REALLOC; + + if ((rv=(*cur)->c_get(*cur, &key, &data, DB_NEXT)) == DB_NOTFOUND) { + return rv; + } else if (rv != 0) { + dbp->err(dbp, rv, "c_get"); + return rv; + } + + /* skip master entry */ + if (strcmp(key.data, MASTER_KEY) == 0) { + free(key.data); + free(data.data); + return bdb_cache_next_entry(cur, dn, entry); + } + + if (*dn) + free(*dn); + *dn = strdup(key.data); + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "got %d bytes", data.size); + + if ((rv=parse_entry(data.data, data.size, entry)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "parsing entry failed: %s", *dn); + printf("%d\n", data.size); + free(key.data); + free(data.data); + return rv; + } + + free(key.data); + free(data.data); + + return 0; +} + +int bdb_cache_free_cursor(DBC *cur) +{ + return cur->c_close(cur); +} + +int bdb_cache_close(void) +{ + int rv = 0; + + if (dbp && (rv = dbp->close(dbp, 0)) != 0) { + dbp->err(dbp, rv, "close"); + } + dbp = NULL; +#ifdef WITH_DB42 + if ((rv = dbenvp->close(dbenvp, 0)) != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "closing database environment failed"); + } +#endif + if (lock_fp != NULL) { + fclose(lock_fp); + lock_fp = NULL; + } + return rv; +} diff --git a/management/univention-directory-listener/src/cache_bdb.h b/management/univention-directory-listener/src/cache_bdb.h new file mode 100644 index 0000000..07762b6 --- /dev/null +++ b/management/univention-directory-listener/src/cache_bdb.h @@ -0,0 +1,94 @@ +/* + * Univention Directory Listener + * header information for cache.c + * + * Copyright 2004-2017 Univention GmbH + * + * http://www.univention.de/ + * + * All rights reserved. + * + * The source code of this program is made available + * under the terms of the GNU Affero General Public License version 3 + * (GNU AGPL V3) as published by the Free Software Foundation. + * + * Binary versions of this program provided by Univention to you as + * well as other copyrighted, protected or trademarked materials like + * Logos, graphics, fonts, specific documentations and configurations, + * cryptographic keys etc. are subject to a license agreement between + * you and Univention and not subject to the GNU AGPL V3. + * + * In the case you use this program under the terms of the GNU AGPL V3, + * the program is provided in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License with the Debian GNU/Linux or Univention distribution in file + * /usr/share/common-licenses/AGPL-3; if not, see + * . + */ + +#ifndef _CACHE_BDB_H_ +#define _CACHE_BDB_H_ + +#include + +#include "network.h" +#include "cache_entry.h" + +extern char *bdb_cache_dir; +extern char *bdb_ldap_dir; + +typedef struct _BdbCacheMasterEntry { + NotifierID id; + NotifierID schema_id; +} BdbCacheMasterEntry; +extern BdbCacheMasterEntry bdb_cache_master_entry; + +int bdb_cache_lock(void); +int bdb_cache_init (void); +void bdb_cache_sync(void); +int bdb_cache_get_master_entry (BdbCacheMasterEntry *master_entry); +int bdb_cache_update_master_entry (BdbCacheMasterEntry *master_entry, + DB_TXN *dptxnp); +int bdb_cache_update_entry (NotifierID id, + char *dn, + CacheEntry *entry); +inline int bdb_cache_update_entry_lower (NotifierID id, + char *dn, + CacheEntry *entry); +int bdb_cache_delete_entry (NotifierID id, + char *dn); +int bdb_cache_delete_entry_lower_upper (NotifierID id, + char *dn); +int bdb_cache_update_or_deleteifunused_entry (NotifierID id, + char *dn, + CacheEntry *entry); +int bdb_cache_get_entry( + char *dn, + CacheEntry *entry); +int bdb_cache_get_entry_lower_upper( + char *dn, + CacheEntry *entry); +int bdb_cache_first_entry (DBC **cur, + char **dn, + CacheEntry *entry); +int bdb_cache_next_entry (DBC **cur, + char **dn, + CacheEntry *entry); +int bdb_cache_free_cursor (DBC *cur); +int bdb_cache_close (void); + +/* deprecated with DB42*/ +int bdb_cache_set_int (char *key, + const NotifierID value); +int bdb_cache_get_int (char *key, + NotifierID *value, + const long def); + +int bdb_cache_get_schema_id(NotifierID *value, const long def); +int bdb_cache_set_schema_id(const NotifierID value); + +#endif /* _CACHE_BDB_H_ */ diff --git a/management/univention-directory-listener/src/cache_dn.c b/management/univention-directory-listener/src/cache_dn.c new file mode 100644 index 0000000..5763d7b --- /dev/null +++ b/management/univention-directory-listener/src/cache_dn.c @@ -0,0 +1,566 @@ +/* + * example implementation for mdb dntree adjecency list + * + * Copyright 2016 Univention GmbH + * Copyright 2016 Arvid Requate + * + * http://www.univention.de/ + * + * All rights reserved. + * + * The source code of this program is made available + * under the terms of the GNU Affero General Public License version 3 + * (GNU AGPL V3) as published by the Free Software Foundation. + * + * Binary versions of this program provided by Univention to you as + * well as other copyrighted, protected or trademarked materials like + * Logos, graphics, fonts, specific documentations and configurations, + * cryptographic keys etc. are subject to a license agreement between + * you and Univention and not subject to the GNU AGPL V3. + * + * In the case you use this program under the terms of the GNU AGPL V3, + * the program is provided in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License with the Debian GNU/Linux or Univention distribution in file + * /usr/share/common-licenses/AGPL-3; if not, see + * . + */ + +/* + * Derived From: This code is derived from dn2id.c written by Howard Chu. + * The code of dn2id.c has been published under OpenLDAP Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cache_dn.h" +#include + +int mdb_dupsort(const MDB_val *a, const MDB_val *b) +{ + int diff; + subDN *sdn_a, *sdn_b; + + sdn_a = (subDN *)a->mv_data; + sdn_b = (subDN *)b->mv_data; + + diff = sdn_a->type - sdn_b->type; + if (diff) { + return diff; + } + if (sdn_a->type == SUBDN_TYPE_LINK) { + return strcmp(sdn_a->data, sdn_b->data); + } else { + return 0; + } +} + +unsigned int num_rdns(LDAPDN dn) { + unsigned int iRDN; + + for (iRDN=0; dn[iRDN]; iRDN++); + + return iRDN; +} + +/* climb the dntree */ +static int dntree_lookup_id4ldapdn(MDB_cursor *cur, LDAPDN dn, DNID *dnid_out, int *found_out) +{ + int rv, iRDN, found; + MDB_val key, data; + DNID parent, id = 0; + char *rdn; + subDN *subdn; + + key.mv_size = sizeof(DNID); + + found = 0; + iRDN = num_rdns(dn); + for (iRDN--; iRDN>=0; iRDN--) { + rv = ldap_rdn2str(dn[iRDN], &rdn, LDAP_DN_FORMAT_LDAPV3); + if (rv != LDAP_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_rdn2str failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + } + + key.mv_data = &parent; + parent = id; + + data.mv_size = sizeof(subDN) + strlen(rdn); + subdn = malloc(data.mv_size); + if (subdn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: Malloc failed", __func__); + ldap_memfree(rdn); + abort(); + } + subdn->type = SUBDN_TYPE_LINK; + strcpy(subdn->data, rdn); + ldap_memfree(rdn); + data.mv_data = subdn; + + rv = mdb_cursor_get(cur, &key, &data, MDB_GET_BOTH); + free(subdn); + + if (rv == MDB_NOTFOUND) { + break; + } + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_get failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + }; + + subdn = (subDN *) data.mv_data; + id = subdn->id; + found++; + } + + if (rv == MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "%s: found id=%lu", + __func__, id); + } else if (rv != MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: failed: %s (%d)", + __func__, mdb_strerror(rv), rv); + } + + *dnid_out = id; + if (found_out) { + *found_out = found; + }; + + return rv; +} + +int dntree_lookup_dn4id(MDB_cursor *cur, DNID dnid, char **dn) +{ + int rv; + MDB_val key, data; + subDN *subdn; + DNID id = dnid; + + key.mv_size = sizeof(DNID); + key.mv_data = &id; + + data.mv_size = sizeof(subDN); + subdn = malloc(data.mv_size); + if (subdn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: Malloc failed", __func__); + abort(); + } + subdn->type = SUBDN_TYPE_NODE; + data.mv_data = subdn; + + rv = mdb_cursor_get(cur, &key, &data, MDB_GET_BOTH); + free(subdn); + data.mv_data = NULL; + data.mv_size = 0; + + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_get (MDB_GET_BOTH): %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + + // Workaround for (ITS#8393) LMDB - MDB_GET_BOTH broken on non-dup value + rv = mdb_cursor_get(cur, &key, &data, MDB_GET_CURRENT); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_get: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + + /* + * or simply use this instead of MDB_GET_BOTH + MDB_GET_CURRENT: + MDB_txn *txn; + MDB_dbi dbi; + txn = mdb_cursor_txn(cur); + dbi = mdb_cursor_dbi(cur); + rv = mdb_get(txn, dbi, &key, &data); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_get: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + */ + + subdn = (subDN *) data.mv_data; + + *dn = malloc(strlen(subdn->data) + 1); + if (*dn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: Malloc failed", __func__); + abort(); + } + strcpy(*dn, subdn->data); + + return rv; +} + +static int next_free_dnid(MDB_cursor *cur, DNID *dnid_out) +{ + int rv; + MDB_val key; + + rv = mdb_cursor_get(cur, &key, NULL, MDB_LAST); + if (rv == MDB_SUCCESS) { + *dnid_out = (*(DNID *) key.mv_data) + 1; + return rv; + } else if (rv == MDB_NOTFOUND) { + *dnid_out = 1; + return MDB_SUCCESS; + } else { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_get: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + } +} + +static int dntree_add_id(MDB_cursor *write_cursor_p, DNID child, LDAPDN dn, DNID parent) +{ + int rv; + MDB_val key, data; + DNID id; + char *rdn_str; + char *dn_str; + size_t dn_len; + size_t rdn_len; + subDN *subdn; + + key.mv_size = sizeof(DNID); + key.mv_data = &id; + id = parent; + + rv = ldap_dn2str(dn, &dn_str, LDAP_DN_FORMAT_LDAPV3); + if (rv != LDAP_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_dn2str failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + } + + rv = ldap_rdn2str(dn[0], &rdn_str, LDAP_DN_FORMAT_LDAPV3); + if (rv != LDAP_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_rdn2str failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + ldap_memfree(dn_str); + return rv; + } + + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "%s: child=%lu, parent=%lu: \"%s\"", + __func__, child, parent, rdn_str); + + dn_len = strlen(dn_str); + subdn = malloc(sizeof(subDN) + dn_len); + rdn_len = strlen(rdn_str); + assert(dn_len >= rdn_len); + data.mv_size = sizeof(subDN) + rdn_len; + if (subdn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: Malloc failed", __func__); + ldap_memfree(dn_str); + ldap_memfree(rdn_str); + abort(); + } + subdn->type = SUBDN_TYPE_LINK; + subdn->id = child; + strcpy(subdn->data, rdn_str); + ldap_memfree(rdn_str); + data.mv_data = subdn; + + // Store subdn link + rv = mdb_cursor_put(write_cursor_p, &key, &data, MDB_NODUPDATA); + + // Store subdn node + if (rv == MDB_SUCCESS) { + id = child; + + data.mv_size = sizeof(subDN) + dn_len; + subdn->type = SUBDN_TYPE_NODE; + subdn->id = parent; // backlink + strcpy(subdn->data, dn_str); + + rv = mdb_cursor_put(write_cursor_p, &key, &data, MDB_NODUPDATA); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_put failed for child %lu: %s (%d)", + __func__, id, mdb_strerror(rv), rv); + } + } else { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_put failed for parent %lu: %s (%d)", + __func__, id, mdb_strerror(rv), rv); + } + ldap_memfree(dn_str); + free(subdn); + + return rv; +} + +static int dntree_has_children(MDB_cursor *local_cursor, DNID dnid) +{ + int rv; + size_t values; + MDB_val key, data; + + key.mv_size = sizeof(DNID); + key.mv_data = &dnid; + + rv = mdb_cursor_get(local_cursor, &key, &data, MDB_SET); + if (rv != MDB_SUCCESS) { + return rv; + } + + rv = mdb_cursor_count(local_cursor, &values); + if (rv == MDB_SUCCESS && values < 2) { + rv = MDB_NOTFOUND; + } + return rv; +} + +int dntree_del_id(MDB_cursor *write_cursor_p, DNID dnid) +{ + int rv; + MDB_val key, data; + MDB_cursor *local_read_cursor_p; + MDB_txn *txn; + MDB_dbi dbi; + + txn = mdb_cursor_txn(write_cursor_p); + dbi = mdb_cursor_dbi(write_cursor_p); + rv = mdb_cursor_open(txn, dbi, &local_read_cursor_p); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_open: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + } + + rv = dntree_has_children(local_read_cursor_p, dnid); + if (rv == MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: delete failed:" + " subordinate objects must be deleted first", + __func__); + return -1; + } else if (rv != MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: dntree_has_children failed: %s (%d)", + __func__, mdb_strerror(rv), rv); + return -1; + } + mdb_cursor_close(local_read_cursor_p); + + // dntree_lookup_id4ldapdn positioned cursor on SUBDN_TYPE_LINK node below parent's key + rv = mdb_cursor_del(write_cursor_p, 0); + + if (rv == MDB_SUCCESS) { + key.mv_size = sizeof(DNID); + key.mv_data = &dnid; + rv = mdb_cursor_get(write_cursor_p, &key, &data, MDB_SET); + if (rv == MDB_SUCCESS) { + rv = mdb_cursor_del(write_cursor_p, 0); + } + } + + return rv; +} + +int dntree_del_ldapdn(MDB_cursor *write_cursor_p, LDAPDN dn) +{ + int rv; + DNID id; + + rv = dntree_lookup_id4ldapdn(write_cursor_p, dn, &id, NULL); + if (rv != MDB_SUCCESS) { + return rv; + } + + return dntree_del_id(write_cursor_p, id); +} + + +static int dntree_get_id4ldapdn(MDB_cursor *write_cursor_p, LDAPDN dn, DNID *dnid_out) +{ + int rv, found; + DNID id, parent = 0; + LDAPDN pdn; + + if (dn == NULL) { + *dnid_out = 0; + return MDB_SUCCESS; + } + + rv = dntree_lookup_id4ldapdn(write_cursor_p, dn, &id, &found); + if (rv == MDB_NOTFOUND) { + pdn = &dn[1]; + if (num_rdns(pdn) != found) { + rv = dntree_get_id4ldapdn(write_cursor_p, pdn, &parent); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: failed: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + } + } else { + parent = id; + } + rv = next_free_dnid(write_cursor_p, &id); + if (rv != MDB_SUCCESS) { + return rv; + } + rv = dntree_add_id(write_cursor_p, id, dn, parent); + } + + *dnid_out = id; + + return rv; +} + +int dntree_get_id4dn(MDB_cursor *id2dn_cursor_p, char *dn, DNID *dnid, bool create) +{ + int rv; + LDAPDN ldapdn; + + rv = ldap_str2dn(dn, &ldapdn, LDAP_DN_FORMAT_LDAP); + if (rv != LDAP_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_str2dn failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + } + + if (ldapdn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_str2dn NULL: %s (%d): %s", + __func__, ldap_err2string(rv), rv, dn); + return 1; + } + + if (create == true) { + rv = dntree_get_id4ldapdn(id2dn_cursor_p, ldapdn, dnid); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: failed for: %s", + __func__, dn); + } + + } else { + rv = dntree_lookup_id4ldapdn(id2dn_cursor_p, ldapdn, dnid, NULL); + if (rv == MDB_NOTFOUND) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "%s: DN %s not in id2dn", + __func__, dn); + } + } + + ldap_dnfree(ldapdn); + return rv; +} + +int dntree_del_dn(MDB_cursor *write_cursor_p, char *dn) +{ + int rv; + LDAPDN ldapdn; + + rv = ldap_str2dn(dn, &ldapdn, LDAP_DN_FORMAT_LDAP); + if (rv != LDAP_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_str2dn failed: %s (%d)", + __func__, ldap_err2string(rv), rv); + return rv; + } + + if (ldapdn == NULL) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: ldap_str2dn NULL: %s (%d): %s", + __func__, ldap_err2string(rv), rv, dn); + return 1; + } + + rv = dntree_del_ldapdn(write_cursor_p, ldapdn); + + ldap_dnfree(ldapdn); + return rv; +} + + +int dntree_init(MDB_dbi *dbi_p, MDB_txn *cache_init_txn_p, int mdb_flags) +{ + int rv; + MDB_val key, data; + MDB_cursor *cur; + int mdb_dbi_flags = MDB_INTEGERKEY|MDB_DUPSORT; + bool readonly = (mdb_flags & MDB_RDONLY) != 0; + + if (!readonly) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, + "dntree_init: MDB_RDONLY"); + mdb_dbi_flags |= MDB_CREATE; + } + + rv = mdb_dbi_open(cache_init_txn_p, "id2dn", mdb_dbi_flags, dbi_p); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_dbi_open: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + rv = mdb_set_dupsort(cache_init_txn_p, *dbi_p, mdb_dupsort); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_set_dupsort: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + + if (readonly) { + return MDB_SUCCESS; + } + + rv = mdb_cursor_open(cache_init_txn_p, *dbi_p, &cur); + if (rv != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "%s: mdb_cursor_open: %s (%d)", + __func__, mdb_strerror(rv), rv); + return rv; + }; + + // create root node + key.mv_size = sizeof(DNID); + key.mv_data = &(DNID) {0}; + data.mv_size = sizeof(subDN); + data.mv_data = &(subDN) {0, SUBDN_TYPE_NODE, {0}}; + // ignore exists + mdb_cursor_put(cur, &key, &data, MDB_NODUPDATA); + // not strictly required, mdb_txn_commit does it for write txn + mdb_cursor_close(cur); + + return MDB_SUCCESS; +} diff --git a/management/univention-directory-listener/src/cache_dn.h b/management/univention-directory-listener/src/cache_dn.h new file mode 100644 index 0000000..371e6c1 --- /dev/null +++ b/management/univention-directory-listener/src/cache_dn.h @@ -0,0 +1,64 @@ +/* + * header information for dntree.c + * + * Copyright 2016 Univention GmbH + * Copyright 2016 Arvid Requate + * + * http://www.univention.de/ + * + * All rights reserved. + * + * The source code of this program is made available + * under the terms of the GNU Affero General Public License version 3 + * (GNU AGPL V3) as published by the Free Software Foundation. + * + * Binary versions of this program provided by Univention to you as + * well as other copyrighted, protected or trademarked materials like + * Logos, graphics, fonts, specific documentations and configurations, + * cryptographic keys etc. are subject to a license agreement between + * you and Univention and not subject to the GNU AGPL V3. + * + * In the case you use this program under the terms of the GNU AGPL V3, + * the program is provided in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License with the Debian GNU/Linux or Univention distribution in file + * /usr/share/common-licenses/AGPL-3; if not, see + * . + */ + +#ifndef _DNTREE_H_ +#define _DNTREE_H_ + +#include +#include +#include + +typedef struct mdb_ctx { + MDB_env *env; + MDB_dbi dbi; + MDB_txn *txn; + MDB_cursor *cur; +} mdb_ctx; + +#define SUBDN_TYPE_NODE 0 +#define SUBDN_TYPE_LINK 1 + +typedef unsigned char SUBDNTYPE; +typedef unsigned long DNID; +typedef struct subDN { + DNID id; + SUBDNTYPE type; + char data[1]; +} subDN; + +int dntree_init(MDB_dbi *dbi_ptr, MDB_txn *write_txn_p, int mdb_flags); +int dntree_get_id4dn(MDB_cursor *cursor, char *dn, DNID *dnid, bool create); +int dntree_lookup_dn4id(MDB_cursor *cur, DNID dnid, char **dn); +int dntree_del_id(MDB_cursor *cursor, DNID dnid); +int dntree_del_ldapdn(MDB_cursor *cursor, LDAPDN dn); + +#endif /* _DNTREE_H_ */ diff --git a/management/univention-directory-listener/src/change.c b/management/univention-directory-listener/src/change.c index d1f1c8c..418860b 100644 --- a/management/univention-directory-listener/src/change.c +++ b/management/univention-directory-listener/src/change.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include @@ -74,7 +74,8 @@ static int change_init_module(univention_ldap_parameters_t *lp, Handler *handler struct filter **f; int rv; CacheEntry cache_entry, old_cache_entry; - DBC *dbc_cur; + MDB_cursor *id2entry_read_cursor_p = NULL; + MDB_cursor *id2dn_read_cursor_p = NULL; char *dn = NULL; int i; bool abort_init = false; @@ -93,26 +94,26 @@ static int change_init_module(univention_ldap_parameters_t *lp, Handler *handler /* remove old entries for module */ univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, "remove old entries for module %s", handler->name); - for (rv=cache_first_entry(&dbc_cur, &dn, &cache_entry); rv != DB_NOTFOUND; - rv=cache_next_entry(&dbc_cur, &dn, &cache_entry)) { + for (rv=cache_first_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &cache_entry); rv != MDB_NOTFOUND; + rv=cache_next_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &cache_entry)) { if (rv == -1) continue; if (rv < 0) break; cache_entry_module_remove(&cache_entry, handler->name); - cache_update_or_deleteifunused_entry(0, dn, &cache_entry); + cache_update_or_deleteifunused_entry(0, dn, &cache_entry, &id2dn_read_cursor_p); cache_free_entry(&dn, &cache_entry); } univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, "call cache_free_cursor for module %s", handler->name); - cache_free_cursor(dbc_cur); + cache_free_cursor(id2entry_read_cursor_p, id2dn_read_cursor_p); univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_INFO, "initialize schema for module %s", handler->name); /* initialize schema; if it's not in cache yet (it really should be), it'll be initialized on the regular schema check after ldapsearches */ if ((rv = cache_get_entry_lower_upper("cn=Subschema", &cache_entry)) != 0 && - rv != DB_NOTFOUND) { + rv != MDB_NOTFOUND) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "error while reading from database"); return LDAP_OTHER; @@ -184,7 +185,7 @@ static int change_init_module(univention_ldap_parameters_t *lp, Handler *handler for (i=0; ild, dns[i].dn, LDAP_SCOPE_BASE, "(objectClass=*)", attrs, attrsonly0, serverctrls, clientctrls, &timeout, sizelimit0, &res2)); @@ -246,7 +247,6 @@ int change_new_modules(univention_ldap_parameters_t *lp) } } INIT_ONLY = old_init_only; - cache_sync(); return 0; } @@ -267,7 +267,7 @@ int change_update_entry(univention_ldap_parameters_t *lp, NotifierID id, LDAPMes univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "error while converting LDAP entry to cache entry"); goto result; } - if ((rv = cache_get_entry_lower_upper(dn, &old_cache_entry)) != 0 && rv != DB_NOTFOUND) { + if ((rv = cache_get_entry_lower_upper(dn, &old_cache_entry)) != 0 && rv != MDB_NOTFOUND) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "error while reading from database"); rv = LDAP_OTHER; } else { @@ -694,7 +694,7 @@ int change_update_dn(struct transaction *trans) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_PROCESS, "updating '%s' command %c", trans->cur.notify.dn, trans->cur.notify.command); rv = cache_get_entry_lower_upper(trans->cur.notify.dn, &trans->cur.cache); - if (rv != 0 && rv != DB_NOTFOUND) { + if (rv != 0 && rv != MDB_NOTFOUND) { univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_WARN, "error reading database for %s", trans->cur.notify.dn); return LDAP_OTHER; } diff --git a/management/univention-directory-listener/src/convert.c b/management/univention-directory-listener/src/convert.c new file mode 100644 index 0000000..7a02977 --- /dev/null +++ b/management/univention-directory-listener/src/convert.c @@ -0,0 +1,134 @@ +/* + * Univention Directory Listener + * tool to convert the cache. + * + * Copyright 2016-2017 Univention GmbH + * + * http://www.univention.de/ + * + * All rights reserved. + * + * The source code of this program is made available + * under the terms of the GNU Affero General Public License version 3 + * (GNU AGPL V3) as published by the Free Software Foundation. + * + * Binary versions of this program provided by Univention to you as + * well as other copyrighted, protected or trademarked materials like + * Logos, graphics, fonts, specific documentations and configurations, + * cryptographic keys etc. are subject to a license agreement between + * you and Univention and not subject to the GNU AGPL V3. + * + * In the case you use this program under the terms of the GNU AGPL V3, + * the program is provided in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License with the Debian GNU/Linux or Univention distribution in file + * /usr/share/common-licenses/AGPL-3; if not, see + * . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cache_bdb.h" +#include "cache.h" +#include "common.h" + + +static void usage(void) +{ + fprintf(stderr, "Usage: univention-directory-listener-convert [options]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -d debugging\n"); + fprintf(stderr, " -c Listener cache path\n"); +} + + +int main(int argc, char* argv[]) +{ + int debugging = 0; + int rv; + DBC *cur; + char *dn = NULL; + CacheEntry entry; + + univention_debug_init("stderr", 1, 1); + + /* parse arguments */ + for (;;) { + int c; + + c = getopt(argc, argv, "d:c:"); + if (c < 0) + break; + switch (c) { + case 'd': + debugging=atoi(optarg); + break; + case 'c': + cache_dir=strdup(optarg); + bdb_cache_dir=strdup(optarg); + break; + default: + usage(); + exit(1); + } + } + + if (debugging > 1) { + univention_debug_set_level(UV_DEBUG_LISTENER, UV_DEBUG_ALL); + univention_debug_set_level(UV_DEBUG_LDAP, UV_DEBUG_ALL); + univention_debug_set_level(UV_DEBUG_KERBEROS, UV_DEBUG_ALL); + } else if ( debugging > 0 ) { + univention_debug_set_level(UV_DEBUG_LISTENER, UV_DEBUG_INFO); + univention_debug_set_level(UV_DEBUG_LDAP, UV_DEBUG_INFO); + univention_debug_set_level(UV_DEBUG_KERBEROS, UV_DEBUG_INFO); + } else { + univention_debug_set_level(UV_DEBUG_LISTENER, UV_DEBUG_ERROR); + univention_debug_set_level(UV_DEBUG_LDAP, UV_DEBUG_ERROR); + univention_debug_set_level(UV_DEBUG_KERBEROS, UV_DEBUG_ERROR); + } + + rv = bdb_cache_lock(); + rv = cache_lock(); + + if (bdb_cache_init() != 0) + exit(1); + + if (cache_init(0) != 0) + exit(1); + + bdb_cache_get_master_entry(&bdb_cache_master_entry); + rv = cache_update_master_entry(&cache_master_entry, NULL); + + for (rv=bdb_cache_first_entry(&cur, &dn, &entry); rv != DB_NOTFOUND; + rv=bdb_cache_next_entry(&cur, &dn, &entry)) { + if (rv != 0) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "error while reading database"); + } else if ((rv=cache_update_entry_lower(0, dn, &entry)) != MDB_SUCCESS) { + univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ERROR, + "error while writing to database"); + } + cache_free_entry(&dn, &entry); + if (rv < -1) break; + } + + bdb_cache_free_cursor(cur); + + bdb_cache_close(); + cache_close(); + + return 0; +} diff --git a/management/univention-directory-listener/src/dump.c b/management/univention-directory-listener/src/dump.c index f850bec..b3a3104 100644 --- a/management/univention-directory-listener/src/dump.c +++ b/management/univention-directory-listener/src/dump.c @@ -67,7 +67,8 @@ int main(int argc, char* argv[]) char *output_file = NULL; FILE *fp; int rv; - DBC *cur; + MDB_cursor *id2entry_read_cursor_p = NULL; + MDB_cursor *id2dn_read_cursor_p = NULL; char *dn = NULL; CacheEntry entry; @@ -124,7 +125,7 @@ int main(int argc, char* argv[]) "Couldn't open dump file"); exit(1); } - if (cache_init() != 0) + if (cache_init(MDB_RDONLY) != 0) exit(1); if (id_only) { @@ -133,8 +134,8 @@ int main(int argc, char* argv[]) printf("%ld %ld\n", cache_master_entry.id, cache_master_entry.schema_id); } else { - for (rv=cache_first_entry(&cur, &dn, &entry); rv != DB_NOTFOUND; - rv=cache_next_entry(&cur, &dn, &entry)) { + for (rv=cache_first_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &entry); rv != MDB_NOTFOUND; + rv=cache_next_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &entry)) { if ((rv == 0 && !broken_only) || (rv == -1 && broken_only)) { cache_dump_entry(dn, &entry, fp); fprintf(fp, "\n"); @@ -142,7 +143,7 @@ int main(int argc, char* argv[]) cache_free_entry(&dn, &entry); if (rv < -1) break; } - cache_free_cursor(cur); + cache_free_cursor(id2entry_read_cursor_p, id2dn_read_cursor_p); } diff --git a/management/univention-directory-listener/src/main.c b/management/univention-directory-listener/src/main.c index 8d2f11d..dcc9ddb 100644 --- a/management/univention-directory-listener/src/main.c +++ b/management/univention-directory-listener/src/main.c @@ -536,7 +536,8 @@ int main(int argc, char* argv[]) /* XXX: we shouldn't block all signals for so long */ signals_block(); - cache_init(); + if (cache_init(0) != 0) + exit(1); handlers_init(); /* pass data to handlers */ @@ -560,7 +561,7 @@ int main(int argc, char* argv[]) /* if no ID is set, assume the database has just been initialized */ rv = cache_get_master_entry(&cache_master_entry); - if (rv == DB_NOTFOUND) { + if (rv == MDB_NOTFOUND) { cache_get_int("notifier_id", &cache_master_entry.id, -1); if (cache_master_entry.id == -1) { rv = notifier_get_id_s(NULL, &cache_master_entry.id); diff --git a/management/univention-directory-listener/src/verify.c b/management/univention-directory-listener/src/verify.c index 507f245..d6a43e6 100644 --- a/management/univention-directory-listener/src/verify.c +++ b/management/univention-directory-listener/src/verify.c @@ -140,7 +140,8 @@ int main(int argc, char* argv[]) char *bindpw = NULL; char *basedn = NULL; int rv; - DBC *cur; + MDB_cursor *id2entry_read_cursor_p = NULL; + MDB_cursor *id2dn_read_cursor_p = NULL; char *dn; CacheEntry entry; LDAP *ld; @@ -221,11 +222,11 @@ int main(int argc, char* argv[]) } - if (cache_init() != 0) + if (cache_init(MDB_RDONLY) != 0) exit(1); - for (rv=cache_first_entry(&cur, &dn, &entry); rv != DB_NOTFOUND; - rv=cache_next_entry(&cur, &dn, &entry)) { + for (rv=cache_first_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &entry); rv != MDB_NOTFOUND; + rv=cache_next_entry(&id2entry_read_cursor_p, &id2dn_read_cursor_p, &dn, &entry)) { if (rv < -1) break; if (has_dn(dn)) { @@ -245,7 +246,7 @@ int main(int argc, char* argv[]) } add_dn(dn); } - cache_free_cursor(cur); + cache_free_cursor(id2entry_read_cursor_p, id2dn_read_cursor_p); if ((rv=ldap_search_ext_s(ld, basedn, LDAP_SCOPE_SUBTREE, "(objectClass=*)", attrs, attrsonly0, serverctrls, clientctrls, &timeout, sizelimit0, &res)) != LDAP_SUCCESS) { @@ -258,7 +259,7 @@ int main(int argc, char* argv[]) char *dn = ldap_get_dn(ld, cur); if (has_dn(dn)) continue; - if ((rv = cache_get_entry(dn, &entry)) == DB_NOTFOUND) { + if ((rv = cache_get_entry(dn, &entry)) == MDB_NOTFOUND) { printf("E: %s only in LDAP\n", dn); } else if (rv != 0) { printf("E: error reading %s from cache", dn);