xref: /openbsd/usr.sbin/ldapctl/ldapctl.c (revision d89ec533)
1 /*	$OpenBSD: ldapctl.c,v 1.15 2021/01/15 18:57:04 rob 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/stat.h>
26 #include <sys/queue.h>
27 #include <sys/un.h>
28 #include <sys/tree.h>
29 
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <net/if_media.h>
34 #include <net/if_types.h>
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <event.h>
43 
44 #include "ldapd.h"
45 #include "log.h"
46 
47 enum action {
48 	NONE,
49 	SHOW_STATS,
50 	LOG_VERBOSE,
51 	LOG_BRIEF,
52 	COMPACT_DB,
53 	INDEX_DB
54 };
55 
56 __dead void	 usage(void);
57 void		 show_stats(struct imsg *imsg);
58 void		 show_dbstats(const char *prefix, struct btree_stat *st);
59 void		 show_nsstats(struct imsg *imsg);
60 int		 compact_db(const char *path);
61 int		 compact_namespace(struct namespace *ns, const char *datadir);
62 int		 compact_namespaces(const char *datadir);
63 int		 index_namespace(struct namespace *ns, const char *datadir);
64 int		 index_namespaces(const char *datadir);
65 int		 ssl_load_certfile(struct ldapd_config *, const char *, u_int8_t);
66 
67 __dead void
68 usage(void)
69 {
70 	extern char *__progname;
71 
72 	fprintf(stderr,
73 	    "usage: %s [-v] [-f file] [-r directory] [-s socket] "
74 	    "command [argument ...]\n",
75 	    __progname);
76 	exit(1);
77 }
78 
79 int
80 compact_db(const char *path)
81 {
82 	struct btree	*bt;
83 	int		 rc;
84 
85 	log_info("compacting database %s", path);
86 	bt = btree_open(path, BT_NOSYNC | BT_REVERSEKEY, 0644);
87 	if (bt == NULL)
88 		return -1;
89 
90 	do {
91 		if ((rc = btree_compact(bt)) == -1 && errno == EBUSY)
92 			usleep(100000);
93 	} while (rc == -1 && errno == EBUSY);
94 
95 	btree_close(bt);
96 	return rc;
97 }
98 
99 int
100 compact_namespace(struct namespace *ns, const char *datadir)
101 {
102 	char		*path;
103 
104 	if (asprintf(&path, "%s/%s_data.db", datadir, ns->suffix) == -1)
105 		return -1;
106 	if (compact_db(path) != 0) {
107 		log_warn("%s", path);
108 		free(path);
109 		return -1;
110 	}
111 	free(path);
112 
113 	if (asprintf(&path, "%s/%s_indx.db", datadir, ns->suffix) == -1)
114 		return -1;
115 	if (compact_db(path) != 0) {
116 		log_warn("%s", path);
117 		free(path);
118 		return -1;
119 	}
120 	free(path);
121 
122 	return 0;
123 }
124 
125 int
126 compact_namespaces(const char *datadir)
127 {
128 	struct namespace	*ns;
129 
130 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
131 		if (SLIST_EMPTY(&ns->referrals))
132 		    continue;
133 		if (compact_namespace(ns, datadir) != 0)
134 			return -1;
135 	}
136 
137 	return 0;
138 }
139 
140 int
141 index_namespace(struct namespace *ns, const char *datadir)
142 {
143 	struct btval		 key, val;
144 	struct btree		*data_db, *indx_db;
145 	struct cursor		*cursor;
146 	struct ber_element	*elm;
147 	char			*path;
148 	long long int		 ncomplete = 0;
149 	int			 i, rc;
150 
151 	log_info("indexing namespace %s", ns->suffix);
152 
153 	if (asprintf(&path, "%s/%s_data.db", datadir, ns->suffix) == -1)
154 		return -1;
155 	data_db = btree_open(path, BT_NOSYNC | BT_REVERSEKEY, 0644);
156 	free(path);
157 	if (data_db == NULL)
158 		return -1;
159 
160 	if (asprintf(&path, "%s/%s_indx.db", datadir, ns->suffix) == -1)
161 		return -1;
162 	indx_db = btree_open(path, BT_NOSYNC, 0644);
163 	free(path);
164 	if (indx_db == NULL) {
165 		btree_close(data_db);
166 		return -1;
167 	}
168 
169 	if ((cursor = btree_cursor_open(data_db)) == NULL) {
170 		btree_close(data_db);
171 		btree_close(indx_db);
172 		return -1;
173 	}
174 
175 	bzero(&key, sizeof(key));
176 	bzero(&val, sizeof(val));
177 
178 	for (;;) {
179 		for (;;) {
180 			ns->indx_txn = btree_txn_begin(indx_db, 0);
181 			if (ns->indx_txn == NULL && errno == EBUSY)
182 				usleep(100000);
183 			else
184 				break;
185 		}
186 
187 		if (ns->indx_txn == NULL) {
188 			log_warn("failed to start transaction");
189 			break;
190 		}
191 
192 		for (i = 0; i < 100; i++) {
193 			rc = btree_cursor_get(cursor, &key, &val, BT_NEXT);
194 			if (rc != BT_SUCCESS)
195 				break;
196 			if ((elm = db2ber(&val, ns->compression_level)) == NULL)
197 				continue;
198 			rc = index_entry(ns, &key, elm);
199 			ober_free_elements(elm);
200 			btval_reset(&key);
201 			btval_reset(&val);
202 			if (rc != 0)
203 				break;
204 			++ncomplete;
205 		}
206 
207 		if (btree_txn_commit(ns->indx_txn) != BT_SUCCESS)
208 			break;
209 
210 		if (i != 100)
211 			break;
212 	}
213 
214 	btree_cursor_close(cursor);
215 	btree_close(data_db);
216 	btree_close(indx_db);
217 
218 	return 0;
219 }
220 
221 int
222 index_namespaces(const char *datadir)
223 {
224 	struct namespace	*ns;
225 
226 	TAILQ_FOREACH(ns, &conf->namespaces, next) {
227 		if (SLIST_EMPTY(&ns->referrals))
228 			continue;
229 		if (index_namespace(ns, datadir) != 0)
230 			return -1;
231 	}
232 
233 	return 0;
234 }
235 
236 int
237 ssl_load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags)
238 {
239 	return 0;
240 }
241 
242 int
243 main(int argc, char *argv[])
244 {
245 	int			 ctl_sock;
246 	int			 done = 0, verbose = 0, vlog = 0;
247 	ssize_t			 n;
248 	int			 ch;
249 	enum action		 action = NONE;
250 	const char		*datadir = DATADIR;
251 	struct stat		 sb;
252 	const char		*sock = LDAPD_SOCKET;
253 	char			*conffile = CONFFILE;
254 	struct sockaddr_un	 sun;
255 	struct imsg		 imsg;
256 	struct imsgbuf		 ibuf;
257 
258 	log_init(1, 0);
259 
260 	while ((ch = getopt(argc, argv, "f:r:s:v")) != -1) {
261 		switch (ch) {
262 		case 'f':
263 			conffile = optarg;
264 			break;
265 		case 'r':
266 			datadir = optarg;
267 			break;
268 		case 's':
269 			sock = optarg;
270 			break;
271 		case 'v':
272 			verbose = 1;
273 			break;
274 		default:
275 			usage();
276 			/* NOTREACHED */
277 		}
278 	}
279 	argc -= optind;
280 	argv += optind;
281 
282 	if (argc == 0)
283 		usage();
284 
285 	if (stat(datadir, &sb) == -1)
286 		err(1, "%s", datadir);
287 	if (!S_ISDIR(sb.st_mode))
288 		errx(1, "%s is not a directory", datadir);
289 
290 	ldap_loginit(NULL, 1, verbose);
291 
292 	if (strcmp(argv[0], "stats") == 0)
293 		action = SHOW_STATS;
294 	else if (strcmp(argv[0], "compact") == 0)
295 		action = COMPACT_DB;
296 	else if (strcmp(argv[0], "index") == 0)
297 		action = INDEX_DB;
298 	else if (strcmp(argv[0], "log") == 0) {
299 		if (argc != 2)
300 			usage();
301 		if (strcmp(argv[1], "verbose") == 0)
302 			action = LOG_VERBOSE;
303 		else if (strcmp(argv[1], "brief") == 0)
304 			action = LOG_BRIEF;
305 		else
306 			usage();
307 	} else
308 		usage();
309 
310 	if (action == COMPACT_DB || action == INDEX_DB) {
311 		if (parse_config(conffile) != 0)
312 			exit(2);
313 
314 		if (pledge("stdio rpath wpath cpath flock", NULL) == -1)
315 			err(1, "pledge");
316 
317 		if (action == COMPACT_DB)
318 			return compact_namespaces(datadir);
319 		else
320 			return index_namespaces(datadir);
321 	}
322 
323 	/* connect to ldapd control socket */
324 	if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
325 		err(1, "socket");
326 
327 	bzero(&sun, sizeof(sun));
328 	sun.sun_family = AF_UNIX;
329 	strlcpy(sun.sun_path, sock, sizeof(sun.sun_path));
330 	if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1)
331 		err(1, "connect: %s", sock);
332 
333 	imsg_init(&ibuf, ctl_sock);
334 	done = 0;
335 
336 	if (pledge("stdio", NULL) == -1)
337 		err(1, "pledge");
338 
339 	/* process user request */
340 	switch (action) {
341 	case SHOW_STATS:
342 		imsg_compose(&ibuf, IMSG_CTL_STATS, 0, 0, -1, NULL, 0);
343 		break;
344 	case LOG_VERBOSE:
345 		vlog = 1;
346 		/* FALLTHROUGH */
347 	case LOG_BRIEF:
348 		imsg_compose(&ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1,
349 		    &vlog, sizeof(vlog));
350 		printf("logging request sent.\n");
351 		done = 1;
352 		break;
353 	case NONE:
354 		break;
355 	case COMPACT_DB:
356 	case INDEX_DB:
357 		fatal("internal error");
358 	}
359 
360 	while (ibuf.w.queued)
361 		if (msgbuf_write(&ibuf.w) <= 0 && errno != EAGAIN)
362 			err(1, "write error");
363 
364 	while (!done) {
365 		if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN)
366 			errx(1, "imsg_read error");
367 		if (n == 0)
368 			errx(1, "pipe closed");
369 
370 		while (!done) {
371 			if ((n = imsg_get(&ibuf, &imsg)) == -1)
372 				errx(1, "imsg_get error");
373 			if (n == 0)
374 				break;
375 			switch (imsg.hdr.type) {
376 			case IMSG_CTL_STATS:
377 				show_stats(&imsg);
378 				break;
379 			case IMSG_CTL_NSSTATS:
380 				show_nsstats(&imsg);
381 				break;
382 			case IMSG_CTL_END:
383 				done = 1;
384 				break;
385 			case NONE:
386 				break;
387 			}
388 			imsg_free(&imsg);
389 		}
390 	}
391 	close(ctl_sock);
392 
393 	return (0);
394 }
395 
396 void
397 show_stats(struct imsg *imsg)
398 {
399 	struct ldapd_stats	*st;
400 
401 	st = imsg->data;
402 
403 	printf("start time: %s", ctime(&st->started_at));
404 	printf("requests: %llu\n", st->requests);
405 	printf("search requests: %llu\n", st->req_search);
406 	printf("bind requests: %llu\n", st->req_bind);
407 	printf("modify requests: %llu\n", st->req_mod);
408 	printf("timeouts: %llu\n", st->timeouts);
409 	printf("unindexed searches: %llu\n", st->unindexed);
410 	printf("active connections: %u\n", st->conns);
411 	printf("active searches: %u\n", st->searches);
412 }
413 
414 #define ZDIV(t,n)	((n) == 0 ? 0 : (float)(t) / (n))
415 
416 void
417 show_dbstats(const char *prefix, struct btree_stat *st)
418 {
419 	printf("%s timestamp: %s", prefix, ctime(&st->created_at));
420 	printf("%s page size: %u\n", prefix, st->psize);
421 	printf("%s depth: %u\n", prefix, st->depth);
422 	printf("%s revisions: %u\n", prefix, st->revisions);
423 	printf("%s entries: %llu\n", prefix, st->entries);
424 	printf("%s branch/leaf/overflow pages: %u/%u/%u\n",
425 	    prefix, st->branch_pages, st->leaf_pages, st->overflow_pages);
426 
427 	printf("%s cache size: %u of %u (%.1f%% full)\n", prefix,
428 	    st->cache_size, st->max_cache,
429 	    100 * ZDIV(st->cache_size, st->max_cache));
430 	printf("%s page reads: %llu\n", prefix, st->reads);
431 	printf("%s cache hits: %llu (%.1f%%)\n", prefix, st->hits,
432 	    100 * ZDIV(st->hits, (st->hits + st->reads)));
433 }
434 
435 void
436 show_nsstats(struct imsg *imsg)
437 {
438 	struct ns_stat		*nss;
439 
440 	nss = imsg->data;
441 
442 	printf("\nsuffix: %s\n", nss->suffix);
443 	show_dbstats("data", &nss->data_stat);
444 	show_dbstats("indx", &nss->indx_stat);
445 }
446 
447