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