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