1 /* $OpenBSD: ldapctl.c,v 1.21 2024/11/21 13:38:14 claudio 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 int i, rc; 149 150 log_info("indexing namespace %s", ns->suffix); 151 152 if (asprintf(&path, "%s/%s_data.db", datadir, ns->suffix) == -1) 153 return -1; 154 data_db = btree_open(path, BT_NOSYNC | BT_REVERSEKEY, 0644); 155 free(path); 156 if (data_db == NULL) 157 return -1; 158 159 if (asprintf(&path, "%s/%s_indx.db", datadir, ns->suffix) == -1) 160 return -1; 161 indx_db = btree_open(path, BT_NOSYNC, 0644); 162 free(path); 163 if (indx_db == NULL) { 164 btree_close(data_db); 165 return -1; 166 } 167 168 if ((cursor = btree_cursor_open(data_db)) == NULL) { 169 btree_close(data_db); 170 btree_close(indx_db); 171 return -1; 172 } 173 174 bzero(&key, sizeof(key)); 175 bzero(&val, sizeof(val)); 176 177 for (;;) { 178 for (;;) { 179 ns->indx_txn = btree_txn_begin(indx_db, 0); 180 if (ns->indx_txn == NULL && errno == EBUSY) 181 usleep(100000); 182 else 183 break; 184 } 185 186 if (ns->indx_txn == NULL) { 187 log_warn("failed to start transaction"); 188 break; 189 } 190 191 for (i = 0; i < 100; i++) { 192 rc = btree_cursor_get(cursor, &key, &val, BT_NEXT); 193 if (rc != BT_SUCCESS) 194 break; 195 if ((elm = db2ber(&val, ns->compression_level)) == NULL) 196 continue; 197 rc = index_entry(ns, &key, elm); 198 ober_free_elements(elm); 199 btval_reset(&key); 200 btval_reset(&val); 201 if (rc != 0) 202 break; 203 } 204 205 if (btree_txn_commit(ns->indx_txn) != BT_SUCCESS) 206 break; 207 208 if (i != 100) 209 break; 210 } 211 212 btree_cursor_close(cursor); 213 btree_close(data_db); 214 btree_close(indx_db); 215 216 return 0; 217 } 218 219 int 220 index_namespaces(const char *datadir) 221 { 222 struct namespace *ns; 223 224 TAILQ_FOREACH(ns, &conf->namespaces, next) { 225 if (SLIST_EMPTY(&ns->referrals)) 226 continue; 227 if (index_namespace(ns, datadir) != 0) 228 return -1; 229 } 230 231 return 0; 232 } 233 234 int 235 ssl_load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags) 236 { 237 return 0; 238 } 239 240 int 241 main(int argc, char *argv[]) 242 { 243 int ctl_sock; 244 int done = 0, verbose = 0, vlog = 0; 245 ssize_t n; 246 int ch; 247 enum action action = NONE; 248 const char *datadir = DATADIR; 249 struct stat sb; 250 const char *sock = LDAPD_SOCKET; 251 char *conffile = CONFFILE; 252 struct sockaddr_un sun; 253 struct imsg imsg; 254 struct imsgbuf ibuf; 255 256 log_init(1, 0); 257 258 while ((ch = getopt(argc, argv, "f:r:s:v")) != -1) { 259 switch (ch) { 260 case 'f': 261 conffile = optarg; 262 break; 263 case 'r': 264 datadir = optarg; 265 break; 266 case 's': 267 sock = optarg; 268 break; 269 case 'v': 270 verbose = 1; 271 break; 272 default: 273 usage(); 274 /* NOTREACHED */ 275 } 276 } 277 argc -= optind; 278 argv += optind; 279 280 if (argc == 0) 281 usage(); 282 283 if (stat(datadir, &sb) == -1) 284 err(1, "%s", datadir); 285 if (!S_ISDIR(sb.st_mode)) 286 errx(1, "%s is not a directory", datadir); 287 288 ldap_loginit(NULL, 1, verbose); 289 290 if (strcmp(argv[0], "stats") == 0) 291 action = SHOW_STATS; 292 else if (strcmp(argv[0], "compact") == 0) 293 action = COMPACT_DB; 294 else if (strcmp(argv[0], "index") == 0) 295 action = INDEX_DB; 296 else if (strcmp(argv[0], "log") == 0) { 297 if (argc != 2) 298 usage(); 299 if (strcmp(argv[1], "verbose") == 0) 300 action = LOG_VERBOSE; 301 else if (strcmp(argv[1], "brief") == 0) 302 action = LOG_BRIEF; 303 else 304 usage(); 305 } else 306 usage(); 307 308 if (action == COMPACT_DB || action == INDEX_DB) { 309 if (parse_config(conffile) != 0) 310 exit(2); 311 312 if (pledge("stdio rpath wpath cpath flock", NULL) == -1) 313 err(1, "pledge"); 314 315 if (action == COMPACT_DB) 316 return compact_namespaces(datadir); 317 else 318 return index_namespaces(datadir); 319 } 320 321 /* connect to ldapd control socket */ 322 if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 323 err(1, "socket"); 324 325 bzero(&sun, sizeof(sun)); 326 sun.sun_family = AF_UNIX; 327 strlcpy(sun.sun_path, sock, sizeof(sun.sun_path)); 328 if (connect(ctl_sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 329 err(1, "connect: %s", sock); 330 331 if (imsgbuf_init(&ibuf, ctl_sock) == -1) 332 err(1, "imsgbuf_init"); 333 done = 0; 334 335 if (pledge("stdio", NULL) == -1) 336 err(1, "pledge"); 337 338 /* process user request */ 339 switch (action) { 340 case SHOW_STATS: 341 imsg_compose(&ibuf, IMSG_CTL_STATS, 0, 0, -1, NULL, 0); 342 break; 343 case LOG_VERBOSE: 344 vlog = 1; 345 /* FALLTHROUGH */ 346 case LOG_BRIEF: 347 imsg_compose(&ibuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 348 &vlog, sizeof(vlog)); 349 printf("logging request sent.\n"); 350 done = 1; 351 break; 352 case NONE: 353 break; 354 case COMPACT_DB: 355 case INDEX_DB: 356 fatal("internal error"); 357 } 358 359 if (imsgbuf_flush(&ibuf) == -1) 360 err(1, "write error"); 361 362 while (!done) { 363 if ((n = imsgbuf_read(&ibuf)) == -1) 364 err(1, "read error"); 365 if (n == 0) 366 errx(1, "pipe closed"); 367 368 while (!done) { 369 if ((n = imsg_get(&ibuf, &imsg)) == -1) 370 errx(1, "imsg_get error"); 371 if (n == 0) 372 break; 373 switch (imsg.hdr.type) { 374 case IMSG_CTL_STATS: 375 show_stats(&imsg); 376 break; 377 case IMSG_CTL_NSSTATS: 378 show_nsstats(&imsg); 379 break; 380 case IMSG_CTL_END: 381 done = 1; 382 break; 383 case NONE: 384 break; 385 } 386 imsg_free(&imsg); 387 } 388 } 389 close(ctl_sock); 390 391 return (0); 392 } 393 394 void 395 show_stats(struct imsg *imsg) 396 { 397 struct ldapd_stats *st; 398 399 st = imsg->data; 400 401 printf("start time: %s", ctime(&st->started_at)); 402 printf("requests: %llu\n", st->requests); 403 printf("search requests: %llu\n", st->req_search); 404 printf("bind requests: %llu\n", st->req_bind); 405 printf("modify requests: %llu\n", st->req_mod); 406 printf("timeouts: %llu\n", st->timeouts); 407 printf("unindexed searches: %llu\n", st->unindexed); 408 printf("active connections: %u\n", st->conns); 409 printf("active searches: %u\n", st->searches); 410 } 411 412 #define ZDIV(t,n) ((n) == 0 ? 0 : (float)(t) / (n)) 413 414 void 415 show_dbstats(const char *prefix, struct btree_stat *st) 416 { 417 printf("%s timestamp: %s", prefix, ctime(&st->created_at)); 418 printf("%s page size: %u\n", prefix, st->psize); 419 printf("%s depth: %u\n", prefix, st->depth); 420 printf("%s revisions: %u\n", prefix, st->revisions); 421 printf("%s entries: %llu\n", prefix, st->entries); 422 printf("%s branch/leaf/overflow pages: %u/%u/%u\n", 423 prefix, st->branch_pages, st->leaf_pages, st->overflow_pages); 424 425 printf("%s cache size: %u of %u (%.1f%% full)\n", prefix, 426 st->cache_size, st->max_cache, 427 100 * ZDIV(st->cache_size, st->max_cache)); 428 printf("%s page reads: %llu\n", prefix, st->reads); 429 printf("%s cache hits: %llu (%.1f%%)\n", prefix, st->hits, 430 100 * ZDIV(st->hits, (st->hits + st->reads))); 431 } 432 433 void 434 show_nsstats(struct imsg *imsg) 435 { 436 struct ns_stat *nss; 437 438 nss = imsg->data; 439 440 printf("\nsuffix: %s\n", nss->suffix); 441 show_dbstats("data", &nss->data_stat); 442 show_dbstats("indx", &nss->indx_stat); 443 } 444 445