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