Univention Bugzilla – Attachment 8415 Details for
Bug 23367
Build with libdb4.8 or mdb - replace libdb3
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
udl-lmdb.patch
udl-lmdb.patch (text/plain), 88.74 KB, created by
Arvid Requate
on 2017-02-08 20:15:37 CET
(
hide
)
Description:
udl-lmdb.patch
Filename:
MIME Type:
Creator:
Arvid Requate
Created:
2017-02-08 20:15:37 CET
Size:
88.74 KB
patch
obsolete
>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 <sys/types.h> > #include <sys/file.h> > #include <sys/stat.h> >-#include <db.h> >+#include <lmdb.h> > #include <stdbool.h> > #include <assert.h> >+#include <stdint.h> > > #include <univention/debug.h> > #include <univention/config.h> > > #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 <db.h> >+#include <lmdb.h> > > #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 >+ * <http://www.gnu.org/licenses/>. >+ */ >+ >+/* 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 <stdlib.h> >+#include <string.h> >+#include <limits.h> >+#include <unistd.h> >+#include <ctype.h> >+#include <sys/types.h> >+#include <sys/file.h> >+#include <sys/stat.h> >+#include <db.h> >+#include <stdbool.h> >+#include <assert.h> >+ >+#include <univention/debug.h> >+#include <univention/config.h> >+ >+#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 >+ * <http://www.gnu.org/licenses/>. >+ */ >+ >+#ifndef _CACHE_BDB_H_ >+#define _CACHE_BDB_H_ >+ >+#include <db.h> >+ >+#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 >+ * <http://www.gnu.org/licenses/>. >+ */ >+ >+/* >+ * 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 >+ * <http://www.OpenLDAP.org/license.html>. >+ */ >+ >+#include <stdio.h> >+#include <unistd.h> >+#include <malloc.h> >+#include <stdlib.h> >+#include <sys/types.h> >+#include <sys/stat.h> >+#include <unistd.h> >+#include <assert.h> >+#include "cache_dn.h" >+#include <univention/debug.h> >+ >+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 >+ * <http://www.gnu.org/licenses/>. >+ */ >+ >+#ifndef _DNTREE_H_ >+#define _DNTREE_H_ >+ >+#include <stdbool.h> >+#include <lmdb.h> >+#include <ldap.h> >+ >+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 <stdlib.h> > #include <stdbool.h> > #include <string.h> >-#include <db.h> >+#include <lmdb.h> > > #include <univention/debug.h> > #include <univention/config.h> >@@ -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; i<dn_count; i++) { > univention_debug(UV_DEBUG_LISTENER, UV_DEBUG_ALL, "DN: %s", dns[i].dn); > >- if ((rv = cache_get_entry_lower_upper(dns[i].dn, &cache_entry)) == DB_NOTFOUND) { /* XXX */ >+ if ((rv = cache_get_entry_lower_upper(dns[i].dn, &cache_entry)) == MDB_NOTFOUND) { /* XXX */ > LDAPMessage *res2, *first; > int attrsonly0 = 0; > rv = LDAP_RETRY(lp, ldap_search_ext_s(lp->ld, 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 >+ * <http://www.gnu.org/licenses/>. >+ */ >+ >+#include <stdio.h> >+#include <unistd.h> >+#include <string.h> >+#include <stdlib.h> >+#include <fcntl.h> >+#include <dirent.h> >+#include <pwd.h> >+#include <sys/types.h> >+ >+#include <univention/debug.h> >+ >+#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);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 23367
:
3455
|
7901
|
7904
|
7910
|
8415
|
8416