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