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