Postfix3.3.1
全て データ構造 ファイル 関数 変数 型定義 マクロ定義
postmap.c
[詳解]
1 /*++
2 /* NAME
3 /* postmap 1
4 /* SUMMARY
5 /* Postfix lookup table management
6 /* SYNOPSIS
7 /* .fi
8 /* \fBpostmap\fR [\fB-NbfhimnoprsuUvw\fR] [\fB-c \fIconfig_dir\fR]
9 /* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
10 /* [\fIfile_type\fR:]\fIfile_name\fR ...
11 /* DESCRIPTION
12 /* The \fBpostmap\fR(1) command creates or queries one or more Postfix
13 /* lookup tables, or updates an existing one. The input and output
14 /* file formats are expected to be compatible with:
15 /*
16 /* .nf
17 /* \fBmakemap \fIfile_type\fR \fIfile_name\fR < \fIfile_name\fR
18 /* .fi
19 /*
20 /* If the result files do not exist they will be created with the
21 /* same group and other read permissions as their source file.
22 /*
23 /* While the table update is in progress, signal delivery is
24 /* postponed, and an exclusive, advisory, lock is placed on the
25 /* entire table, in order to avoid surprises in spectator
26 /* processes.
27 /* INPUT FILE FORMAT
28 /* .ad
29 /* .fi
30 /* The format of a lookup table input file is as follows:
31 /* .IP \(bu
32 /* A table entry has the form
33 /* .sp
34 /* .nf
35 /* \fIkey\fR whitespace \fIvalue\fR
36 /* .fi
37 /* .IP \(bu
38 /* Empty lines and whitespace-only lines are ignored, as
39 /* are lines whose first non-whitespace character is a `#'.
40 /* .IP \(bu
41 /* A logical line starts with non-whitespace text. A line that
42 /* starts with whitespace continues a logical line.
43 /* .PP
44 /* The \fIkey\fR and \fIvalue\fR are processed as is, except that
45 /* surrounding white space is stripped off. Whitespace in lookup
46 /* keys is supported as of Postfix 3.2.
47 /*
48 /* When the \fIkey\fR specifies email address information, the
49 /* localpart should be enclosed with double quotes if required
50 /* by RFC 5322. For example, an address localpart that contains
51 /* ";", or a localpart that starts or ends with ".".
52 /*
53 /* By default the lookup key is mapped to lowercase to make
54 /* the lookups case insensitive; as of Postfix 2.3 this case
55 /* folding happens only with tables whose lookup keys are
56 /* fixed-case strings such as btree:, dbm: or hash:. With
57 /* earlier versions, the lookup key is folded even with tables
58 /* where a lookup field can match both upper and lower case
59 /* text, such as regexp: and pcre:. This resulted in loss of
60 /* information with $\fInumber\fR substitutions.
61 /* COMMAND-LINE ARGUMENTS
62 /* .ad
63 /* .fi
64 /* .IP \fB-b\fR
65 /* Enable message body query mode. When reading lookup keys
66 /* from standard input with "\fB-q -\fR", process the input
67 /* as if it is an email message in RFC 5322 format. Each line
68 /* of body content becomes one lookup key.
69 /* .sp
70 /* By default, the \fB-b\fR option starts generating lookup
71 /* keys at the first non-header line, and stops when the end
72 /* of the message is reached.
73 /* To simulate \fBbody_checks\fR(5) processing, enable MIME
74 /* parsing with \fB-m\fR. With this, the \fB-b\fR option
75 /* generates no body-style lookup keys for attachment MIME
76 /* headers and for attached message/* headers.
77 /* .sp
78 /* NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
79 /* option disables UTF-8 syntax checks on query keys and
80 /* lookup results. Specify the \fB-U\fR option to force UTF-8
81 /* syntax checks anyway.
82 /* .sp
83 /* This feature is available in Postfix version 2.6 and later.
84 /* .IP "\fB-c \fIconfig_dir\fR"
85 /* Read the \fBmain.cf\fR configuration file in the named directory
86 /* instead of the default configuration directory.
87 /* .IP "\fB-d \fIkey\fR"
88 /* Search the specified maps for \fIkey\fR and remove one entry per map.
89 /* The exit status is zero when the requested information was found.
90 /*
91 /* If a key value of \fB-\fR is specified, the program reads key
92 /* values from the standard input stream. The exit status is zero
93 /* when at least one of the requested keys was found.
94 /* .IP \fB-f\fR
95 /* Do not fold the lookup key to lower case while creating or querying
96 /* a table.
97 /*
98 /* With Postfix version 2.3 and later, this option has no
99 /* effect for regular expression tables. There, case folding
100 /* is controlled by appending a flag to a pattern.
101 /* .IP \fB-h\fR
102 /* Enable message header query mode. When reading lookup keys
103 /* from standard input with "\fB-q -\fR", process the input
104 /* as if it is an email message in RFC 5322 format. Each
105 /* logical header line becomes one lookup key. A multi-line
106 /* header becomes one lookup key with one or more embedded
107 /* newline characters.
108 /* .sp
109 /* By default, the \fB-h\fR option generates lookup keys until
110 /* the first non-header line is reached.
111 /* To simulate \fBheader_checks\fR(5) processing, enable MIME
112 /* parsing with \fB-m\fR. With this, the \fB-h\fR option also
113 /* generates header-style lookup keys for attachment MIME
114 /* headers and for attached message/* headers.
115 /* .sp
116 /* NOTE: with "smtputf8_enable = yes", the \fB-b\fR option
117 /* option disables UTF-8 syntax checks on query keys and
118 /* lookup results. Specify the \fB-U\fR option to force UTF-8
119 /* syntax checks anyway.
120 /* .sp
121 /* This feature is available in Postfix version 2.6 and later.
122 /* .IP \fB-i\fR
123 /* Incremental mode. Read entries from standard input and do not
124 /* truncate an existing database. By default, \fBpostmap\fR(1) creates
125 /* a new database from the entries in \fBfile_name\fR.
126 /* .IP \fB-m\fR
127 /* Enable MIME parsing with "\fB-b\fR" and "\fB-h\fR".
128 /* .sp
129 /* This feature is available in Postfix version 2.6 and later.
130 /* .IP \fB-N\fR
131 /* Include the terminating null character that terminates lookup keys
132 /* and values. By default, \fBpostmap\fR(1) does whatever is
133 /* the default for
134 /* the host operating system.
135 /* .IP \fB-n\fR
136 /* Don't include the terminating null character that terminates lookup
137 /* keys and values. By default, \fBpostmap\fR(1) does whatever
138 /* is the default for
139 /* the host operating system.
140 /* .IP \fB-o\fR
141 /* Do not release root privileges when processing a non-root
142 /* input file. By default, \fBpostmap\fR(1) drops root privileges
143 /* and runs as the source file owner instead.
144 /* .IP \fB-p\fR
145 /* Do not inherit the file access permissions from the input file
146 /* when creating a new file. Instead, create a new file with default
147 /* access permissions (mode 0644).
148 /* .IP "\fB-q \fIkey\fR"
149 /* Search the specified maps for \fIkey\fR and write the first value
150 /* found to the standard output stream. The exit status is zero
151 /* when the requested information was found.
152 /*
153 /* If a key value of \fB-\fR is specified, the program reads key
154 /* values from the standard input stream and writes one line of
155 /* \fIkey value\fR output for each key that was found. The exit
156 /* status is zero when at least one of the requested keys was found.
157 /* .IP \fB-r\fR
158 /* When updating a table, do not complain about attempts to update
159 /* existing entries, and make those updates anyway.
160 /* .IP \fB-s\fR
161 /* Retrieve all database elements, and write one line of
162 /* \fIkey value\fR output for each element. The elements are
163 /* printed in database order, which is not necessarily the same
164 /* as the original input order.
165 /* .sp
166 /* This feature is available in Postfix version 2.2 and later,
167 /* and is not available for all database types.
168 /* .IP \fB-u\fR
169 /* Disable UTF-8 support. UTF-8 support is enabled by default
170 /* when "smtputf8_enable = yes". It requires that keys and
171 /* values are valid UTF-8 strings.
172 /* .IP \fB-U\fR
173 /* With "smtputf8_enable = yes", force UTF-8 syntax checks
174 /* with the \fB-b\fR and \fB-h\fR options.
175 /* .IP \fB-v\fR
176 /* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
177 /* options make the software increasingly verbose.
178 /* .IP \fB-w\fR
179 /* When updating a table, do not complain about attempts to update
180 /* existing entries, and ignore those attempts.
181 /* .PP
182 /* Arguments:
183 /* .IP \fIfile_type\fR
184 /* The database type. To find out what types are supported, use
185 /* the "\fBpostconf -m\fR" command.
186 /*
187 /* The \fBpostmap\fR(1) command can query any supported file type,
188 /* but it can create only the following file types:
189 /* .RS
190 /* .IP \fBbtree\fR
191 /* The output file is a btree file, named \fIfile_name\fB.db\fR.
192 /* This is available on systems with support for \fBdb\fR databases.
193 /* .IP \fBcdb\fR
194 /* The output consists of one file, named \fIfile_name\fB.cdb\fR.
195 /* This is available on systems with support for \fBcdb\fR databases.
196 /* .IP \fBdbm\fR
197 /* The output consists of two files, named \fIfile_name\fB.pag\fR and
198 /* \fIfile_name\fB.dir\fR.
199 /* This is available on systems with support for \fBdbm\fR databases.
200 /* .IP \fBhash\fR
201 /* The output file is a hashed file, named \fIfile_name\fB.db\fR.
202 /* This is available on systems with support for \fBdb\fR databases.
203 /* .IP \fBfail\fR
204 /* A table that reliably fails all requests. The lookup table
205 /* name is used for logging only. This table exists to simplify
206 /* Postfix error tests.
207 /* .IP \fBsdbm\fR
208 /* The output consists of two files, named \fIfile_name\fB.pag\fR and
209 /* \fIfile_name\fB.dir\fR.
210 /* This is available on systems with support for \fBsdbm\fR databases.
211 /* .PP
212 /* When no \fIfile_type\fR is specified, the software uses the database
213 /* type specified via the \fBdefault_database_type\fR configuration
214 /* parameter.
215 /* .RE
216 /* .IP \fIfile_name\fR
217 /* The name of the lookup table source file when rebuilding a database.
218 /* DIAGNOSTICS
219 /* Problems are logged to the standard error stream and to
220 /* \fBsyslogd\fR(8).
221 /* No output means that no problems were detected. Duplicate entries are
222 /* skipped and are flagged with a warning.
223 /*
224 /* \fBpostmap\fR(1) terminates with zero exit status in case of success
225 /* (including successful "\fBpostmap -q\fR" lookup) and terminates
226 /* with non-zero exit status in case of failure.
227 /* ENVIRONMENT
228 /* .ad
229 /* .fi
230 /* .IP \fBMAIL_CONFIG\fR
231 /* Directory with Postfix configuration files.
232 /* .IP \fBMAIL_VERBOSE\fR
233 /* Enable verbose logging for debugging purposes.
234 /* CONFIGURATION PARAMETERS
235 /* .ad
236 /* .fi
237 /* The following \fBmain.cf\fR parameters are especially relevant to
238 /* this program.
239 /* The text below provides only a parameter summary. See
240 /* \fBpostconf\fR(5) for more details including examples.
241 /* .IP "\fBberkeley_db_create_buffer_size (16777216)\fR"
242 /* The per-table I/O buffer size for programs that create Berkeley DB
243 /* hash or btree tables.
244 /* .IP "\fBberkeley_db_read_buffer_size (131072)\fR"
245 /* The per-table I/O buffer size for programs that read Berkeley DB
246 /* hash or btree tables.
247 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
248 /* The default location of the Postfix main.cf and master.cf
249 /* configuration files.
250 /* .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
251 /* The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
252 /* and \fBpostmap\fR(1) commands.
253 /* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
254 /* The list of environment parameters that a privileged Postfix
255 /* process will import from a non-Postfix parent process, or name=value
256 /* environment overrides.
257 /* .IP "\fBsmtputf8_enable (yes)\fR"
258 /* Enable preliminary SMTPUTF8 support for the protocols described
259 /* in RFC 6531..6533.
260 /* .IP "\fBsyslog_facility (mail)\fR"
261 /* The syslog facility of Postfix logging.
262 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
263 /* A prefix that is prepended to the process name in syslog
264 /* records, so that, for example, "smtpd" becomes "prefix/smtpd".
265 /* SEE ALSO
266 /* postalias(1), create/update/query alias database
267 /* postconf(1), supported database types
268 /* postconf(5), configuration parameters
269 /* syslogd(8), system logging
270 /* README FILES
271 /* .ad
272 /* .fi
273 /* Use "\fBpostconf readme_directory\fR" or
274 /* "\fBpostconf html_directory\fR" to locate this information.
275 /* .na
276 /* .nf
277 /* DATABASE_README, Postfix lookup table overview
278 /* LICENSE
279 /* .ad
280 /* .fi
281 /* The Secure Mailer license must be distributed with this software.
282 /* AUTHOR(S)
283 /* Wietse Venema
284 /* IBM T.J. Watson Research
285 /* P.O. Box 704
286 /* Yorktown Heights, NY 10598, USA
287 /*
288 /* Wietse Venema
289 /* Google, Inc.
290 /* 111 8th Avenue
291 /* New York, NY 10011, USA
292 /*--*/
293 
294 /* System library. */
295 
296 #include <sys_defs.h>
297 #include <sys/stat.h>
298 #include <stdlib.h>
299 #include <unistd.h>
300 #include <fcntl.h>
301 #include <ctype.h>
302 #include <string.h>
303 
304 /* Utility library. */
305 
306 #include <msg.h>
307 #include <mymalloc.h>
308 #include <vstring.h>
309 #include <vstream.h>
310 #include <msg_vstream.h>
311 #include <msg_syslog.h>
312 #include <readlline.h>
313 #include <stringops.h>
314 #include <split_at.h>
315 #include <vstring_vstream.h>
316 #include <set_eugid.h>
317 #include <warn_stat.h>
318 #include <clean_env.h>
319 
320 /* Global library. */
321 
322 #include <mail_conf.h>
323 #include <mail_dict.h>
324 #include <mail_params.h>
325 #include <mail_version.h>
326 #include <mkmap.h>
327 #include <mail_task.h>
328 #include <dict_proxy.h>
329 #include <mime_state.h>
330 #include <rec_type.h>
331 #include <mail_parm_split.h>
332 
333 /* Application-specific. */
334 
335 #define STR vstring_str
336 #define LEN VSTRING_LEN
337 
338 #define POSTMAP_FLAG_AS_OWNER (1<<0) /* open dest as owner of source */
339 #define POSTMAP_FLAG_SAVE_PERM (1<<1) /* copy access permission from source */
340 #define POSTMAP_FLAG_HEADER_KEY (1<<2) /* apply to header text */
341 #define POSTMAP_FLAG_BODY_KEY (1<<3) /* apply to body text */
342 #define POSTMAP_FLAG_MIME_KEY (1<<4) /* enable MIME parsing */
343 
344 #define POSTMAP_FLAG_HB_KEY (POSTMAP_FLAG_HEADER_KEY | POSTMAP_FLAG_BODY_KEY)
345 #define POSTMAP_FLAG_FULL_KEY (POSTMAP_FLAG_BODY_KEY | POSTMAP_FLAG_MIME_KEY)
346 #define POSTMAP_FLAG_ANY_KEY (POSTMAP_FLAG_HB_KEY | POSTMAP_FLAG_MIME_KEY)
347 
348  /*
349  * MIME Engine call-back state for generating lookup keys from an email
350  * message read from standard input.
351  */
352 typedef struct {
353  DICT **dicts; /* map handles */
354  char **maps; /* map names */
355  int map_count; /* yes, indeed */
356  int dict_flags; /* query flags */
357  int header_done; /* past primary header */
358  int found; /* result */
360 
361 /* postmap - create or update mapping database */
362 
363 static void postmap(char *map_type, char *path_name, int postmap_flags,
364  int open_flags, int dict_flags)
365 {
366  VSTREAM *NOCLOBBER source_fp;
367  VSTRING *line_buffer;
368  MKMAP *mkmap;
369  int lineno;
370  int last_line;
371  char *key;
372  char *value;
373  struct stat st;
374  mode_t saved_mask;
375 
376  /*
377  * Initialize.
378  */
379  line_buffer = vstring_alloc(100);
380  if ((open_flags & O_TRUNC) == 0) {
381  /* Incremental mode. */
382  source_fp = VSTREAM_IN;
384  } else {
385  /* Create database. */
386  if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
387  msg_fatal("can't create maps via the proxy service");
388  dict_flags |= DICT_FLAG_BULK_UPDATE;
389  if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0)
390  msg_fatal("open %s: %m", path_name);
391  }
392  if (fstat(vstream_fileno(source_fp), &st) < 0)
393  msg_fatal("fstat %s: %m", path_name);
394 
395  /*
396  * Turn off group/other read permissions as indicated in the source file.
397  */
398  if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
399  saved_mask = umask(022 | (~st.st_mode & 077));
400 
401  /*
402  * If running as root, run as the owner of the source file, so that the
403  * result shows proper ownership, and so that a bug in postmap does not
404  * allow privilege escalation.
405  */
406  if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0
407  && (st.st_uid != geteuid() || st.st_gid != getegid()))
408  set_eugid(st.st_uid, st.st_gid);
409 
410  /*
411  * Open the database, optionally create it when it does not exist,
412  * optionally truncate it when it does exist, and lock out any
413  * spectators.
414  */
415  mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags);
416 
417  /*
418  * And restore the umask, in case it matters.
419  */
420  if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode))
421  umask(saved_mask);
422 
423  /*
424  * Trap "exceptions" so that we can restart a bulk-mode update after a
425  * recoverable error.
426  */
427  for (;;) {
428  if (dict_isjmp(mkmap->dict) != 0
429  && dict_setjmp(mkmap->dict) != 0
430  && vstream_fseek(source_fp, SEEK_SET, 0) < 0)
431  msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp));
432 
433  /*
434  * Add records to the database. XXX This duplicates the parser in
435  * dict_thash.c.
436  */
437  last_line = 0;
438  while (readllines(line_buffer, source_fp, &last_line, &lineno)) {
439  int in_quotes = 0;
440 
441  /*
442  * First some UTF-8 checks sans casefolding.
443  */
444  if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE)
445  && !allascii(STR(line_buffer))
446  && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) {
447  msg_warn("%s, line %d: non-UTF-8 input \"%s\""
448  " -- ignoring this line",
449  VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
450  continue;
451  }
452 
453  /*
454  * Terminate the key on the first unquoted whitespace character,
455  * then trim leading and trailing whitespace from the value.
456  */
457  for (value = STR(line_buffer); *value; value++) {
458  if (*value == '\\') {
459  if (*++value == 0)
460  break;
461  } else if (ISSPACE(*value)) {
462  if (!in_quotes)
463  break;
464  } else if (*value == '"') {
465  in_quotes = !in_quotes;
466  }
467  }
468  if (in_quotes) {
469  msg_warn("%s, line %d: unbalanced '\"' in '%s'"
470  " -- ignoring this line",
471  VSTREAM_PATH(source_fp), lineno, STR(line_buffer));
472  continue;
473  }
474  if (*value)
475  *value++ = 0;
476  while (ISSPACE(*value))
477  value++;
478  trimblanks(value, 0)[0] = 0;
479 
480  /*
481  * Leave the key in quoted form, because 1) postmap cannot assume
482  * that a string without @ contains an email address localpart,
483  * and 2) an address localpart may require quoting even when the
484  * quoted form contains no backslash or ".
485  */
486  key = STR(line_buffer);
487 
488  /*
489  * Enforce the "key whitespace value" format. Disallow missing
490  * keys or missing values.
491  */
492  if (*key == 0 || *value == 0) {
493  msg_warn("%s, line %d: expected format: key whitespace value",
494  VSTREAM_PATH(source_fp), lineno);
495  continue;
496  }
497  if (key[strlen(key) - 1] == ':')
498  msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?",
499  VSTREAM_PATH(source_fp), lineno);
500 
501  /*
502  * Store the value under a (possibly case-insensitive) key, as
503  * specified with open_flags.
504  */
505  mkmap_append(mkmap, key, value);
506  if (mkmap->dict->error)
507  msg_fatal("table %s:%s: write error: %m",
508  mkmap->dict->type, mkmap->dict->name);
509  }
510  break;
511  }
512 
513  /*
514  * Close the mapping database, and release the lock.
515  */
516  mkmap_close(mkmap);
517 
518  /*
519  * Cleanup. We're about to terminate, but it is a good sanity check.
520  */
521  vstring_free(line_buffer);
522  if (source_fp != VSTREAM_IN)
523  vstream_fclose(source_fp);
524 }
525 
526 /* postmap_body - MIME engine body call-back routine */
527 
528 static void postmap_body(void *ptr, int unused_rec_type,
529  const char *keybuf,
530  ssize_t unused_len,
531  off_t unused_offset)
532 {
533  POSTMAP_KEY_STATE *state = (POSTMAP_KEY_STATE *) ptr;
534  DICT **dicts = state->dicts;
535  char **maps = state->maps;
536  int map_count = state->map_count;
537  int dict_flags = state->dict_flags;
538  const char *map_name;
539  const char *value;
540  int n;
541 
542  for (n = 0; n < map_count; n++) {
543  if (dicts[n] == 0)
544  dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
545  dict_open3(maps[n], map_name, O_RDONLY, dict_flags) :
546  dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags));
547  if ((value = dict_get(dicts[n], keybuf)) != 0) {
548  if (*value == 0) {
549  msg_warn("table %s:%s: key %s: empty string result is not allowed",
550  dicts[n]->type, dicts[n]->name, keybuf);
551  msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
552  dicts[n]->type, dicts[n]->name);
553  }
554  vstream_printf("%s %s\n", keybuf, value);
555  state->found = 1;
556  break;
557  }
558  if (dicts[n]->error)
559  msg_fatal("table %s:%s: query error: %m",
560  dicts[n]->type, dicts[n]->name);
561  }
562 }
563 
564 /* postmap_header - MIME engine header call-back routine */
565 
566 static void postmap_header(void *ptr, int unused_header_class,
567  const HEADER_OPTS *unused_header_info,
568  VSTRING *header_buf,
569  off_t offset)
570 {
571 
572  /*
573  * Don't re-invent an already working wheel.
574  */
575  postmap_body(ptr, 0, STR(header_buf), LEN(header_buf), offset);
576 }
577 
578 /* postmap_head_end - MIME engine end-of-header call-back routine */
579 
580 static void postmap_head_end(void *ptr)
581 {
582  POSTMAP_KEY_STATE *state = (POSTMAP_KEY_STATE *) ptr;
583 
584  /*
585  * Don't process the message body when we only examine primary headers.
586  */
587  state->header_done = 1;
588 }
589 
590 /* postmap_queries - apply multiple requests from stdin */
591 
592 static int postmap_queries(VSTREAM *in, char **maps, const int map_count,
593  const int postmap_flags,
594  const int dict_flags)
595 {
596  int found = 0;
597  VSTRING *keybuf = vstring_alloc(100);
598  DICT **dicts;
599  const char *map_name;
600  const char *value;
601  int n;
602 
603  /*
604  * Sanity check.
605  */
606  if (map_count <= 0)
607  msg_panic("postmap_queries: bad map count");
608 
609  /*
610  * Prepare to open maps lazily.
611  */
612  dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
613  for (n = 0; n < map_count; n++)
614  dicts[n] = 0;
615 
616  /*
617  * Perform all queries. Open maps on the fly, to avoid opening unecessary
618  * maps.
619  */
620  if ((postmap_flags & POSTMAP_FLAG_HB_KEY) == 0) {
621  while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
622  for (n = 0; n < map_count; n++) {
623  if (dicts[n] == 0)
624  dicts[n] = ((map_name = split_at(maps[n], ':')) != 0 ?
625  dict_open3(maps[n], map_name, O_RDONLY, dict_flags) :
626  dict_open3(var_db_type, maps[n], O_RDONLY, dict_flags));
627  if ((value = dict_get(dicts[n], STR(keybuf))) != 0) {
628  if (*value == 0) {
629  msg_warn("table %s:%s: key %s: empty string result is not allowed",
630  dicts[n]->type, dicts[n]->name, STR(keybuf));
631  msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
632  dicts[n]->type, dicts[n]->name);
633  }
634  vstream_printf("%s %s\n", STR(keybuf), value);
635  found = 1;
636  break;
637  }
638  if (dicts[n]->error)
639  msg_fatal("table %s:%s: query error: %m",
640  dicts[n]->type, dicts[n]->name);
641  }
642  }
643  } else {
644  POSTMAP_KEY_STATE key_state;
645  MIME_STATE *mime_state;
646  int mime_errs = 0;
647 
648  /*
649  * Bundle up the request and instantiate a MIME parsing engine.
650  */
651  key_state.dicts = dicts;
652  key_state.maps = maps;
653  key_state.map_count = map_count;
654  key_state.dict_flags = dict_flags;
655  key_state.header_done = 0;
656  key_state.found = 0;
657  mime_state =
658  mime_state_alloc((postmap_flags & POSTMAP_FLAG_MIME_KEY) ?
660  (postmap_flags & POSTMAP_FLAG_HEADER_KEY) ?
661  postmap_header : (MIME_STATE_HEAD_OUT) 0,
662  (postmap_flags & POSTMAP_FLAG_FULL_KEY) ?
663  (MIME_STATE_ANY_END) 0 : postmap_head_end,
664  (postmap_flags & POSTMAP_FLAG_BODY_KEY) ?
665  postmap_body : (MIME_STATE_BODY_OUT) 0,
666  (MIME_STATE_ANY_END) 0,
668  (void *) &key_state);
669 
670  /*
671  * Process the input message.
672  */
673  while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF
674  && key_state.header_done == 0 && mime_errs == 0)
675  mime_errs = mime_state_update(mime_state, REC_TYPE_NORM,
676  STR(keybuf), LEN(keybuf));
677 
678  /*
679  * Flush the MIME engine output buffer and tidy up loose ends.
680  */
681  if (mime_errs == 0)
682  mime_errs = mime_state_update(mime_state, REC_TYPE_END, "", 0);
683  if (mime_errs)
684  msg_fatal("message format error: %s",
685  mime_state_detail(mime_errs)->text);
686  mime_state_free(mime_state);
687  found = key_state.found;
688  }
689  if (found)
691 
692  /*
693  * Cleanup.
694  */
695  for (n = 0; n < map_count; n++)
696  if (dicts[n])
697  dict_close(dicts[n]);
698  myfree((void *) dicts);
699  vstring_free(keybuf);
700 
701  return (found);
702 }
703 
704 /* postmap_query - query a map and print the result to stdout */
705 
706 static int postmap_query(const char *map_type, const char *map_name,
707  const char *key, int dict_flags)
708 {
709  DICT *dict;
710  const char *value;
711 
712  dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
713  if ((value = dict_get(dict, key)) != 0) {
714  if (*value == 0) {
715  msg_warn("table %s:%s: key %s: empty string result is not allowed",
716  map_type, map_name, key);
717  msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
718  map_type, map_name);
719  }
720  vstream_printf("%s\n", value);
721  }
722  if (dict->error)
723  msg_fatal("table %s:%s: query error: %m", dict->type, dict->name);
725  dict_close(dict);
726  return (value != 0);
727 }
728 
729 /* postmap_deletes - apply multiple requests from stdin */
730 
731 static int postmap_deletes(VSTREAM *in, char **maps, const int map_count,
732  int dict_flags)
733 {
734  int found = 0;
735  VSTRING *keybuf = vstring_alloc(100);
736  DICT **dicts;
737  const char *map_name;
738  int n;
739  int open_flags;
740 
741  /*
742  * Sanity check.
743  */
744  if (map_count <= 0)
745  msg_panic("postmap_deletes: bad map count");
746 
747  /*
748  * Open maps ahead of time.
749  */
750  dicts = (DICT **) mymalloc(sizeof(*dicts) * map_count);
751  for (n = 0; n < map_count; n++) {
752  map_name = split_at(maps[n], ':');
753  if (map_name && strcmp(maps[n], DICT_TYPE_PROXY) == 0)
754  open_flags = O_RDWR | O_CREAT; /* XXX */
755  else
756  open_flags = O_RDWR;
757  dicts[n] = (map_name != 0 ?
758  dict_open3(maps[n], map_name, open_flags, dict_flags) :
759  dict_open3(var_db_type, maps[n], open_flags, dict_flags));
760  }
761 
762  /*
763  * Perform all requests.
764  */
765  while (vstring_get_nonl(keybuf, in) != VSTREAM_EOF) {
766  for (n = 0; n < map_count; n++) {
767  found |= (dict_del(dicts[n], STR(keybuf)) == 0);
768  if (dicts[n]->error)
769  msg_fatal("table %s:%s: delete error: %m",
770  dicts[n]->type, dicts[n]->name);
771  }
772  }
773 
774  /*
775  * Cleanup.
776  */
777  for (n = 0; n < map_count; n++)
778  if (dicts[n])
779  dict_close(dicts[n]);
780  myfree((void *) dicts);
781  vstring_free(keybuf);
782 
783  return (found);
784 }
785 
786 /* postmap_delete - delete a (key, value) pair from a map */
787 
788 static int postmap_delete(const char *map_type, const char *map_name,
789  const char *key, int dict_flags)
790 {
791  DICT *dict;
792  int status;
793  int open_flags;
794 
795  if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
796  open_flags = O_RDWR | O_CREAT; /* XXX */
797  else
798  open_flags = O_RDWR;
799  dict = dict_open3(map_type, map_name, open_flags, dict_flags);
800  status = dict_del(dict, key);
801  if (dict->error)
802  msg_fatal("table %s:%s: delete error: %m", dict->type, dict->name);
803  dict_close(dict);
804  return (status == 0);
805 }
806 
807 /* postmap_seq - print all map entries to stdout */
808 
809 static void postmap_seq(const char *map_type, const char *map_name,
810  int dict_flags)
811 {
812  DICT *dict;
813  const char *key;
814  const char *value;
815  int func;
816 
817  if (strcmp(map_type, DICT_TYPE_PROXY) == 0)
818  msg_fatal("can't sequence maps via the proxy service");
819  dict = dict_open3(map_type, map_name, O_RDONLY, dict_flags);
820  for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
821  if (dict_seq(dict, func, &key, &value) != 0)
822  break;
823  if (*key == 0) {
824  msg_warn("table %s:%s: empty lookup key value is not allowed",
825  map_type, map_name);
826  } else if (*value == 0) {
827  msg_warn("table %s:%s: key %s: empty string result is not allowed",
828  map_type, map_name, key);
829  msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
830  map_type, map_name);
831  }
832  vstream_printf("%s %s\n", key, value);
833  }
834  if (dict->error)
835  msg_fatal("table %s:%s: sequence error: %m", dict->type, dict->name);
837  dict_close(dict);
838 }
839 
840 /* usage - explain */
841 
842 static NORETURN usage(char *myname)
843 {
844  msg_fatal("usage: %s [-NfinoprsuUvw] [-c config_dir] [-d key] [-q key] [map_type:]file...",
845  myname);
846 }
847 
849 
850 int main(int argc, char **argv)
851 {
852  char *path_name;
853  int ch;
854  int fd;
855  char *slash;
856  struct stat st;
857  int postmap_flags = POSTMAP_FLAG_AS_OWNER | POSTMAP_FLAG_SAVE_PERM;
858  int open_flags = O_RDWR | O_CREAT | O_TRUNC;
859  int dict_flags = (DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_FIX
861  char *query = 0;
862  char *delkey = 0;
863  int sequence = 0;
864  int found;
865  int force_utf8 = 0;
866  ARGV *import_env;
867 
868  /*
869  * Fingerprint executables and core dumps.
870  */
872 
873  /*
874  * Be consistent with file permissions.
875  */
876  umask(022);
877 
878  /*
879  * To minimize confusion, make sure that the standard file descriptors
880  * are open before opening anything else. XXX Work around for 44BSD where
881  * fstat can return EBADF on an open file descriptor.
882  */
883  for (fd = 0; fd < 3; fd++)
884  if (fstat(fd, &st) == -1
885  && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
886  msg_fatal("open /dev/null: %m");
887 
888  /*
889  * Process environment options as early as we can. We are not set-uid,
890  * and we are supposed to be running in a controlled environment.
891  */
892  if (getenv(CONF_ENV_VERB))
893  msg_verbose = 1;
894 
895  /*
896  * Initialize. Set up logging, read the global configuration file and
897  * extract configuration information.
898  */
899  if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
900  argv[0] = slash + 1;
901  msg_vstream_init(argv[0], VSTREAM_ERR);
902  msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY);
903 
904  /*
905  * Check the Postfix library version as soon as we enable logging.
906  */
908 
909  /*
910  * Parse JCL.
911  */
912  while ((ch = GETOPT(argc, argv, "Nbc:d:fhimnopq:rsuUvw")) > 0) {
913  switch (ch) {
914  default:
915  usage(argv[0]);
916  break;
917  case 'N':
918  dict_flags |= DICT_FLAG_TRY1NULL;
919  dict_flags &= ~DICT_FLAG_TRY0NULL;
920  break;
921  case 'b':
922  postmap_flags |= POSTMAP_FLAG_BODY_KEY;
923  break;
924  case 'c':
925  if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
926  msg_fatal("out of memory");
927  break;
928  case 'd':
929  if (sequence || query || delkey)
930  msg_fatal("specify only one of -s -q or -d");
931  delkey = optarg;
932  break;
933  case 'f':
934  dict_flags &= ~DICT_FLAG_FOLD_FIX;
935  break;
936  case 'h':
937  postmap_flags |= POSTMAP_FLAG_HEADER_KEY;
938  break;
939  case 'i':
940  open_flags &= ~O_TRUNC;
941  break;
942  case 'm':
943  postmap_flags |= POSTMAP_FLAG_MIME_KEY;
944  break;
945  case 'n':
946  dict_flags |= DICT_FLAG_TRY0NULL;
947  dict_flags &= ~DICT_FLAG_TRY1NULL;
948  break;
949  case 'o':
950  postmap_flags &= ~POSTMAP_FLAG_AS_OWNER;
951  break;
952  case 'p':
953  postmap_flags &= ~POSTMAP_FLAG_SAVE_PERM;
954  break;
955  case 'q':
956  if (sequence || query || delkey)
957  msg_fatal("specify only one of -s -q or -d");
958  query = optarg;
959  break;
960  case 'r':
961  dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE);
962  dict_flags |= DICT_FLAG_DUP_REPLACE;
963  break;
964  case 's':
965  if (query || delkey)
966  msg_fatal("specify only one of -s or -q or -d");
967  sequence = 1;
968  break;
969  case 'u':
970  dict_flags &= ~DICT_FLAG_UTF8_REQUEST;
971  break;
972  case 'U':
973  force_utf8 = 1;
974  break;
975  case 'v':
976  msg_verbose++;
977  break;
978  case 'w':
979  dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_REPLACE);
980  dict_flags |= DICT_FLAG_DUP_IGNORE;
981  break;
982  }
983  }
984  mail_conf_read();
985  /* Enforce consistent operation of different Postfix parts. */
987  update_env(import_env->argv);
988  argv_free(import_env);
989  /* Re-evaluate mail_task() after reading main.cf. */
990  msg_syslog_init(mail_task(argv[0]), LOG_PID, LOG_FACILITY);
991  mail_dict_init();
992  if ((query == 0 || strcmp(query, "-") != 0)
993  && (postmap_flags & POSTMAP_FLAG_ANY_KEY))
994  msg_fatal("specify -b -h or -m only with \"-q -\"");
995  if ((postmap_flags & POSTMAP_FLAG_ANY_KEY) != 0
996  && (postmap_flags & POSTMAP_FLAG_ANY_KEY)
997  == (postmap_flags & POSTMAP_FLAG_MIME_KEY))
998  msg_warn("ignoring -m option without -b or -h");
999  if ((postmap_flags & (POSTMAP_FLAG_ANY_KEY & ~POSTMAP_FLAG_MIME_KEY))
1000  && force_utf8 == 0)
1001  dict_flags &= ~DICT_FLAG_UTF8_MASK;
1002 
1003  /*
1004  * Use the map type specified by the user, or fall back to a default
1005  * database type.
1006  */
1007  if (delkey) { /* remove entry */
1008  if (optind + 1 > argc)
1009  usage(argv[0]);
1010  if (strcmp(delkey, "-") == 0)
1011  exit(postmap_deletes(VSTREAM_IN, argv + optind, argc - optind,
1012  dict_flags | DICT_FLAG_LOCK) == 0);
1013  found = 0;
1014  while (optind < argc) {
1015  if ((path_name = split_at(argv[optind], ':')) != 0) {
1016  found |= postmap_delete(argv[optind], path_name, delkey,
1017  dict_flags | DICT_FLAG_LOCK);
1018  } else {
1019  found |= postmap_delete(var_db_type, argv[optind], delkey,
1020  dict_flags | DICT_FLAG_LOCK);
1021  }
1022  optind++;
1023  }
1024  exit(found ? 0 : 1);
1025  } else if (query) { /* query map(s) */
1026  if (optind + 1 > argc)
1027  usage(argv[0]);
1028  if (strcmp(query, "-") == 0)
1029  exit(postmap_queries(VSTREAM_IN, argv + optind, argc - optind,
1030  postmap_flags, dict_flags | DICT_FLAG_LOCK) == 0);
1031  while (optind < argc) {
1032  if ((path_name = split_at(argv[optind], ':')) != 0) {
1033  found = postmap_query(argv[optind], path_name, query,
1034  dict_flags | DICT_FLAG_LOCK);
1035  } else {
1036  found = postmap_query(var_db_type, argv[optind], query,
1037  dict_flags | DICT_FLAG_LOCK);
1038  }
1039  if (found)
1040  exit(0);
1041  optind++;
1042  }
1043  exit(1);
1044  } else if (sequence) {
1045  while (optind < argc) {
1046  if ((path_name = split_at(argv[optind], ':')) != 0) {
1047  postmap_seq(argv[optind], path_name,
1048  dict_flags | DICT_FLAG_LOCK);
1049  } else {
1050  postmap_seq(var_db_type, argv[optind],
1051  dict_flags | DICT_FLAG_LOCK);
1052  }
1053  exit(0);
1054  }
1055  exit(1);
1056  } else { /* create/update map(s) */
1057  if (optind + 1 > argc)
1058  usage(argv[0]);
1059  while (optind < argc) {
1060  if ((path_name = split_at(argv[optind], ':')) != 0) {
1061  postmap(argv[optind], path_name, postmap_flags,
1062  open_flags, dict_flags);
1063  } else {
1064  postmap(var_db_type, argv[optind], postmap_flags,
1065  open_flags, dict_flags);
1066  }
1067  optind++;
1068  }
1069  exit(0);
1070  }
1071 }
int msg_verbose
Definition: msg.c:177
#define DICT_FLAG_DUP_IGNORE
Definition: dict.h:111
#define VSTREAM_EOF
Definition: vstream.h:110
void myfree(void *ptr)
Definition: mymalloc.c:207
char ** maps
Definition: postmap.c:354
const char * mail_task(const char *argv0)
Definition: mail_task.c:49
#define DICT_TYPE_PROXY
Definition: dict_proxy.h:22
#define NOCLOBBER
Definition: sys_defs.h:1670
char * var_import_environ
Definition: mail_params.c:296
int vstring_get_nonl(VSTRING *vp, VSTREAM *fp)
#define POSTMAP_FLAG_MIME_KEY
Definition: postmap.c:342
MIME_STATE * mime_state_alloc(int flags, MIME_STATE_HEAD_OUT head_out, MIME_STATE_ANY_END head_end, MIME_STATE_BODY_OUT body_out, MIME_STATE_ANY_END body_end, MIME_STATE_ERR_PRINT err_print, void *context)
Definition: mime_state.c:493
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
#define DICT_SEQ_FUN_FIRST
Definition: dict.h:200
#define NORETURN
Definition: sys_defs.h:1583
Definition: argv.h:17
#define VAR_IMPORT_ENVIRON
Definition: mail_params.h:2506
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define VSTREAM_OUT
Definition: vstream.h:67
char * name
Definition: dict.h:80
#define stat(p, s)
Definition: warn_stat.h:18
#define LEN
Definition: postmap.c:336
#define DICT_SEQ_FUN_NEXT
Definition: dict.h:201
int flags
Definition: dict.h:81
#define POSTMAP_FLAG_ANY_KEY
Definition: postmap.c:346
int valid_utf8_string(const char *, ssize_t)
#define LOG_FACILITY
Definition: mail_params.h:357
char ** argv
Definition: argv.h:20
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
#define VSTREAM_IN
Definition: vstream.h:66
#define REC_TYPE_END
Definition: rec_type.h:77
#define POSTMAP_FLAG_HB_KEY
Definition: postmap.c:344
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
#define CONF_ENV_VERB
Definition: mail_conf.h:23
#define MIME_OPT_DISABLE_MIME
Definition: mime_state.h:47
#define STR
Definition: postmap.c:335
#define POSTMAP_FLAG_FULL_KEY
Definition: postmap.c:345
#define POSTMAP_FLAG_HEADER_KEY
Definition: postmap.c:340
void mail_conf_read(void)
Definition: mail_conf.c:178
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
DICT * dict_open3(const char *, const char *, int, int)
Definition: dict_open.c:439
Definition: dict.h:78
char * type
Definition: dict.h:79
void(* MIME_STATE_BODY_OUT)(void *, int, const char *, ssize_t, off_t)
Definition: mime_state.h:29
#define CONF_ENV_PATH
Definition: mail_conf.h:22
int mime_state_update(MIME_STATE *state, int rec_type, const char *text, ssize_t len)
Definition: mime_state.c:755
ARGV * mail_parm_split(const char *name, const char *value)
#define dict_get(dp, key)
Definition: dict.h:236
#define DICT_FLAG_DUP_REPLACE
Definition: dict.h:117
MIME_STATE * mime_state_free(MIME_STATE *state)
Definition: mime_state.c:530
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
void mkmap_close(MKMAP *)
Definition: mkmap_open.c:205
#define DICT_FLAG_TRY1NULL
Definition: dict.h:113
void(* MIME_STATE_ANY_END)(void *)
Definition: mime_state.h:30
#define dict_seq(dp, f, key, val)
Definition: dict.h:239
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
void(* MIME_STATE_HEAD_OUT)(void *, int, const HEADER_OPTS *, VSTRING *, off_t)
Definition: mime_state.h:28
#define DICT_FLAG_LOCK
Definition: dict.h:116
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
MKMAP * mkmap_open(const char *, const char *, int, int)
Definition: mkmap_open.c:237
void mail_dict_init(void)
Definition: mail_dict.c:81
#define MAIL_VERSION_STAMP_ALLOCATE
Definition: mail_version.h:67
int error
Definition: dict.h:94
const MIME_STATE_DETAIL * mime_state_detail(int error_code)
Definition: mime_state.c:1163
DICT ** dicts
Definition: postmap.c:353
#define allascii(s)
Definition: stringops.h:66
#define DICT_FLAG_DUP_WARN
Definition: dict.h:110
char * trimblanks(char *, ssize_t)
Definition: trimblanks.c:37
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
#define MAIL_VERSION_CHECK
Definition: mail_version.h:90
VSTRING * readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
Definition: readlline.c:82
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
void update_env(char **preserve_list)
Definition: clean_env.c:102
void set_eugid(uid_t euid, gid_t egid)
Definition: set_eugid.c:54
#define GETOPT(argc, argv, str)
Definition: sys_defs.h:1313
void msg_syslog_init(const char *name, int logopt, int facility)
Definition: msg_syslog.c:173
Definition: mkmap.h:25
#define dict_isjmp(dict)
Definition: dict.h:295
MAIL_VERSION_STAMP_DECLARE
Definition: postmap.c:848
#define dict_setjmp(dict)
Definition: dict.h:292
#define POSTMAP_FLAG_SAVE_PERM
Definition: postmap.c:339
void(* MIME_STATE_ERR_PRINT)(void *, int, const char *, ssize_t)
Definition: mime_state.h:31
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define vstream_fileno(vp)
Definition: vstream.h:115
#define mkmap_append(map, key, val)
Definition: mkmap.h:37
void msg_vstream_init(const char *name, VSTREAM *vp)
Definition: msg_vstream.c:77
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define REC_TYPE_NORM
Definition: rec_type.h:59
#define CA_VSTREAM_CTL_PATH(v)
Definition: vstream.h:158
#define ISSPACE(c)
Definition: sys_defs.h:1753
#define POSTMAP_FLAG_BODY_KEY
Definition: postmap.c:341
int main(int argc, char **argv)
Definition: postmap.c:850
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
#define dict_del(dp, key)
Definition: dict.h:238
struct DICT * dict
Definition: mkmap.h:27
char * var_db_type
Definition: mail_params.c:270
#define DICT_FLAG_BULK_UPDATE
Definition: dict.h:128
#define DICT_FLAG_TRY0NULL
Definition: dict.h:112
#define DICT_FLAG_UTF8_MASK
Definition: dict.h:133
#define POSTMAP_FLAG_AS_OWNER
Definition: postmap.c:338
#define VSTREAM_ERR
Definition: vstream.h:68
#define DICT_FLAG_UTF8_ACTIVE
Definition: dict.h:131
#define fstat(f, s)
Definition: warn_stat.h:20
#define dict_close(dp)
Definition: dict.h:240
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150