xref: /openbsd/usr.sbin/ldapctl/ldapctl.c (revision a6445c1d)
1 /*	$OpenBSD: ldapctl.c,v 1.5 2013/11/14 20:48:52 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
5  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@vantronix.net>
6  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
7  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/queue.h>
26 #include <sys/un.h>
27 #include <sys/tree.h>
28 
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <net/if.h>
32 #include <net/if_media.h>
33 #include <net/if_types.h>
34 
35 #include <err.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <event.h>
42 
43 #include "ldapd.h"
44 
45 enum action {
46 	NONE,
47 	SHOW_STATS,
48 	LOG_VERBOSE,
49 	LOG_BRIEF,
50 	COMPACT_DB,
51 	INDEX_DB
52 };
53 
54 __dead void	 usage(void);
55 void		 show_stats(struct imsg *imsg);
56 void		 show_dbstats(const char *prefix, struct btree_stat *st);
57 void		 show_nsstats(struct imsg *imsg);
58 int		 compact_db(const char *path);
59 int		 compact_namespace(struct namespace *ns);
60 int		 compact_namespaces(void);
61 int		 index_namespace(struct namespace *ns);
62 int		 index_namespaces(void);
63 
64 __dead void
65 usage(void)
66 {
67 	extern char *__progname;
68 
69 	fprintf(stderr,
70 	    "usage: %s [-v] [-f file] [-s socket] command [argument ...]\n",
71 	    __progname);
72 	exit(1);
73 }
74 
75 int
76 compact_db(const char *path)
77 {
78 	struct btree	*bt;
79 	int		 rc;
80 
81 	log_info("compacting database %s", path);
82 	bt = btree_open(path, BT_NOSYNC | BT_REVERSEKEY, 0644);
83 	if (bt == NULL)
84 		return -1;
85 
86 	do {
87 		if ((rc = btree_compact(bt)) == -1 && errno == EBUSY)
88 			usleep(100000);
89 	} while (rc == -1 && errno == EBUSY);
90 
91 	btree_close(bt);
92 	return rc;
93 }
94 
95 int
96 compact_namespace(struct namespace *ns)
97 {
98 	char		*path;
99 
100 	if (asprintf(&path, "%s/%s_data.db", DATADIR, ns->suffix) < 0)
101 		return -1;
102 	if (compact_db(path) != 0) {
103 		log_warn("%s", path);
104 		free(path);
105 		return -1;
106 	}
107 	free(path);
108 
109 	if (asprintf(&path, "%s/%s_indx.db", DATADIR, ns->suffix) < 0)
110 		return -1;
111 	if (compact_db(path) != 0) {
112 		log_warn("%s", path);
113 		free(path);
114 		return -1;
115 	}
116 	free(path);
117 
118 	return 0;
119 }
120 
121 int
122 compact_namespaces(void)
123 {
124 	struct namespace	*ns;
125 
126 	TAILQ_FOREACH(ns, &conf->namespaces, next)
127 		if (SLIST_EMPTY(&ns->referrals) && compact_namespace(ns) != 0)
128 			return -1;
129 
130 	return 0;
131 }
132 
133 int
134 index_namespace(struct namespace *ns)
135 {
136 	struct btval		 key, val;
137 	struct btree		*data_db, *indx_db;
138 	struct cursor		*cursor;
139 	struct ber_element	*elm;
140 	char			*path;
141 	long long int		 ncomplete = 0;
142 	int			 i, rc;
143 
144 	log_info("indexing namespace %s", ns->suffix);
145 
146 	if (asprintf(&path, "%s/%s_data.db", DATADIR, ns->suffix) < 0)
147 		return -1;
148 	data_db = btree_open(path, BT_NOSYNC | BT_REVERSEKEY, 0644);
149 	free(path);
150 	if (data_db == NULL)
151 		return -1;
152 
153 	if (asprintf(&path, "%s/%s_indx.db", DATADIR, ns->suffix) < 0)
154 		return -1;
155 	indx_db = btree_open(path, BT_NOSYNC, 0644);
156 	free(path);
157 	if (indx_db == NULL) {
158 		btree_close(data_db);
159 		return -1;
160 	}
161 
162 	if ((cursor = btree_cursor_open(data_db)) == NULL) {
163 		btree_close(data_db);
164 		btree_close(indx_db);
165 		return -1;
166 	}
167 
168 	bzero(&key, sizeof(key));
169 	bzero(&val, sizeof(val));
170 
171 	for (;;) {
172 		for (;;) {
173 			ns->indx_txn = btree_txn_begin(indx_db, 0);
174 			if (ns->indx_txn == NULL && errno == EBUSY)
175 				usleep(100000);
176 			else
177 				break;
178 		}
179 
180 		if (ns->indx_txn == NULL) {
181 			log_warn("failed to start transaction");
182 			break;
183 		}
184 
185 		for (i = 0; i < 100; i++) {
186 			rc = btree_cursor_get(cursor, &key, &val, BT_NEXT);
187 			if (rc != BT_SUCCESS)
188 				break;
189 			if ((elm = db2ber(&val, ns->compression_level)) == NULL)
190 				continue;
191 			rc = index_entry(ns, &key, elm);
192 			ber_free_elements(elm);
193 			btval_reset(&key);
194 			btval_reset(&val);
195 			if (rc != 0)
196 				break;
197 			++ncomplete;
198 		}
199 
200 		if (btree_txn_commit(ns->indx_txn) != BT_SUCCESS)
201 			break;
202 
203 		if (i != 100)
204 			break;
205 	}
206 
207 	btree_cursor_close(cursor);
208 	btree_close(data_db);
209 	btree_close(indx_db);
210 
211 	return 0;
212 }
213 
214 int
215 index_namespaces(void)
216 {
217 	struct namespace	*ns;
218 
219 	TAILQ_FOREACH(ns, &conf->namespaces, next)
220 		if (SLIST_EMPTY(&ns->referrals) && index_namespace(ns) != 0)
221 			return -1;
222 
223 	return 0;
224 }
225 
226 int
227 ssl_load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags)
228 {
229 	return 0;
230 }
231 
232 int
233 main(int argc, char *argv[])
234 {
235 	int			 ctl_sock;
236 	int			 done = 0, verbose = 0;
237 	ssize_t			 n;
238 	int			 ch;
239 	enum action		 action = NONE;
240 	const char		*sock = LDAPD_SOCKET;
241 	char			*conffile = CONFFILE;
242 	struct sockaddr_un	 sun;
243 	struct imsg		 imsg;
244 	struct imsgbuf		 ibuf;
245 
246 	log_init(1);
247 
248 	while ((ch = getopt(argc, argv, "f:s:v")) != -1) {
249 		switch (ch) {
250 		case 'f':
251 			conffile = optarg;
252 			break;
253 		case 's':
254 			sock = optarg;
255 			break;
256 		case 'v':
257 			verbose = 1;
258 			break;
259 		default:
260 			usage();
261 			/* NOTREACHED */
262 		}
263 	}
264 	argc -= optind;
265 	argv += optind;
266 
267 	if (argc == 0)
268 		usage();
269 
270 	log_verbose(verbose);
271 
272 	if (strcmp(argv[0], "stats") == 0)
273 		action = SHOW_STATS;
274 	else if (strcmp(argv[0], "compact") == 0)
275 		action = COMPACT_DB;
276 	else if (strcmp(argv[0], "index") == 0)
277 		action = INDEX_DB;
278 	else if (strcmp(argv[0], "log") == 0) {
279 		if (argc != 2)
280 			usage();
281 		if (strcmp(argv[1], "verbose") == 0)
282 			action = LOG_VERBOSE;
283 		else if (strcmp(argv[1], "brief") == 0)
284 			action = LOG_BRIEF;
285 		else
286 			usage();
287 	} else
288 		usage();
289 
290 	if (action == COMPACT_DB || action == INDEX_DB) {
291 		if (parse_config(conffile) != 0)
292 			exit(2);
293 		if (action == COMPACT_DB)
294 			return compact_namespaces();
295 		else
296 			return index_namespaces();
297 	}
298 
299 	/* connect to ldapd control socket */
300 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
301 		err(1, "socket");
302 
303 	bzero(&sun, sizeof(sun));
304 	sun.sun_family = AF_UNIX;
305 	strlcpy(sun.sun_path, sock, sizeof(sun.sun_path));
306 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
307 		err(1, "connect: %s", sock);
308 
309 	imsg_init(&ibuf, ctl_sock);
310 	done = 0;
311 
312 	/* process user request */
313 	switch (action) {
314 	case SHOW_STATS:
315 		imsg_compose(&ibuf, IMSG_CTL_STATS, 0, 0, -1, NULL, 0);
316 		break;
317 	case LOG_VERBOSE:
318 		verbose = 1;
319 		/* FALLTHROUGH */
320 	case LOG_BRIEF:
321 		imsg_compose(&ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
322 		    &verbose, sizeof(verbose));
323 		printf("logging request sent.\n");
324 		done = 1;
325 		break;
326 	case NONE:
327 		break;
328 	case COMPACT_DB:
329 	case INDEX_DB:
330 		fatal("internal error");
331 	}
332 
333 	while (ibuf.w.queued)
334 		if (msgbuf_write(&ibuf.w) <= 0 && errno != EAGAIN)
335 			err(1, "write error");
336 
337 	while (!done) {
338 		if ((n = imsg_read(&ibuf)) == -1)
339 			errx(1, "imsg_read error");
340 		if (n == 0)
341 			errx(1, "pipe closed");
342 
343 		while (!done) {
344 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
345 				errx(1, "imsg_get error");
346 			if (n == 0)
347 				break;
348 			switch (imsg.hdr.type) {
349 			case IMSG_CTL_STATS:
350 				show_stats(&imsg);
351 				break;
352 			case IMSG_CTL_NSSTATS:
353 				show_nsstats(&imsg);
354 				break;
355 			case IMSG_CTL_END:
356 				done = 1;
357 				break;
358 			case NONE:
359 				break;
360 			}
361 			imsg_free(&imsg);
362 		}
363 	}
364 	close(ctl_sock);
365 
366 	return (0);
367 }
368 
369 void
370 show_stats(struct imsg *imsg)
371 {
372 	struct ldapd_stats	*st;
373 
374 	st = imsg->data;
375 
376 	printf("start time: %s", ctime(&st->started_at));
377 	printf("requests: %llu\n", st->requests);
378 	printf("search requests: %llu\n", st->req_search);
379 	printf("bind requests: %llu\n", st->req_bind);
380 	printf("modify requests: %llu\n", st->req_mod);
381 	printf("timeouts: %llu\n", st->timeouts);
382 	printf("unindexed searches: %llu\n", st->unindexed);
383 	printf("active connections: %u\n", st->conns);
384 	printf("active searches: %u\n", st->searches);
385 }
386 
387 #define ZDIV(t,n)	((n) == 0 ? 0 : (float)(t) / (n))
388 
389 void
390 show_dbstats(const char *prefix, struct btree_stat *st)
391 {
392 	printf("%s timestamp: %s", prefix, ctime(&st->created_at));
393 	printf("%s page size: %u\n", prefix, st->psize);
394 	printf("%s depth: %u\n", prefix, st->depth);
395 	printf("%s revisions: %u\n", prefix, st->revisions);
396 	printf("%s entries: %llu\n", prefix, st->entries);
397 	printf("%s branch/leaf/overflow pages: %u/%u/%u\n",
398 	    prefix, st->branch_pages, st->leaf_pages, st->overflow_pages);
399 
400 	printf("%s cache size: %u of %u (%.1f%% full)\n", prefix,
401 	    st->cache_size, st->max_cache,
402 	    100 * ZDIV(st->cache_size, st->max_cache));
403 	printf("%s page reads: %llu\n", prefix, st->reads);
404 	printf("%s cache hits: %llu (%.1f%%)\n", prefix, st->hits,
405 	    100 * ZDIV(st->hits, (st->hits + st->reads)));
406 }
407 
408 void
409 show_nsstats(struct imsg *imsg)
410 {
411 	struct ns_stat		*nss;
412 
413 	nss = imsg->data;
414 
415 	printf("\nsuffix: %s\n", nss->suffix);
416 	show_dbstats("data", &nss->data_stat);
417 	show_dbstats("indx", &nss->indx_stat);
418 }
419 
420