1 /* $NetBSD: dict.c,v 1.1.1.7 2014/07/06 19:27:57 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict 3 6 /* SUMMARY 7 /* dictionary manager 8 /* SYNOPSIS 9 /* #include <dict.h> 10 /* 11 /* void dict_register(dict_name, dict_info) 12 /* const char *dict_name; 13 /* DICT *dict_info; 14 /* 15 /* DICT *dict_handle(dict_name) 16 /* const char *dict_name; 17 /* 18 /* void dict_unregister(dict_name) 19 /* const char *dict_name; 20 /* 21 /* int dict_update(dict_name, member, value) 22 /* const char *dict_name; 23 /* const char *member; 24 /* const char *value; 25 /* 26 /* const char *dict_lookup(dict_name, member) 27 /* const char *dict_name; 28 /* const char *member; 29 /* 30 /* int dict_delete(dict_name, member) 31 /* const char *dict_name; 32 /* const char *member; 33 /* 34 /* int dict_sequence(dict_name, func, member, value) 35 /* const char *dict_name; 36 /* int func; 37 /* const char **member; 38 /* const char **value; 39 /* 40 /* const char *dict_eval(dict_name, string, int recursive) 41 /* const char *dict_name; 42 /* const char *string; 43 /* int recursive; 44 /* 45 /* int dict_walk(action, context) 46 /* void (*action)(dict_name, dict_handle, context) 47 /* char *context; 48 /* 49 /* int dict_error(dict_name) 50 /* const char *dict_name; 51 /* 52 /* const char *dict_changed_name() 53 /* AUXILIARY FUNCTIONS 54 /* int dict_load_file_xt(dict_name, path) 55 /* const char *dict_name; 56 /* const char *path; 57 /* 58 /* void dict_load_fp(dict_name, fp) 59 /* const char *dict_name; 60 /* VSTREAM *fp; 61 /* 62 /* const char *dict_flags_str(dict_flags) 63 /* int dict_flags; 64 /* 65 /* int dict_flags_mask(names) 66 /* const char *names; 67 /* DESCRIPTION 68 /* This module maintains a collection of name-value dictionaries. 69 /* Each dictionary has its own name and has its own methods to read 70 /* or update members. Examples of dictionaries that can be accessed 71 /* in this manner are the global UNIX-style process environment, 72 /* hash tables, NIS maps, DBM files, and so on. Dictionary values 73 /* are not limited to strings but can be arbitrary objects as long 74 /* as they can be represented by character pointers. 75 /* FEATURES 76 /* .fi 77 /* .ad 78 /* Notable features of this module are: 79 /* .IP "macro expansion (string-valued dictionaries only)" 80 /* Macros of the form $\fIname\fR can be expanded to the current 81 /* value of \fIname\fR. The forms $(\fIname\fR) and ${\fIname\fR} are 82 /* also supported. 83 /* .IP "unknown names" 84 /* An update request for an unknown dictionary name will trigger 85 /* the instantiation of an in-memory dictionary with that name. 86 /* A lookup request (including delete and sequence) for an 87 /* unknown dictionary will result in a "not found" and "no 88 /* error" result. 89 /* .PP 90 /* dict_register() adds a new dictionary, including access methods, 91 /* to the list of known dictionaries, or increments the reference 92 /* count for an existing (name, dictionary) pair. Otherwise, it is 93 /* an error to pass an existing name (this would cause a memory leak). 94 /* 95 /* dict_handle() returns the generic dictionary handle of the 96 /* named dictionary, or a null pointer when the named dictionary 97 /* is not found. 98 /* 99 /* dict_unregister() decrements the reference count of the named 100 /* dictionary. When the reference count reaches zero, dict_unregister() 101 /* breaks the (name, dictionary) association and executes the 102 /* dictionary's optional \fIremove\fR method. 103 /* 104 /* dict_update() updates the value of the named dictionary member. 105 /* The dictionary member and the named dictionary are instantiated 106 /* on the fly. The result value is zero (DICT_STAT_SUCCESS) 107 /* when the update was made. 108 /* 109 /* dict_lookup() returns the value of the named member (i.e. without 110 /* expanding macros in the member value). The \fIdict_name\fR argument 111 /* specifies the dictionary to search. The result is a null pointer 112 /* when no value is found, otherwise the result is owned by the 113 /* underlying dictionary method. Make a copy if the result is to be 114 /* modified, or if the result is to survive multiple dict_lookup() calls. 115 /* 116 /* dict_delete() removes the named member from the named dictionary. 117 /* The result value is zero (DICT_STAT_SUCCESS) when the member 118 /* was found. 119 /* 120 /* dict_sequence() steps through the named dictionary and returns 121 /* keys and values in some implementation-defined order. The func 122 /* argument is DICT_SEQ_FUN_FIRST to set the cursor to the first 123 /* entry or DICT_SEQ_FUN_NEXT to select the next entry. The result 124 /* is owned by the underlying dictionary method. Make a copy if the 125 /* result is to be modified, or if the result is to survive multiple 126 /* dict_sequence() calls. The result value is zero (DICT_STAT_SUCCESS) 127 /* when a member was found. 128 /* 129 /* dict_eval() expands macro references in the specified string. 130 /* The result is owned by the dictionary manager. Make a copy if the 131 /* result is to survive multiple dict_eval() calls. When the 132 /* \fIrecursive\fR argument is non-zero, macro references in macro 133 /* lookup results are expanded recursively. 134 /* 135 /* dict_walk() iterates over all registered dictionaries in some 136 /* arbitrary order, and invokes the specified action routine with 137 /* as arguments: 138 /* .IP "const char *dict_name" 139 /* Dictionary name. 140 /* .IP "DICT *dict_handle" 141 /* Generic dictionary handle. 142 /* .IP "char *context" 143 /* Application context from the caller. 144 /* .PP 145 /* dict_changed_name() returns non-zero when any dictionary needs to 146 /* be re-opened because it has changed or because it was unlinked. 147 /* A non-zero result is the name of a changed dictionary. 148 /* 149 /* dict_load_file_xt() reads name-value entries from the named file. 150 /* Lines that begin with whitespace are concatenated to the preceding 151 /* line (the newline is deleted). 152 /* Each entry is stored in the dictionary named by \fIdict_name\fR. 153 /* The result is zero if the file could not be opened. 154 /* 155 /* dict_load_fp() reads name-value entries from an open stream. 156 /* It has the same semantics as the dict_load_file_xt() function. 157 /* 158 /* dict_flags_str() returns a printable representation of the 159 /* specified dictionary flags. The result is overwritten upon 160 /* each call. 161 /* 162 /* dict_flags_mask() returns the bitmask for the specified 163 /* comma/space-separated dictionary flag names. 164 /* SEE ALSO 165 /* htable(3) 166 /* BUGS 167 /* DIAGNOSTICS 168 /* Fatal errors: out of memory, malformed macro name. 169 /* 170 /* The lookup routine returns non-null when the request is 171 /* satisfied. The update, delete and sequence routines return 172 /* zero (DICT_STAT_SUCCESS) when the request is satisfied. 173 /* The dict_error() function returns non-zero only when the 174 /* last operation was not satisfied due to a dictionary access 175 /* error. The result can have the following values: 176 /* .IP DICT_ERR_NONE(zero) 177 /* There was no dictionary access error. For example, the 178 /* request was satisfied, the requested information did not 179 /* exist in the dictionary, or the information already existed 180 /* when it should not exist (collision). 181 /* .IP DICT_ERR_RETRY(<0) 182 /* The dictionary was temporarily unavailable. This can happen 183 /* with network-based services. 184 /* .IP DICT_ERR_CONFIG(<0) 185 /* The dictionary was unavailable due to a configuration error. 186 /* .PP 187 /* Generally, a program is expected to test the function result 188 /* value for "success" first. If the operation was not successful, 189 /* a program is expected to test for a non-zero dict->error 190 /* status to distinguish between a data notfound/collision 191 /* condition or a dictionary access error. 192 /* LICENSE 193 /* .ad 194 /* .fi 195 /* The Secure Mailer license must be distributed with this software. 196 /* AUTHOR(S) 197 /* Wietse Venema 198 /* IBM T.J. Watson Research 199 /* P.O. Box 704 200 /* Yorktown Heights, NY 10598, USA 201 /*--*/ 202 203 /* System libraries. */ 204 205 #include "sys_defs.h" 206 #include <sys/stat.h> 207 #include <fcntl.h> 208 #include <ctype.h> 209 #include <string.h> 210 #include <time.h> 211 212 /* Utility library. */ 213 214 #include "msg.h" 215 #include "htable.h" 216 #include "mymalloc.h" 217 #include "vstream.h" 218 #include "vstring.h" 219 #include "readlline.h" 220 #include "mac_expand.h" 221 #include "stringops.h" 222 #include "iostuff.h" 223 #include "name_mask.h" 224 #include "dict.h" 225 #include "dict_ht.h" 226 #include "warn_stat.h" 227 #include "line_number.h" 228 229 static HTABLE *dict_table; 230 231 /* 232 * Each (name, dictionary) instance has a reference count. The count is part 233 * of the name, not the dictionary. The same dictionary may be registered 234 * under multiple names. The structure below keeps track of instances and 235 * reference counts. 236 */ 237 typedef struct { 238 DICT *dict; 239 int refcount; 240 } DICT_NODE; 241 242 #define dict_node(dict) \ 243 (dict_table ? (DICT_NODE *) htable_find(dict_table, dict) : 0) 244 245 /* Find a dictionary handle by name for lookup purposes. */ 246 247 #define DICT_FIND_FOR_LOOKUP(dict, dict_name) do { \ 248 DICT_NODE *node; \ 249 if ((node = dict_node(dict_name)) != 0) \ 250 dict = node->dict; \ 251 else \ 252 dict = 0; \ 253 } while (0) 254 255 /* Find a dictionary handle by name for update purposes. */ 256 257 #define DICT_FIND_FOR_UPDATE(dict, dict_name) do { \ 258 DICT_NODE *node; \ 259 if ((node = dict_node(dict_name)) == 0) { \ 260 dict = dict_ht_open(dict_name, O_CREAT | O_RDWR, 0); \ 261 dict_register(dict_name, dict); \ 262 } else \ 263 dict = node->dict; \ 264 } while (0) 265 266 #define STR(x) vstring_str(x) 267 268 /* dict_register - make association with dictionary */ 269 270 void dict_register(const char *dict_name, DICT *dict_info) 271 { 272 const char *myname = "dict_register"; 273 DICT_NODE *node; 274 275 if (dict_table == 0) 276 dict_table = htable_create(0); 277 if ((node = dict_node(dict_name)) == 0) { 278 node = (DICT_NODE *) mymalloc(sizeof(*node)); 279 node->dict = dict_info; 280 node->refcount = 0; 281 htable_enter(dict_table, dict_name, (char *) node); 282 } else if (dict_info != node->dict) 283 msg_fatal("%s: dictionary name exists: %s", myname, dict_name); 284 node->refcount++; 285 if (msg_verbose > 1) 286 msg_info("%s: %s %d", myname, dict_name, node->refcount); 287 } 288 289 /* dict_handle - locate generic dictionary handle */ 290 291 DICT *dict_handle(const char *dict_name) 292 { 293 DICT_NODE *node; 294 295 return ((node = dict_node(dict_name)) != 0 ? node->dict : 0); 296 } 297 298 /* dict_node_free - dict_unregister() callback */ 299 300 static void dict_node_free(char *ptr) 301 { 302 DICT_NODE *node = (DICT_NODE *) ptr; 303 DICT *dict = node->dict; 304 305 if (dict->close) 306 dict->close(dict); 307 myfree((char *) node); 308 } 309 310 /* dict_unregister - break association with named dictionary */ 311 312 void dict_unregister(const char *dict_name) 313 { 314 const char *myname = "dict_unregister"; 315 DICT_NODE *node; 316 317 if ((node = dict_node(dict_name)) == 0) 318 msg_panic("non-existing dictionary: %s", dict_name); 319 if (msg_verbose > 1) 320 msg_info("%s: %s %d", myname, dict_name, node->refcount); 321 if (--(node->refcount) == 0) 322 htable_delete(dict_table, dict_name, dict_node_free); 323 } 324 325 /* dict_update - replace or add dictionary entry */ 326 327 int dict_update(const char *dict_name, const char *member, const char *value) 328 { 329 const char *myname = "dict_update"; 330 DICT *dict; 331 332 DICT_FIND_FOR_UPDATE(dict, dict_name); 333 if (msg_verbose > 1) 334 msg_info("%s: %s = %s", myname, member, value); 335 return (dict->update(dict, member, value)); 336 } 337 338 /* dict_lookup - look up dictionary entry */ 339 340 const char *dict_lookup(const char *dict_name, const char *member) 341 { 342 const char *myname = "dict_lookup"; 343 DICT *dict; 344 const char *ret; 345 346 DICT_FIND_FOR_LOOKUP(dict, dict_name); 347 if (dict != 0) { 348 ret = dict->lookup(dict, member); 349 if (msg_verbose > 1) 350 msg_info("%s: %s = %s", myname, member, ret ? ret : 351 dict->error ? "(error)" : "(notfound)"); 352 return (ret); 353 } else { 354 if (msg_verbose > 1) 355 msg_info("%s: %s = %s", myname, member, "(notfound)"); 356 return (0); 357 } 358 } 359 360 /* dict_delete - delete dictionary entry */ 361 362 int dict_delete(const char *dict_name, const char *member) 363 { 364 const char *myname = "dict_delete"; 365 DICT *dict; 366 367 DICT_FIND_FOR_LOOKUP(dict, dict_name); 368 if (msg_verbose > 1) 369 msg_info("%s: delete %s", myname, member); 370 return (dict ? dict->delete(dict, member) : DICT_STAT_FAIL); 371 } 372 373 /* dict_sequence - traverse dictionary */ 374 375 int dict_sequence(const char *dict_name, const int func, 376 const char **member, const char **value) 377 { 378 const char *myname = "dict_sequence"; 379 DICT *dict; 380 381 DICT_FIND_FOR_LOOKUP(dict, dict_name); 382 if (msg_verbose > 1) 383 msg_info("%s: sequence func %d", myname, func); 384 return (dict ? dict->sequence(dict, func, member, value) : DICT_STAT_FAIL); 385 } 386 387 /* dict_error - return last error */ 388 389 int dict_error(const char *dict_name) 390 { 391 DICT *dict; 392 393 DICT_FIND_FOR_LOOKUP(dict, dict_name); 394 return (dict ? dict->error : DICT_ERR_NONE); 395 } 396 397 /* dict_load_file_xt - read entries from text file */ 398 399 int dict_load_file_xt(const char *dict_name, const char *path) 400 { 401 VSTREAM *fp; 402 struct stat st; 403 time_t before; 404 time_t after; 405 406 /* 407 * Read the file again if it is hot. This may result in reading a partial 408 * parameter name when a file changes in the middle of a read. 409 */ 410 for (before = time((time_t *) 0); /* see below */ ; before = after) { 411 if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) 412 return (0); 413 dict_load_fp(dict_name, fp); 414 if (fstat(vstream_fileno(fp), &st) < 0) 415 msg_fatal("fstat %s: %m", path); 416 if (vstream_ferror(fp) || vstream_fclose(fp)) 417 msg_fatal("read %s: %m", path); 418 after = time((time_t *) 0); 419 if (st.st_mtime < before - 1 || st.st_mtime > after) 420 break; 421 if (msg_verbose > 1) 422 msg_info("pausing to let %s cool down", path); 423 doze(300000); 424 } 425 return (1); 426 } 427 428 /* dict_load_fp - read entries from open stream */ 429 430 void dict_load_fp(const char *dict_name, VSTREAM *fp) 431 { 432 const char *myname = "dict_load_fp"; 433 VSTRING *buf; 434 char *member; 435 char *val; 436 const char *old; 437 int old_lineno; 438 int lineno; 439 const char *err; 440 struct stat st; 441 DICT *dict; 442 443 /* 444 * Instantiate the dictionary even if the file is empty. 445 */ 446 DICT_FIND_FOR_UPDATE(dict, dict_name); 447 buf = vstring_alloc(100); 448 old_lineno = lineno = 0; 449 450 if (fstat(vstream_fileno(fp), &st) < 0) 451 msg_fatal("fstat %s: %m", VSTREAM_PATH(fp)); 452 for ( /* void */ ; readlline(buf, fp, &lineno); old_lineno = lineno) { 453 if ((err = split_nameval(STR(buf), &member, &val)) != 0) 454 msg_fatal("%s, line %s: %s: \"%s\"", 455 VSTREAM_PATH(fp), 456 format_line_number((VSTRING *) 0, 457 old_lineno + 1, lineno), 458 err, STR(buf)); 459 if (msg_verbose > 1) 460 msg_info("%s: %s = %s", myname, member, val); 461 if ((old = dict->lookup(dict, member)) != 0 462 && strcmp(old, val) != 0) 463 msg_warn("%s, line %d: overriding earlier entry: %s=%s", 464 VSTREAM_PATH(fp), lineno, member, old); 465 if (dict->update(dict, member, val) != 0) 466 msg_fatal("%s, line %d: unable to update %s:%s", 467 VSTREAM_PATH(fp), lineno, dict->type, dict->name); 468 } 469 vstring_free(buf); 470 dict->owner.uid = st.st_uid; 471 dict->owner.status = (st.st_uid != 0); 472 } 473 474 /* dict_eval_lookup - macro parser call-back routine */ 475 476 static const char *dict_eval_lookup(const char *key, int unused_type, 477 char *dict_name) 478 { 479 const char *pp = 0; 480 DICT *dict; 481 482 /* 483 * XXX how would one recover? 484 */ 485 DICT_FIND_FOR_LOOKUP(dict, dict_name); 486 if (dict != 0 487 && (pp = dict->lookup(dict, key)) == 0 && dict->error != 0) 488 msg_fatal("dictionary %s: lookup %s: operation failed", dict_name, key); 489 return (pp); 490 } 491 492 /* dict_eval - expand embedded dictionary references */ 493 494 const char *dict_eval(const char *dict_name, const char *value, int recursive) 495 { 496 const char *myname = "dict_eval"; 497 static VSTRING *buf; 498 int status; 499 500 /* 501 * Initialize. 502 */ 503 if (buf == 0) 504 buf = vstring_alloc(10); 505 506 /* 507 * Expand macros, possibly recursively. 508 */ 509 #define DONT_FILTER (char *) 0 510 511 status = mac_expand(buf, value, 512 recursive ? MAC_EXP_FLAG_RECURSE : MAC_EXP_FLAG_NONE, 513 DONT_FILTER, dict_eval_lookup, (char *) dict_name); 514 if (status & MAC_PARSE_ERROR) 515 msg_fatal("dictionary %s: macro processing error", dict_name); 516 if (msg_verbose > 1) { 517 if (strcmp(value, STR(buf)) != 0) 518 msg_info("%s: expand %s -> %s", myname, value, STR(buf)); 519 else 520 msg_info("%s: const %s", myname, value); 521 } 522 return (STR(buf)); 523 } 524 525 /* dict_walk - iterate over all dictionaries in arbitrary order */ 526 527 void dict_walk(DICT_WALK_ACTION action, char *ptr) 528 { 529 HTABLE_INFO **ht_info_list; 530 HTABLE_INFO **ht; 531 HTABLE_INFO *h; 532 533 ht_info_list = htable_list(dict_table); 534 for (ht = ht_info_list; (h = *ht) != 0; ht++) 535 action(h->key, (DICT *) h->value, ptr); 536 myfree((char *) ht_info_list); 537 } 538 539 /* dict_changed_name - see if any dictionary has changed */ 540 541 const char *dict_changed_name(void) 542 { 543 const char *myname = "dict_changed_name"; 544 struct stat st; 545 HTABLE_INFO **ht_info_list; 546 HTABLE_INFO **ht; 547 HTABLE_INFO *h; 548 const char *status; 549 DICT *dict; 550 551 ht_info_list = htable_list(dict_table); 552 for (status = 0, ht = ht_info_list; status == 0 && (h = *ht) != 0; ht++) { 553 dict = ((DICT_NODE *) h->value)->dict; 554 if (dict->stat_fd < 0) /* not file-based */ 555 continue; 556 if (dict->mtime == 0) /* not bloody likely */ 557 msg_warn("%s: table %s: null time stamp", myname, h->key); 558 if (fstat(dict->stat_fd, &st) < 0) 559 msg_fatal("%s: fstat: %m", myname); 560 if (((dict->flags & DICT_FLAG_MULTI_WRITER) == 0 561 && st.st_mtime != dict->mtime) 562 || st.st_nlink == 0) 563 status = h->key; 564 } 565 myfree((char *) ht_info_list); 566 return (status); 567 } 568 569 /* dict_changed - backwards compatibility */ 570 571 int dict_changed(void) 572 { 573 return (dict_changed_name() != 0); 574 } 575 576 /* 577 * Mapping between flag names and flag values. 578 */ 579 static const NAME_MASK dict_mask[] = { 580 "warn_dup", DICT_FLAG_DUP_WARN, /* if file, warn about dups */ 581 "ignore_dup", DICT_FLAG_DUP_IGNORE, /* if file, ignore dups */ 582 "try0null", DICT_FLAG_TRY0NULL, /* do not append 0 to key/value */ 583 "try1null", DICT_FLAG_TRY1NULL, /* append 0 to key/value */ 584 "fixed", DICT_FLAG_FIXED, /* fixed key map */ 585 "pattern", DICT_FLAG_PATTERN, /* keys are patterns */ 586 "lock", DICT_FLAG_LOCK, /* lock before access */ 587 "replace", DICT_FLAG_DUP_REPLACE, /* if file, replace dups */ 588 "sync_update", DICT_FLAG_SYNC_UPDATE, /* if file, sync updates */ 589 "debug", DICT_FLAG_DEBUG, /* log access */ 590 "no_regsub", DICT_FLAG_NO_REGSUB, /* disallow regexp substitution */ 591 "no_proxy", DICT_FLAG_NO_PROXY, /* disallow proxy mapping */ 592 "no_unauth", DICT_FLAG_NO_UNAUTH, /* disallow unauthenticated data */ 593 "fold_fix", DICT_FLAG_FOLD_FIX, /* case-fold with fixed-case key map */ 594 "fold_mul", DICT_FLAG_FOLD_MUL, /* case-fold with multi-case key map */ 595 "open_lock", DICT_FLAG_OPEN_LOCK, /* permanent lock upon open */ 596 "bulk_update", DICT_FLAG_BULK_UPDATE, /* bulk update if supported */ 597 "multi_writer", DICT_FLAG_MULTI_WRITER, /* multi-writer safe */ 598 0, 599 }; 600 601 /* dict_flags_str - convert bitmask to symbolic flag names */ 602 603 const char *dict_flags_str(int dict_flags) 604 { 605 static VSTRING *buf = 0; 606 607 if (buf == 0) 608 buf = vstring_alloc(1); 609 610 return (str_name_mask_opt(buf, "dictionary flags", dict_mask, dict_flags, 611 NAME_MASK_NUMBER | NAME_MASK_PIPE)); 612 } 613 614 /* dict_flags_mask - convert symbolic flag names to bitmask */ 615 616 int dict_flags_mask(const char *names) 617 { 618 return (name_mask("dictionary flags", dict_mask, names)); 619 } 620