xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/dict_memcache.c (revision 154bfe8e089c1a0a4e9ed8414f08d3da90949162)
1 /*	$NetBSD: dict_memcache.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	dict_memcache 3
6 /* SUMMARY
7 /*	dictionary interface to memcaches
8 /* SYNOPSIS
9 /*	#include <dict_memcache.h>
10 /*
11 /*	DICT	*dict_memcache_open(name, open_flags, dict_flags)
12 /*	const char *name;
13 /*	int	open_flags;
14 /*	int	dict_flags;
15 /* DESCRIPTION
16 /*	dict_memcache_open() opens a memcache, providing
17 /*	a dictionary interface for Postfix key->value mappings.
18 /*	The result is a pointer to the installed dictionary.
19 /*
20 /*	Configuration parameters are described in memcache_table(5).
21 /*
22 /*	Arguments:
23 /* .IP name
24 /*	The path to the Postfix memcache configuration file.
25 /* .IP open_flags
26 /*	O_RDONLY or O_RDWR. This function ignores flags that don't
27 /*	specify a read, write or append mode.
28 /* .IP dict_flags
29 /*	See dict_open(3).
30 /* SEE ALSO
31 /*	dict(3) generic dictionary manager
32 /* HISTORY
33 /* .ad
34 /* .fi
35 /*	The first memcache client for Postfix was written by Omar
36 /*	Kilani, and was based on libmemcache.  The current
37 /*	implementation implements the memcache protocol directly,
38 /*	and bears no resemblance to earlier work.
39 /* AUTHOR(S)
40 /*	Wietse Venema
41 /*	IBM T.J. Watson Research
42 /*	P.O. Box 704
43 /*	Yorktown Heights, NY 10598, USA
44 /*--*/
45 
46 /* System library. */
47 
48 #include <sys_defs.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <stdio.h>			/* XXX sscanf() */
53 
54 /* Utility library. */
55 
56 #include <msg.h>
57 #include <mymalloc.h>
58 #include <dict.h>
59 #include <vstring.h>
60 #include <stringops.h>
61 #include <auto_clnt.h>
62 #include <vstream.h>
63 
64 /* Global library. */
65 
66 #include <cfg_parser.h>
67 #include <db_common.h>
68 #include <memcache_proto.h>
69 
70 /* Application-specific. */
71 
72 #include <dict_memcache.h>
73 
74  /*
75   * Structure of one memcache dictionary handle.
76   */
77 typedef struct {
78     DICT    dict;			/* parent class */
79     CFG_PARSER *parser;			/* common parameter parser */
80     void   *dbc_ctxt;			/* db_common context */
81     char   *key_format;			/* query key translation */
82     int     timeout;			/* client timeout */
83     int     mc_ttl;			/* memcache update expiration */
84     int     mc_flags;			/* memcache update flags */
85     int     err_pause;			/* delay between errors */
86     int     max_tries;			/* number of tries */
87     int     max_line;			/* reply line limit */
88     int     max_data;			/* reply data limit */
89     char   *memcache;			/* memcache server spec */
90     AUTO_CLNT *clnt;			/* memcache client stream */
91     VSTRING *clnt_buf;			/* memcache client buffer */
92     VSTRING *key_buf;			/* lookup key */
93     VSTRING *res_buf;			/* lookup result */
94     int     error;			/* memcache dict_errno */
95     DICT   *backup;			/* persistent backup */
96 } DICT_MC;
97 
98  /*
99   * Memcache option defaults and names.
100   */
101 #define DICT_MC_DEF_HOST	"localhost"
102 #define DICT_MC_DEF_PORT	"11211"
103 #define DICT_MC_DEF_MEMCACHE	"inet:" DICT_MC_DEF_HOST ":" DICT_MC_DEF_PORT
104 #define DICT_MC_DEF_KEY_FMT	"%s"
105 #define DICT_MC_DEF_MC_TTL	3600
106 #define DICT_MC_DEF_MC_TIMEOUT	2
107 #define DICT_MC_DEF_MC_FLAGS	0
108 #define DICT_MC_DEF_MAX_TRY	2
109 #define DICT_MC_DEF_MAX_LINE	1024
110 #define DICT_MC_DEF_MAX_DATA	10240
111 #define DICT_MC_DEF_ERR_PAUSE	1
112 
113 #define DICT_MC_NAME_MEMCACHE	"memcache"
114 #define DICT_MC_NAME_BACKUP	"backup"
115 #define DICT_MC_NAME_KEY_FMT	"key_format"
116 #define DICT_MC_NAME_MC_TTL	"ttl"
117 #define DICT_MC_NAME_MC_TIMEOUT	"timeout"
118 #define DICT_MC_NAME_MC_FLAGS	"flags"
119 #define DICT_MC_NAME_MAX_TRY	"max_try"
120 #define DICT_MC_NAME_MAX_LINE	"line_size_limit"
121 #define DICT_MC_NAME_MAX_DATA	"data_size_limit"
122 #define DICT_MC_NAME_ERR_PAUSE	"retry_pause"
123 
124  /*
125   * SLMs.
126   */
127 #define STR(x)	vstring_str(x)
128 #define LEN(x)	VSTRING_LEN(x)
129 
130 /*#define msg_verbose 1*/
131 
132 /* dict_memcache_set - set memcache key/value */
133 
134 static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
135 {
136     VSTREAM *fp;
137     int     count;
138     size_t  data_len = strlen(value);
139 
140     /*
141      * Return a permanent error if we can't store this data. This results in
142      * loss of information.
143      */
144     if (data_len > dict_mc->max_data) {
145 	msg_warn("database %s:%s: data for key %s is too long (%s=%d) "
146 		 "-- not stored", DICT_TYPE_MEMCACHE, dict_mc->dict.name,
147 		 STR(dict_mc->key_buf), DICT_MC_NAME_MAX_DATA,
148 		 dict_mc->max_data);
149 	/* Not stored! */
150 	DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_FAIL);
151     }
152     for (count = 0; count < dict_mc->max_tries; count++) {
153 	if (count > 0)
154 	    sleep(dict_mc->err_pause);
155 	if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
156 	    break;
157 	} else if (memcache_printf(fp, "set %s %d %d %ld",
158 				   STR(dict_mc->key_buf), dict_mc->mc_flags,
159 				   ttl, (long) data_len) < 0
160 		   || memcache_fwrite(fp, value, strlen(value)) < 0
161 		   || memcache_get(fp, dict_mc->clnt_buf,
162 				   dict_mc->max_line) < 0) {
163 	    if (count > 0)
164 		msg_warn(errno ? "database %s:%s: I/O error: %m" :
165 			 "database %s:%s: I/O error",
166 			 DICT_TYPE_MEMCACHE, dict_mc->dict.name);
167 	} else if (strcmp(STR(dict_mc->clnt_buf), "STORED") != 0) {
168 	    if (count > 0)
169 		msg_warn("database %s:%s: update failed: %.30s",
170 			 DICT_TYPE_MEMCACHE, dict_mc->dict.name,
171 			 STR(dict_mc->clnt_buf));
172 	} else {
173 	    /* Victory! */
174 	    DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_SUCCESS);
175 	}
176 	auto_clnt_recover(dict_mc->clnt);
177     }
178     DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, DICT_STAT_ERROR);
179 }
180 
181 /* dict_memcache_get - get memcache key/value */
182 
183 static const char *dict_memcache_get(DICT_MC *dict_mc)
184 {
185     VSTREAM *fp;
186     long    todo;
187     int     count;
188 
189     for (count = 0; count < dict_mc->max_tries; count++) {
190 	if (count > 0)
191 	    sleep(dict_mc->err_pause);
192 	if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
193 	    break;
194 	} else if (memcache_printf(fp, "get %s", STR(dict_mc->key_buf)) < 0
195 	    || memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0) {
196 	    if (count > 0)
197 		msg_warn(errno ? "database %s:%s: I/O error: %m" :
198 			 "database %s:%s: I/O error",
199 			 DICT_TYPE_MEMCACHE, dict_mc->dict.name);
200 	} else if (strcmp(STR(dict_mc->clnt_buf), "END") == 0) {
201 	    /* Not found. */
202 	    DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, (char *) 0);
203 	} else if (sscanf(STR(dict_mc->clnt_buf),
204 			  "VALUE %*s %*s %ld", &todo) != 1
205 		   || todo < 0 || todo > dict_mc->max_data) {
206 	    if (count > 0)
207 		msg_warn("%s: unexpected memcache server reply: %.30s",
208 			 dict_mc->dict.name, STR(dict_mc->clnt_buf));
209 	} else if (memcache_fread(fp, dict_mc->res_buf, todo) < 0) {
210 	    if (count > 0)
211 		msg_warn("%s: EOF receiving memcache server reply",
212 			 dict_mc->dict.name);
213 	} else {
214 	    /* Victory! */
215 	    if (memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0
216 		|| strcmp(STR(dict_mc->clnt_buf), "END") != 0)
217 		auto_clnt_recover(dict_mc->clnt);
218 	    DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, STR(dict_mc->res_buf));
219 	}
220 	auto_clnt_recover(dict_mc->clnt);
221     }
222     DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, (char *) 0);
223 }
224 
225 /* dict_memcache_del - delete memcache key/value */
226 
227 static int dict_memcache_del(DICT_MC *dict_mc)
228 {
229     VSTREAM *fp;
230     int     count;
231 
232     for (count = 0; count < dict_mc->max_tries; count++) {
233 	if (count > 0)
234 	    sleep(dict_mc->err_pause);
235 	if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
236 	    break;
237 	} else if (memcache_printf(fp, "delete %s", STR(dict_mc->key_buf)) < 0
238 	    || memcache_get(fp, dict_mc->clnt_buf, dict_mc->max_line) < 0) {
239 	    if (count > 0)
240 		msg_warn(errno ? "database %s:%s: I/O error: %m" :
241 			 "database %s:%s: I/O error",
242 			 DICT_TYPE_MEMCACHE, dict_mc->dict.name);
243 	} else if (strcmp(STR(dict_mc->clnt_buf), "DELETED") == 0) {
244 	    /* Victory! */
245 	    DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_SUCCESS);
246 	} else if (strcmp(STR(dict_mc->clnt_buf), "NOT_FOUND") == 0) {
247 	    /* Not found! */
248 	    DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, DICT_STAT_FAIL);
249 	} else {
250 	    if (count > 0)
251 		msg_warn("database %s:%s: delete failed: %.30s",
252 			 DICT_TYPE_MEMCACHE, dict_mc->dict.name,
253 			 STR(dict_mc->clnt_buf));
254 	}
255 	auto_clnt_recover(dict_mc->clnt);
256     }
257     DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_RETRY, DICT_STAT_ERROR);
258 }
259 
260 /* dict_memcache_prepare_key - prepare lookup key */
261 
262 static ssize_t dict_memcache_prepare_key(DICT_MC *dict_mc, const char *name)
263 {
264 
265     /*
266      * Optionally case-fold the search string.
267      */
268     if (dict_mc->dict.flags & DICT_FLAG_FOLD_FIX) {
269 	if (dict_mc->dict.fold_buf == 0)
270 	    dict_mc->dict.fold_buf = vstring_alloc(10);
271 	vstring_strcpy(dict_mc->dict.fold_buf, name);
272 	name = lowercase(STR(dict_mc->dict.fold_buf));
273     }
274 
275     /*
276      * Optionally expand the query key format.
277      */
278 #define DICT_MC_NO_KEY		(0)
279 #define DICT_MC_NO_QUOTING	((void (*)(DICT *, const char *, VSTRING *)) 0)
280 
281     if (dict_mc->key_format != 0
282 	&& strcmp(dict_mc->key_format, DICT_MC_DEF_KEY_FMT) != 0) {
283 	VSTRING_RESET(dict_mc->key_buf);
284 	if (db_common_expand(dict_mc->dbc_ctxt, dict_mc->key_format,
285 			     name, DICT_MC_NO_KEY, dict_mc->key_buf,
286 			     DICT_MC_NO_QUOTING) == 0)
287 	    return (0);
288     } else {
289 	vstring_strcpy(dict_mc->key_buf, name);
290     }
291 
292     /*
293      * The length indicates whether the expansion is empty or not.
294      */
295     return (LEN(dict_mc->key_buf));
296 }
297 
298 /* dict_memcache_valid_key - validate key */
299 
300 static int dict_memcache_valid_key(DICT_MC *dict_mc,
301 				           const char *name,
302 				           const char *operation,
303 				        void (*log_func) (const char *,...))
304 {
305     unsigned char *cp;
306     int     rc;
307 
308 #define DICT_MC_SKIP(why) do { \
309 	if (msg_verbose || log_func != msg_info) \
310 	    log_func("%s: skipping %s for name \"%s\": %s", \
311 		     dict_mc->dict.name, operation, name, (why)); \
312 	DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, 0); \
313     } while (0)
314 
315     if (*name == 0)
316 	DICT_MC_SKIP("empty lookup key");
317     if ((rc = db_common_check_domain(dict_mc->dbc_ctxt, name)) == 0)
318 	DICT_MC_SKIP("domain mismatch");
319     if (rc < 0)
320 	DICT_ERR_VAL_RETURN(dict_mc, rc, 0);
321     if (dict_memcache_prepare_key(dict_mc, name) == 0)
322 	DICT_MC_SKIP("empty lookup key expansion");
323     for (cp = (unsigned char *) STR(dict_mc->key_buf); *cp; cp++)
324 	if (isascii(*cp) && isspace(*cp))
325 	    DICT_MC_SKIP("name contains space");
326 
327     DICT_ERR_VAL_RETURN(dict_mc, DICT_ERR_NONE, 1);
328 }
329 
330 /* dict_memcache_update - update memcache */
331 
332 static int dict_memcache_update(DICT *dict, const char *name,
333 				        const char *value)
334 {
335     const char *myname = "dict_memcache_update";
336     DICT_MC *dict_mc = (DICT_MC *) dict;
337     DICT   *backup = dict_mc->backup;
338     int     upd_res;
339 
340     /*
341      * Skip updates with an inapplicable key, noisily. This results in loss
342      * of information.
343      */
344     if (dict_memcache_valid_key(dict_mc, name, "update", msg_warn) == 0)
345 	DICT_ERR_VAL_RETURN(dict, dict_mc->error, DICT_STAT_FAIL);
346 
347     /*
348      * Update the memcache first.
349      */
350     upd_res = dict_memcache_set(dict_mc, value, dict_mc->mc_ttl);
351     dict->error = dict_mc->error;
352 
353     /*
354      * Update the backup database last.
355      */
356     if (backup) {
357 	upd_res = backup->update(backup, name, value);
358 	dict->error = backup->error;
359     }
360     if (msg_verbose)
361 	msg_info("%s: %s: update key \"%s\"(%s) => \"%s\" %s",
362 		 myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
363 		 value, dict_mc->error ? "(memcache error)" : (backup
364 		       && backup->error) ? "(backup error)" : "(no error)");
365 
366     return (upd_res);
367 }
368 
369 /* dict_memcache_lookup - lookup memcache */
370 
371 static const char *dict_memcache_lookup(DICT *dict, const char *name)
372 {
373     const char *myname = "dict_memcache_lookup";
374     DICT_MC *dict_mc = (DICT_MC *) dict;
375     DICT   *backup = dict_mc->backup;
376     const char *retval;
377 
378     /*
379      * Skip lookups with an inapplicable key, silently. This is just asking
380      * for information that cannot exist.
381      */
382     if (dict_memcache_valid_key(dict_mc, name, "lookup", msg_info) == 0)
383 	DICT_ERR_VAL_RETURN(dict, dict_mc->error, (char *) 0);
384 
385     /*
386      * Search the memcache first.
387      */
388     retval = dict_memcache_get(dict_mc);
389     dict->error = dict_mc->error;
390 
391     /*
392      * Search the backup database last. Update the memcache if the data is
393      * found.
394      */
395     if (backup) {
396 	backup->error = 0;
397 	if (retval == 0) {
398 	    retval = backup->lookup(backup, name);
399 	    dict->error = backup->error;
400 	    /* Update the cache. */
401 	    if (retval != 0)
402 		dict_memcache_set(dict_mc, retval, dict_mc->mc_ttl);
403 	}
404     }
405     if (msg_verbose)
406 	msg_info("%s: %s: key \"%s\"(%s) => %s",
407 		 myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
408 		 retval ? retval : dict_mc->error ? "(memcache error)" :
409 	      (backup && backup->error) ? "(backup error)" : "(not found)");
410 
411     return (retval);
412 }
413 
414 /* dict_memcache_delete - delete memcache entry */
415 
416 static int dict_memcache_delete(DICT *dict, const char *name)
417 {
418     const char *myname = "dict_memcache_delete";
419     DICT_MC *dict_mc = (DICT_MC *) dict;
420     DICT   *backup = dict_mc->backup;
421     int     del_res;
422 
423     /*
424      * Skip lookups with an inapplicable key, noisily. This is just deleting
425      * information that cannot exist.
426      */
427     if (dict_memcache_valid_key(dict_mc, name, "delete", msg_info) == 0)
428 	DICT_ERR_VAL_RETURN(dict, dict_mc->error, dict_mc->error ?
429 			    DICT_STAT_ERROR : DICT_STAT_FAIL);
430 
431     /*
432      * Update the memcache first.
433      */
434     del_res = dict_memcache_del(dict_mc);
435     dict->error = dict_mc->error;
436 
437     /*
438      * Update the persistent database last.
439      */
440     if (backup) {
441 	del_res = backup->delete(backup, name);
442 	dict->error = backup->error;
443     }
444     if (msg_verbose)
445 	msg_info("%s: %s: delete key \"%s\"(%s) => %s",
446 		 myname, dict_mc->dict.name, name, STR(dict_mc->key_buf),
447 		 dict_mc->error ? "(memcache error)" : (backup
448 		       && backup->error) ? "(backup error)" : "(no error)");
449 
450     return (del_res);
451 }
452 
453 /* dict_memcache_sequence - first/next lookup */
454 
455 static int dict_memcache_sequence(DICT *dict, int function, const char **key,
456 				          const char **value)
457 {
458     const char *myname = "dict_memcache_sequence";
459     DICT_MC *dict_mc = (DICT_MC *) dict;
460     DICT   *backup = dict_mc->backup;
461     int     seq_res;
462 
463     if (backup == 0) {
464 	msg_warn("database %s:%s: first/next support requires backup database",
465 		 DICT_TYPE_MEMCACHE, dict_mc->dict.name);
466 	DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_FAIL);
467     } else {
468 	seq_res = backup->sequence(backup, function, key, value);
469 	if (msg_verbose)
470 	    msg_info("%s: %s: key \"%s\" => %s",
471 		     myname, dict_mc->dict.name, *key ? *key : "(not found)",
472 		     *value ? *value : backup->error ? "(backup error)" :
473 		     "(not found)");
474 	DICT_ERR_VAL_RETURN(dict, backup->error, seq_res);
475     }
476 }
477 
478 /* dict_memcache_close - close memcache */
479 
480 static void dict_memcache_close(DICT *dict)
481 {
482     DICT_MC *dict_mc = (DICT_MC *) dict;
483 
484     cfg_parser_free(dict_mc->parser);
485     db_common_free_ctx(dict_mc->dbc_ctxt);
486     if (dict_mc->key_format)
487 	myfree(dict_mc->key_format);
488     myfree(dict_mc->memcache);
489     auto_clnt_free(dict_mc->clnt);
490     vstring_free(dict_mc->clnt_buf);
491     vstring_free(dict_mc->key_buf);
492     vstring_free(dict_mc->res_buf);
493     if (dict->fold_buf)
494 	vstring_free(dict->fold_buf);
495     if (dict_mc->backup)
496 	dict_close(dict_mc->backup);
497     dict_free(dict);
498 }
499 
500 /* dict_memcache_open - open memcache */
501 
502 DICT   *dict_memcache_open(const char *name, int open_flags, int dict_flags)
503 {
504     DICT_MC *dict_mc;
505     char   *backup;
506     CFG_PARSER *parser;
507 
508     /*
509      * Sanity checks.
510      */
511     if (dict_flags & DICT_FLAG_NO_UNAUTH)
512 	return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
513 		     "%s:%s map is not allowed for security-sensitive data",
514 			       DICT_TYPE_MEMCACHE, name));
515     open_flags &= (O_RDONLY | O_RDWR | O_WRONLY | O_APPEND);
516     if (open_flags != O_RDONLY && open_flags != O_RDWR)
517 	return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
518 			"%s:%s map requires O_RDONLY or O_RDWR access mode",
519 			       DICT_TYPE_MEMCACHE, name));
520 
521     /*
522      * Open the configuration file.
523      */
524     if ((parser = cfg_parser_alloc(name)) == 0)
525 	return (dict_surrogate(DICT_TYPE_MEMCACHE, name, open_flags, dict_flags,
526 			       "open %s: %m", name));
527 
528     /*
529      * Create the dictionary object.
530      */
531     dict_mc = (DICT_MC *) dict_alloc(DICT_TYPE_MEMCACHE, name,
532 				     sizeof(*dict_mc));
533     dict_mc->dict.lookup = dict_memcache_lookup;
534     if (open_flags == O_RDWR) {
535 	dict_mc->dict.update = dict_memcache_update;
536 	dict_mc->dict.delete = dict_memcache_delete;
537     }
538     dict_mc->dict.sequence = dict_memcache_sequence;
539     dict_mc->dict.close = dict_memcache_close;
540     dict_mc->dict.flags = dict_flags;
541     dict_mc->key_buf = vstring_alloc(10);
542     dict_mc->res_buf = vstring_alloc(10);
543 
544     /*
545      * Parse the configuration file.
546      */
547     dict_mc->parser = parser;
548     dict_mc->key_format = cfg_get_str(dict_mc->parser, DICT_MC_NAME_KEY_FMT,
549 				      DICT_MC_DEF_KEY_FMT, 0, 0);
550     dict_mc->timeout = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_TIMEOUT,
551 				   DICT_MC_DEF_MC_TIMEOUT, 0, 0);
552     dict_mc->mc_ttl = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_TTL,
553 				  DICT_MC_DEF_MC_TTL, 0, 0);
554     dict_mc->mc_flags = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MC_FLAGS,
555 				    DICT_MC_DEF_MC_FLAGS, 0, 0);
556     dict_mc->err_pause = cfg_get_int(dict_mc->parser, DICT_MC_NAME_ERR_PAUSE,
557 				     DICT_MC_DEF_ERR_PAUSE, 1, 0);
558     dict_mc->max_tries = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_TRY,
559 				     DICT_MC_DEF_MAX_TRY, 1, 0);
560     dict_mc->max_line = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_LINE,
561 				    DICT_MC_DEF_MAX_LINE, 1, 0);
562     dict_mc->max_data = cfg_get_int(dict_mc->parser, DICT_MC_NAME_MAX_DATA,
563 				    DICT_MC_DEF_MAX_DATA, 1, 0);
564     dict_mc->memcache = cfg_get_str(dict_mc->parser, DICT_MC_NAME_MEMCACHE,
565 				    DICT_MC_DEF_MEMCACHE, 0, 0);
566 
567     /*
568      * Initialize the memcache client.
569      */
570     dict_mc->clnt = auto_clnt_create(dict_mc->memcache, dict_mc->timeout, 0, 0);
571     dict_mc->clnt_buf = vstring_alloc(100);
572 
573     /*
574      * Open the optional backup database.
575      */
576     backup = cfg_get_str(dict_mc->parser, DICT_MC_NAME_BACKUP,
577 			 (char *) 0, 0, 0);
578     if (backup) {
579 	dict_mc->backup = dict_open(backup, open_flags, dict_flags);
580 	myfree(backup);
581     } else
582 	dict_mc->backup = 0;
583 
584     /*
585      * Parse templates and common database parameters. Maps that use
586      * substring keys should only be used with the full input key.
587      */
588     dict_mc->dbc_ctxt = 0;
589     db_common_parse(&dict_mc->dict, &dict_mc->dbc_ctxt,
590 		    dict_mc->key_format, 1);
591     db_common_parse_domain(dict_mc->parser, dict_mc->dbc_ctxt);
592     if (db_common_dict_partial(dict_mc->dbc_ctxt))
593 	/* Breaks recipient delimiters */
594 	dict_mc->dict.flags |= DICT_FLAG_PATTERN;
595     else
596 	dict_mc->dict.flags |= DICT_FLAG_FIXED;
597 
598     dict_mc->dict.flags |= DICT_FLAG_MULTI_WRITER;
599 
600     return (&dict_mc->dict);
601 }
602