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