xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict.c (revision 501cd18a74d52bfcca7d9e7e3b0d472bbc870558)
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