xref: /openbsd/usr.sbin/ldapd/namespace.c (revision 403185e4)
1*403185e4Smartijn /*	$OpenBSD: namespace.c,v 1.20 2020/03/05 07:39:25 martijn Exp $ */
25d465952Smartinh 
35d465952Smartinh /*
45d465952Smartinh  * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh  *
65d465952Smartinh  * Permission to use, copy, modify, and distribute this software for any
75d465952Smartinh  * purpose with or without fee is hereby granted, provided that the above
85d465952Smartinh  * copyright notice and this permission notice appear in all copies.
95d465952Smartinh  *
105d465952Smartinh  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115d465952Smartinh  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125d465952Smartinh  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135d465952Smartinh  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145d465952Smartinh  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155d465952Smartinh  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165d465952Smartinh  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175d465952Smartinh  */
185d465952Smartinh 
195d465952Smartinh #include <sys/types.h>
205d465952Smartinh #include <sys/queue.h>
215d465952Smartinh 
225d465952Smartinh #include <assert.h>
23a9ac9ba1Smartinh #include <errno.h>
245d465952Smartinh #include <stdio.h>
255d465952Smartinh #include <stdlib.h>
265d465952Smartinh #include <string.h>
275d465952Smartinh #include <zlib.h>
285d465952Smartinh 
295d465952Smartinh #include "ldapd.h"
30fdd30f56Sbenno #include "log.h"
315d465952Smartinh 
32*403185e4Smartijn extern char		*datadir;
332f9fe121Sjca 
345d465952Smartinh /* Maximum number of requests to queue per namespace during compaction.
355d465952Smartinh  * After this many requests, we return LDAP_BUSY.
365d465952Smartinh  */
375d465952Smartinh #define MAX_REQUEST_QUEUE	 10000
385d465952Smartinh 
395d465952Smartinh static struct btval	*namespace_find(struct namespace *ns, char *dn);
405d465952Smartinh static void		 namespace_queue_replay(int fd, short event, void *arg);
41898a5208Smartinh static int		 namespace_set_fd(struct namespace *ns,
42898a5208Smartinh 			    struct btree **bt, int fd, unsigned int flags);
435d465952Smartinh 
445d465952Smartinh int
namespace_begin_txn(struct namespace * ns,struct btree_txn ** data_txn,struct btree_txn ** indx_txn,int rdonly)45a9ac9ba1Smartinh namespace_begin_txn(struct namespace *ns, struct btree_txn **data_txn,
46a9ac9ba1Smartinh     struct btree_txn **indx_txn, int rdonly)
475d465952Smartinh {
480279e526Smartinh 	if (ns->data_db == NULL || ns->indx_db == NULL) {
490279e526Smartinh 		errno = EBUSY;	/* namespace is being reopened */
500279e526Smartinh 		return -1;
510279e526Smartinh 	}
52a9ac9ba1Smartinh 
53a9ac9ba1Smartinh 	if ((*data_txn = btree_txn_begin(ns->data_db, rdonly)) == NULL ||
54a9ac9ba1Smartinh 	    (*indx_txn = btree_txn_begin(ns->indx_db, rdonly)) == NULL) {
550279e526Smartinh 		if (errno == ESTALE) {
56a9ac9ba1Smartinh 			if (*data_txn == NULL)
57a9ac9ba1Smartinh 				namespace_reopen_data(ns);
58a9ac9ba1Smartinh 			else
59a9ac9ba1Smartinh 				namespace_reopen_indx(ns);
600279e526Smartinh 			errno = EBUSY;
61a9ac9ba1Smartinh 		}
62a9ac9ba1Smartinh 		log_warn("failed to open transaction");
63a9ac9ba1Smartinh 		btree_txn_abort(*data_txn);
64a9ac9ba1Smartinh 		*data_txn = NULL;
650279e526Smartinh 		return -1;
665d465952Smartinh 	}
675d465952Smartinh 
680279e526Smartinh 	return 0;
69a9ac9ba1Smartinh }
70a9ac9ba1Smartinh 
71a9ac9ba1Smartinh int
namespace_begin(struct namespace * ns)72a9ac9ba1Smartinh namespace_begin(struct namespace *ns)
73a9ac9ba1Smartinh {
74a9ac9ba1Smartinh 	return namespace_begin_txn(ns, &ns->data_txn, &ns->indx_txn, 0);
755d465952Smartinh }
765d465952Smartinh 
775d465952Smartinh int
namespace_commit(struct namespace * ns)785d465952Smartinh namespace_commit(struct namespace *ns)
795d465952Smartinh {
805d465952Smartinh 	if (ns->indx_txn != NULL &&
815d465952Smartinh 	    btree_txn_commit(ns->indx_txn) != BT_SUCCESS) {
825d465952Smartinh 		log_warn("%s(indx): commit failed", ns->suffix);
835d465952Smartinh 		btree_txn_abort(ns->data_txn);
845d465952Smartinh 		ns->indx_txn = ns->data_txn = NULL;
855d465952Smartinh 		return -1;
865d465952Smartinh 	}
875d465952Smartinh 	ns->indx_txn = NULL;
885d465952Smartinh 
895d465952Smartinh 	if (ns->data_txn != NULL &&
905d465952Smartinh 	    btree_txn_commit(ns->data_txn) != BT_SUCCESS) {
915d465952Smartinh 		log_warn("%s(data): commit failed", ns->suffix);
925d465952Smartinh 		ns->data_txn = NULL;
935d465952Smartinh 		return -1;
945d465952Smartinh 	}
955d465952Smartinh 	ns->data_txn = NULL;
965d465952Smartinh 
975d465952Smartinh 	return 0;
985d465952Smartinh }
995d465952Smartinh 
1005d465952Smartinh void
namespace_abort(struct namespace * ns)1015d465952Smartinh namespace_abort(struct namespace *ns)
1025d465952Smartinh {
1035d465952Smartinh 	btree_txn_abort(ns->data_txn);
1045d465952Smartinh 	ns->data_txn = NULL;
1055d465952Smartinh 
1065d465952Smartinh 	btree_txn_abort(ns->indx_txn);
1075d465952Smartinh 	ns->indx_txn = NULL;
1085d465952Smartinh }
1095d465952Smartinh 
1105d465952Smartinh int
namespace_open(struct namespace * ns)1115d465952Smartinh namespace_open(struct namespace *ns)
1125d465952Smartinh {
1135d465952Smartinh 	unsigned int	 db_flags = 0;
1145d465952Smartinh 
1155d465952Smartinh 	assert(ns);
1165d465952Smartinh 	assert(ns->suffix);
1175d465952Smartinh 
1185d465952Smartinh 	if (ns->sync == 0)
1195d465952Smartinh 		db_flags |= BT_NOSYNC;
1205d465952Smartinh 
12195af8abfSderaadt 	if (asprintf(&ns->data_path, "%s/%s_data.db", datadir, ns->suffix) == -1)
1225d465952Smartinh 		return -1;
1235d465952Smartinh 	log_info("opening namespace %s", ns->suffix);
1245d465952Smartinh 	ns->data_db = btree_open(ns->data_path, db_flags | BT_REVERSEKEY, 0644);
1255d465952Smartinh 	if (ns->data_db == NULL)
1265d465952Smartinh 		return -1;
1275d465952Smartinh 
1285d465952Smartinh 	btree_set_cache_size(ns->data_db, ns->cache_size);
1295d465952Smartinh 
13095af8abfSderaadt 	if (asprintf(&ns->indx_path, "%s/%s_indx.db", datadir, ns->suffix) == -1)
1315d465952Smartinh 		return -1;
1325d465952Smartinh 	ns->indx_db = btree_open(ns->indx_path, db_flags, 0644);
1335d465952Smartinh 	if (ns->indx_db == NULL)
1345d465952Smartinh 		return -1;
1355d465952Smartinh 
1365d465952Smartinh 	btree_set_cache_size(ns->indx_db, ns->index_cache_size);
1375d465952Smartinh 
1385d465952Smartinh 	/* prepare request queue scheduler */
1395d465952Smartinh 	evtimer_set(&ns->ev_queue, namespace_queue_replay, ns);
1405d465952Smartinh 
1415d465952Smartinh 	return 0;
1425d465952Smartinh }
1435d465952Smartinh 
144a9ac9ba1Smartinh static int
namespace_reopen(const char * path)145a9ac9ba1Smartinh namespace_reopen(const char *path)
146a9ac9ba1Smartinh {
147a9ac9ba1Smartinh 	struct open_req		 req;
148a9ac9ba1Smartinh 
149a9ac9ba1Smartinh 	log_debug("asking parent to open %s", path);
150a9ac9ba1Smartinh 
15137f4b933Smmcc 	memset(&req, 0, sizeof(req));
152a9ac9ba1Smartinh 	if (strlcpy(req.path, path, sizeof(req.path)) >= sizeof(req.path)) {
153a9ac9ba1Smartinh 		log_warnx("%s: path truncated", __func__);
154a9ac9ba1Smartinh 		return -1;
155a9ac9ba1Smartinh 	}
156a9ac9ba1Smartinh 
157bb773af3Smartinh 	return imsgev_compose(iev_ldapd, IMSG_LDAPD_OPEN, 0, 0, -1, &req,
158a9ac9ba1Smartinh 	    sizeof(req));
159a9ac9ba1Smartinh }
160a9ac9ba1Smartinh 
1615d465952Smartinh int
namespace_reopen_data(struct namespace * ns)1625d465952Smartinh namespace_reopen_data(struct namespace *ns)
1635d465952Smartinh {
164a9ac9ba1Smartinh 	if (ns->data_db != NULL) {
1655d465952Smartinh 		btree_close(ns->data_db);
166a9ac9ba1Smartinh 		ns->data_db = NULL;
167a9ac9ba1Smartinh 		return namespace_reopen(ns->data_path);
168a9ac9ba1Smartinh 	}
169a9ac9ba1Smartinh 	return 1;
1705d465952Smartinh }
1715d465952Smartinh 
1725d465952Smartinh int
namespace_reopen_indx(struct namespace * ns)1735d465952Smartinh namespace_reopen_indx(struct namespace *ns)
1745d465952Smartinh {
175a9ac9ba1Smartinh 	if (ns->indx_db != NULL) {
1765d465952Smartinh 		btree_close(ns->indx_db);
177a9ac9ba1Smartinh 		ns->indx_db = NULL;
178a9ac9ba1Smartinh 		return namespace_reopen(ns->indx_path);
179a9ac9ba1Smartinh 	}
180a9ac9ba1Smartinh 	return 1;
1815d465952Smartinh }
1825d465952Smartinh 
183898a5208Smartinh static int
namespace_set_fd(struct namespace * ns,struct btree ** bt,int fd,unsigned int flags)184898a5208Smartinh namespace_set_fd(struct namespace *ns, struct btree **bt, int fd,
185898a5208Smartinh     unsigned int flags)
186898a5208Smartinh {
187898a5208Smartinh 	log_info("reopening namespace %s (entries)", ns->suffix);
188898a5208Smartinh 	btree_close(*bt);
189898a5208Smartinh 	if (ns->sync == 0)
190898a5208Smartinh 		flags |= BT_NOSYNC;
191898a5208Smartinh 	*bt = btree_open_fd(fd, flags);
192898a5208Smartinh 	if (*bt == NULL)
193898a5208Smartinh 		return -1;
194898a5208Smartinh 	return 0;
195898a5208Smartinh }
196898a5208Smartinh 
197898a5208Smartinh int
namespace_set_data_fd(struct namespace * ns,int fd)198898a5208Smartinh namespace_set_data_fd(struct namespace *ns, int fd)
199898a5208Smartinh {
200898a5208Smartinh 	return namespace_set_fd(ns, &ns->data_db, fd, BT_REVERSEKEY);
201898a5208Smartinh }
202898a5208Smartinh 
203898a5208Smartinh int
namespace_set_indx_fd(struct namespace * ns,int fd)204898a5208Smartinh namespace_set_indx_fd(struct namespace *ns, int fd)
205898a5208Smartinh {
206898a5208Smartinh 	return namespace_set_fd(ns, &ns->indx_db, fd, 0);
207898a5208Smartinh }
208898a5208Smartinh 
2095d465952Smartinh void
namespace_close(struct namespace * ns)2105d465952Smartinh namespace_close(struct namespace *ns)
2115d465952Smartinh {
2125d465952Smartinh 	struct conn		*conn;
2135d465952Smartinh 	struct search		*search, *next;
2145d465952Smartinh 	struct request		*req;
2155d465952Smartinh 
2165d465952Smartinh 	/* Cancel any queued requests for this namespace.
2175d465952Smartinh 	 */
2185d465952Smartinh 	if (ns->queued_requests > 0) {
2195d465952Smartinh 		log_warnx("cancelling %u queued requests on namespace %s",
2205d465952Smartinh 		    ns->queued_requests, ns->suffix);
2215d465952Smartinh 		while ((req = TAILQ_FIRST(&ns->request_queue)) != NULL) {
2225d465952Smartinh 			TAILQ_REMOVE(&ns->request_queue, req, next);
2235d465952Smartinh 			ldap_respond(req, LDAP_UNAVAILABLE);
2245d465952Smartinh 		}
2255d465952Smartinh 	}
2265d465952Smartinh 
2275d465952Smartinh 	/* Cancel any searches on this namespace.
2285d465952Smartinh 	 */
2295d465952Smartinh 	TAILQ_FOREACH(conn, &conn_list, next) {
2305d465952Smartinh 		for (search = TAILQ_FIRST(&conn->searches); search != NULL;
2315d465952Smartinh 		    search = next) {
2325d465952Smartinh 			next = TAILQ_NEXT(search, next);
2335d465952Smartinh 			if (search->ns == ns)
2345d465952Smartinh 				search_close(search);
2355d465952Smartinh 		}
2365d465952Smartinh 	}
2375d465952Smartinh 
2385d465952Smartinh 	free(ns->suffix);
2395d465952Smartinh 	btree_close(ns->data_db);
2405d465952Smartinh 	btree_close(ns->indx_db);
2415d465952Smartinh 	if (evtimer_pending(&ns->ev_queue, NULL))
2425d465952Smartinh 		evtimer_del(&ns->ev_queue);
2435d465952Smartinh 	free(ns->data_path);
2445d465952Smartinh 	free(ns->indx_path);
2455d465952Smartinh 	free(ns);
2465d465952Smartinh }
2475d465952Smartinh 
2485d465952Smartinh void
namespace_remove(struct namespace * ns)2495d465952Smartinh namespace_remove(struct namespace *ns)
2505d465952Smartinh {
2515d465952Smartinh 	TAILQ_REMOVE(&conf->namespaces, ns, next);
2525d465952Smartinh 	namespace_close(ns);
2535d465952Smartinh }
2545d465952Smartinh 
2555d465952Smartinh static struct btval *
namespace_find(struct namespace * ns,char * dn)2565d465952Smartinh namespace_find(struct namespace *ns, char *dn)
2575d465952Smartinh {
2585d465952Smartinh 	struct btval		 key;
2595d465952Smartinh 	static struct btval	 val;
2605d465952Smartinh 
2610279e526Smartinh 	if (ns->data_db == NULL) {
2620279e526Smartinh 		errno = EBUSY;	/* namespace is being reopened */
2630279e526Smartinh 		return NULL;
2640279e526Smartinh 	}
2650279e526Smartinh 
26637f4b933Smmcc 	memset(&key, 0, sizeof(key));
26737f4b933Smmcc 	memset(&val, 0, sizeof(val));
2685d465952Smartinh 
2695d465952Smartinh 	key.data = dn;
2705d465952Smartinh 	key.size = strlen(dn);
2715d465952Smartinh 
2720279e526Smartinh 	if (btree_txn_get(ns->data_db, ns->data_txn, &key, &val) != 0) {
2730279e526Smartinh 		if (errno == ENOENT)
2745d465952Smartinh 			log_debug("%s: dn not found", dn);
2750279e526Smartinh 		else
2760279e526Smartinh 			log_warn("%s", dn);
2770279e526Smartinh 
2780279e526Smartinh 		if (errno == ESTALE)
2790279e526Smartinh 			namespace_reopen_data(ns);
2800279e526Smartinh 
2815d465952Smartinh 		return NULL;
2825d465952Smartinh 	}
2835d465952Smartinh 
2845d465952Smartinh 	return &val;
2855d465952Smartinh }
2865d465952Smartinh 
2875d465952Smartinh struct ber_element *
namespace_get(struct namespace * ns,char * dn)2885d465952Smartinh namespace_get(struct namespace *ns, char *dn)
2895d465952Smartinh {
2905d465952Smartinh 	struct ber_element	*elm;
2915d465952Smartinh 	struct btval		*val;
2925d465952Smartinh 
2935d465952Smartinh 	if ((val = namespace_find(ns, dn)) == NULL)
2945d465952Smartinh 		return NULL;
2955d465952Smartinh 
2965d465952Smartinh 	elm = namespace_db2ber(ns, val);
2975d465952Smartinh 	btval_reset(val);
2985d465952Smartinh 	return elm;
2995d465952Smartinh }
3005d465952Smartinh 
3015d465952Smartinh int
namespace_exists(struct namespace * ns,char * dn)3025d465952Smartinh namespace_exists(struct namespace *ns, char *dn)
3035d465952Smartinh {
3045d465952Smartinh 	struct btval		*val;
3055d465952Smartinh 
3065d465952Smartinh 	if ((val = namespace_find(ns, dn)) == NULL)
3075d465952Smartinh 		return 0;
3085d465952Smartinh 	btval_reset(val);
3095d465952Smartinh 	return 1;
3105d465952Smartinh }
3115d465952Smartinh 
3125d465952Smartinh int
namespace_ber2db(struct namespace * ns,struct ber_element * root,struct btval * val)3135d465952Smartinh namespace_ber2db(struct namespace *ns, struct ber_element *root,
3145d465952Smartinh     struct btval *val)
3155d465952Smartinh {
316f5f15bc0Smartinh 	return ber2db(root, val, ns->compression_level);
3175d465952Smartinh }
3185d465952Smartinh 
3195d465952Smartinh struct ber_element *
namespace_db2ber(struct namespace * ns,struct btval * val)3205d465952Smartinh namespace_db2ber(struct namespace *ns, struct btval *val)
3215d465952Smartinh {
322f5f15bc0Smartinh 	return db2ber(val, ns->compression_level);
3235d465952Smartinh }
3245d465952Smartinh 
3255d465952Smartinh static int
namespace_put(struct namespace * ns,char * dn,struct ber_element * root,int update)3265d465952Smartinh namespace_put(struct namespace *ns, char *dn, struct ber_element *root,
3275d465952Smartinh     int update)
3285d465952Smartinh {
3295d465952Smartinh 	int			 rc;
3305d465952Smartinh 	struct btval		 key, val;
3315d465952Smartinh 
3325d465952Smartinh 	assert(ns != NULL);
333a9ac9ba1Smartinh 	assert(ns->data_txn != NULL);
334a9ac9ba1Smartinh 	assert(ns->indx_txn != NULL);
3355d465952Smartinh 
33637f4b933Smmcc 	memset(&key, 0, sizeof(key));
3375d465952Smartinh 	key.data = dn;
3385d465952Smartinh 	key.size = strlen(dn);
3395d465952Smartinh 
3405d465952Smartinh 	if (namespace_ber2db(ns, root, &val) != 0)
3415d465952Smartinh 		return BT_FAIL;
3425d465952Smartinh 
343b91780d1Smartinh 	rc = btree_txn_put(NULL, ns->data_txn, &key, &val,
3445d465952Smartinh 	    update ? 0 : BT_NOOVERWRITE);
3455d465952Smartinh 	if (rc != BT_SUCCESS) {
3460279e526Smartinh 		if (errno == EEXIST)
3475d465952Smartinh 			log_debug("%s: already exists", dn);
3485d465952Smartinh 		else
3495d465952Smartinh 			log_warn("%s", dn);
350a9ac9ba1Smartinh 		goto done;
3515d465952Smartinh 	}
3525d465952Smartinh 
3535d465952Smartinh 	/* FIXME: if updating, try harder to just update changed indices.
3545d465952Smartinh 	 */
3550279e526Smartinh 	if (update && (rc = unindex_entry(ns, &key, root)) != BT_SUCCESS)
356a9ac9ba1Smartinh 		goto done;
3575d465952Smartinh 
358a9ac9ba1Smartinh 	rc = index_entry(ns, &key, root);
3595d465952Smartinh 
360a9ac9ba1Smartinh done:
3615d465952Smartinh 	btval_reset(&val);
3625d465952Smartinh 	return rc;
3635d465952Smartinh }
3645d465952Smartinh 
3655d465952Smartinh int
namespace_add(struct namespace * ns,char * dn,struct ber_element * root)3665d465952Smartinh namespace_add(struct namespace *ns, char *dn, struct ber_element *root)
3675d465952Smartinh {
3685d465952Smartinh 	return namespace_put(ns, dn, root, 0);
3695d465952Smartinh }
3705d465952Smartinh 
3715d465952Smartinh int
namespace_update(struct namespace * ns,char * dn,struct ber_element * root)3725d465952Smartinh namespace_update(struct namespace *ns, char *dn, struct ber_element *root)
3735d465952Smartinh {
3745d465952Smartinh 	return namespace_put(ns, dn, root, 1);
3755d465952Smartinh }
3765d465952Smartinh 
3775d465952Smartinh int
namespace_del(struct namespace * ns,char * dn)3785d465952Smartinh namespace_del(struct namespace *ns, char *dn)
3795d465952Smartinh {
3805d465952Smartinh 	int			 rc;
3815d465952Smartinh 	struct ber_element	*root;
3825d465952Smartinh 	struct btval		 key, data;
3835d465952Smartinh 
3845d465952Smartinh 	assert(ns != NULL);
385a9ac9ba1Smartinh 	assert(ns->indx_txn != NULL);
386a9ac9ba1Smartinh 	assert(ns->data_txn != NULL);
3875d465952Smartinh 
38837f4b933Smmcc 	memset(&key, 0, sizeof(key));
38937f4b933Smmcc 	memset(&data, 0, sizeof(data));
3905d465952Smartinh 
3915d465952Smartinh 	key.data = dn;
3925d465952Smartinh 	key.size = strlen(key.data);
3935d465952Smartinh 
394b91780d1Smartinh 	rc = btree_txn_del(NULL, ns->data_txn, &key, &data);
395a9ac9ba1Smartinh 	if (rc == BT_SUCCESS && (root = namespace_db2ber(ns, &data)) != NULL)
396a9ac9ba1Smartinh 		rc = unindex_entry(ns, &key, root);
3975d465952Smartinh 
3985d465952Smartinh 	btval_reset(&data);
3995d465952Smartinh 	return rc;
4005d465952Smartinh }
4015d465952Smartinh 
40238c09006Smartinh int
namespace_has_referrals(struct namespace * ns)40338c09006Smartinh namespace_has_referrals(struct namespace *ns)
40438c09006Smartinh {
40538c09006Smartinh 	return !SLIST_EMPTY(&ns->referrals);
40638c09006Smartinh }
40738c09006Smartinh 
4085d465952Smartinh struct namespace *
namespace_lookup_base(const char * basedn,int include_referrals)40938c09006Smartinh namespace_lookup_base(const char *basedn, int include_referrals)
4105d465952Smartinh {
4115d465952Smartinh 	size_t			 blen, slen;
41238c09006Smartinh 	struct namespace	*ns, *matched_ns = NULL;
4135d465952Smartinh 
4145d465952Smartinh 	assert(basedn);
4155d465952Smartinh 	blen = strlen(basedn);
4165d465952Smartinh 
4175d465952Smartinh 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
4185d465952Smartinh 		slen = strlen(ns->suffix);
41938c09006Smartinh 		if ((include_referrals || !namespace_has_referrals(ns)) &&
42038c09006Smartinh 		    blen >= slen &&
42138c09006Smartinh 		    bcmp(basedn + blen - slen, ns->suffix, slen) == 0) {
42238c09006Smartinh 			/* Match the longest namespace suffix. */
42338c09006Smartinh 			if (matched_ns == NULL ||
42438c09006Smartinh 			    strlen(ns->suffix) > strlen(matched_ns->suffix))
42538c09006Smartinh 				matched_ns = ns;
4265d465952Smartinh 		}
42738c09006Smartinh 	}
42838c09006Smartinh 
42938c09006Smartinh 	return matched_ns;
43038c09006Smartinh }
43138c09006Smartinh 
43238c09006Smartinh struct namespace *
namespace_for_base(const char * basedn)43338c09006Smartinh namespace_for_base(const char *basedn)
43438c09006Smartinh {
43538c09006Smartinh 	return namespace_lookup_base(basedn, 0);
43638c09006Smartinh }
43738c09006Smartinh 
43838c09006Smartinh struct referrals *
namespace_referrals(const char * basedn)43938c09006Smartinh namespace_referrals(const char *basedn)
44038c09006Smartinh {
44138c09006Smartinh 	struct namespace	*ns;
44238c09006Smartinh 
44338c09006Smartinh 	if ((ns = namespace_lookup_base(basedn, 1)) != NULL &&
44438c09006Smartinh 	    namespace_has_referrals(ns))
44538c09006Smartinh 		return &ns->referrals;
44638c09006Smartinh 
44738c09006Smartinh 	if (!SLIST_EMPTY(&conf->referrals))
44838c09006Smartinh 		return &conf->referrals;
4495d465952Smartinh 
4505d465952Smartinh 	return NULL;
4515d465952Smartinh }
4525d465952Smartinh 
4535d465952Smartinh int
namespace_has_index(struct namespace * ns,const char * attr,enum index_type type)4545d465952Smartinh namespace_has_index(struct namespace *ns, const char *attr,
4555d465952Smartinh     enum index_type type)
4565d465952Smartinh {
4575d465952Smartinh 	struct attr_index	*ai;
4585d465952Smartinh 
4595d465952Smartinh 	assert(ns);
4605d465952Smartinh 	assert(attr);
4615d465952Smartinh 	TAILQ_FOREACH(ai, &ns->indices, next) {
4625d465952Smartinh 		if (strcasecmp(attr, ai->attr) == 0 && ai->type == type)
4635d465952Smartinh 			return 1;
4645d465952Smartinh 	}
4655d465952Smartinh 
4665d465952Smartinh 	return 0;
4675d465952Smartinh }
4685d465952Smartinh 
469a9ac9ba1Smartinh /* Queues modification requests while the namespace is being reopened.
4705d465952Smartinh  */
4715d465952Smartinh int
namespace_queue_request(struct namespace * ns,struct request * req)4725d465952Smartinh namespace_queue_request(struct namespace *ns, struct request *req)
4735d465952Smartinh {
4745d465952Smartinh 	if (ns->queued_requests > MAX_REQUEST_QUEUE) {
475c0785a05Sreyk 		log_warn("%u requests already queued, sorry",
476c0785a05Sreyk 		    ns->queued_requests);
4775d465952Smartinh 		return -1;
4785d465952Smartinh 	}
4795d465952Smartinh 
4805d465952Smartinh 	TAILQ_INSERT_TAIL(&ns->request_queue, req, next);
4815d465952Smartinh 	ns->queued_requests++;
482a8b81f96Smartinh 
483a8b81f96Smartinh 	if (!evtimer_pending(&ns->ev_queue, NULL))
484a8b81f96Smartinh 		namespace_queue_schedule(ns, 250000);
485a8b81f96Smartinh 
4865d465952Smartinh 	return 0;
4875d465952Smartinh }
4885d465952Smartinh 
4895d465952Smartinh static void
namespace_queue_replay(int fd,short event,void * data)4905d465952Smartinh namespace_queue_replay(int fd, short event, void *data)
4915d465952Smartinh {
4925d465952Smartinh 	struct namespace	*ns = data;
4935d465952Smartinh 	struct request		*req;
4945d465952Smartinh 
495a9ac9ba1Smartinh 	if (ns->data_db == NULL || ns->indx_db == NULL) {
496a9ac9ba1Smartinh 		log_debug("%s: database is being reopened", ns->suffix);
497a9ac9ba1Smartinh 		return;		/* Database is being reopened. */
498a9ac9ba1Smartinh 	}
499a9ac9ba1Smartinh 
5005d465952Smartinh 	if ((req = TAILQ_FIRST(&ns->request_queue)) == NULL)
5015d465952Smartinh 		return;
5025d465952Smartinh 	TAILQ_REMOVE(&ns->request_queue, req, next);
5035d465952Smartinh 
504a9ac9ba1Smartinh 	log_debug("replaying queued request");
5055d465952Smartinh 	req->replayed = 1;
5065d465952Smartinh 	request_dispatch(req);
5075d465952Smartinh 	ns->queued_requests--;
508a8b81f96Smartinh 
509a8b81f96Smartinh 	if (!evtimer_pending(&ns->ev_queue, NULL))
510a8b81f96Smartinh 		namespace_queue_schedule(ns, 0);
5115d465952Smartinh }
5125d465952Smartinh 
5135d465952Smartinh void
namespace_queue_schedule(struct namespace * ns,unsigned int usec)514a8b81f96Smartinh namespace_queue_schedule(struct namespace *ns, unsigned int usec)
5155d465952Smartinh {
5165d465952Smartinh 	struct timeval	 tv;
5175d465952Smartinh 
5185d465952Smartinh 	tv.tv_sec = 0;
519a8b81f96Smartinh 	tv.tv_usec = usec;
5205d465952Smartinh 	evtimer_add(&ns->ev_queue, &tv);
5215d465952Smartinh }
5225d465952Smartinh 
5235d465952Smartinh /* Cancel all queued requests from the given connection. Drops matching
5245d465952Smartinh  * requests from all namespaces without sending a response.
5255d465952Smartinh  */
5265d465952Smartinh void
namespace_cancel_conn(struct conn * conn)5275d465952Smartinh namespace_cancel_conn(struct conn *conn)
5285d465952Smartinh {
5295d465952Smartinh 	struct namespace	*ns;
5305d465952Smartinh 	struct request		*req, *next;
5315d465952Smartinh 
5325d465952Smartinh 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
533abcbcc4dSdoug 		for (req = TAILQ_FIRST(&ns->request_queue); req != NULL;
534abcbcc4dSdoug 		    req = next) {
5355d465952Smartinh 			next = TAILQ_NEXT(req, next);
5365d465952Smartinh 
5375d465952Smartinh 			if (req->conn == conn) {
5385d465952Smartinh 				TAILQ_REMOVE(&ns->request_queue, req, next);
5395d465952Smartinh 				request_free(req);
5405d465952Smartinh 			}
5415d465952Smartinh 		}
5425d465952Smartinh 	}
5435d465952Smartinh }
5445d465952Smartinh 
5450d43ad28Sjmatthew int
namespace_conn_queue_count(struct conn * conn)5460d43ad28Sjmatthew namespace_conn_queue_count(struct conn *conn)
5470d43ad28Sjmatthew {
5480d43ad28Sjmatthew 	struct namespace	*ns;
5490d43ad28Sjmatthew 	struct request		*req;
5500d43ad28Sjmatthew 	int			 count = 0;
5510d43ad28Sjmatthew 
5520d43ad28Sjmatthew 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
5530d43ad28Sjmatthew 		TAILQ_FOREACH(req, &ns->request_queue, next) {
5540d43ad28Sjmatthew 			if (req->conn == conn)
5550d43ad28Sjmatthew 				count++;
5560d43ad28Sjmatthew 		}
5570d43ad28Sjmatthew 	}
5580d43ad28Sjmatthew 
5590d43ad28Sjmatthew 	return count;
5600d43ad28Sjmatthew }
561