1 /* $NetBSD: dict.c,v 1.1.1.2 2010/06/17 18:07:11 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 */ 206 int dict_unknown_allowed = 1; 207 int dict_errno = 0; 208 209 static 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 */ 217 typedef 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 229 void 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 250 DICT *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 259 static 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 271 void 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 286 void 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 306 const 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 329 int 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 353 int 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 374 void 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) 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 404 void 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 426 static 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 442 const 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) { 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 475 void 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 489 const 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 517 int dict_changed(void) 518 { 519 return (dict_changed_name() != 0); 520 } 521 522 /* 523 * Mapping between flag names and flag values. 524 */ 525 static 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 }; 542 543 /* dict_flags_str - convert mask to string for debugging purposes */ 544 545 const char *dict_flags_str(int dict_flags) 546 { 547 static VSTRING *buf = 0; 548 549 if (buf == 0) 550 buf = vstring_alloc(1); 551 552 return (str_name_mask_opt(buf, "dictionary flags", dict_mask, dict_flags, 553 NAME_MASK_RETURN | NAME_MASK_PIPE)); 554 } 555