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