Postfix3.3.1
mail_queue.c
[詳解]
1 /*++
2 /* NAME
3 /* mail_queue 3
4 /* SUMMARY
5 /* mail queue file access
6 /* SYNOPSIS
7 /* #include <mail_queue.h>
8 /*
9 /* VSTREAM *mail_queue_enter(queue_name, mode, tp)
10 /* const char *queue_name;
11 /* mode_t mode;
12 /* struct timeval *tp;
13 /*
14 /* VSTREAM *mail_queue_open(queue_name, queue_id, flags, mode)
15 /* const char *queue_name;
16 /* const char *queue_id;
17 /* int flags;
18 /* mode_t mode;
19 /*
20 /* char *mail_queue_dir(buf, queue_name, queue_id)
21 /* VSTRING *buf;
22 /* const char *queue_name;
23 /* const char *queue_id;
24 /*
25 /* char *mail_queue_path(buf, queue_name, queue_id)
26 /* VSTRING *buf;
27 /* const char *queue_name;
28 /* const char *queue_id;
29 /*
30 /* int mail_queue_mkdirs(path)
31 /* const char *path;
32 /*
33 /* int mail_queue_rename(queue_id, old_queue, new_queue)
34 /* const char *queue_id;
35 /* const char *old_queue;
36 /* const char *new_queue;
37 /*
38 /* int mail_queue_remove(queue_name, queue_id)
39 /* const char *queue_name;
40 /* const char *queue_id;
41 /*
42 /* int mail_queue_name_ok(queue_name)
43 /* const char *queue_name;
44 /*
45 /* int mail_queue_id_ok(queue_id)
46 /* const char *queue_id;
47 /* DESCRIPTION
48 /* This module encapsulates access to the mail queue hierarchy.
49 /* Unlike most other modules, this one does not abort the program
50 /* in case of file access problems. But it does abort when the
51 /* application attempts to use a malformed queue name or queue id.
52 /*
53 /* mail_queue_enter() creates an entry in the named queue. The queue
54 /* id is the file base name, see VSTREAM_PATH(). Queue ids are
55 /* relatively short strings and are recycled in the course of time.
56 /* The only guarantee given is that on a given machine, no two queue
57 /* entries will have the same queue ID at the same time. The tp
58 /* argument, if not a null pointer, receives the time stamp that
59 /* corresponds with the queue ID.
60 /*
61 /* mail_queue_open() opens the named queue file. The \fIflags\fR
62 /* and \fImode\fR arguments are as with open(2). The result is a
63 /* null pointer in case of problems.
64 /*
65 /* mail_queue_dir() returns the directory name of the specified queue
66 /* file. When a null result buffer pointer is provided, the result is
67 /* written to a private buffer that may be overwritten upon the next
68 /* call.
69 /*
70 /* mail_queue_path() returns the pathname of the specified queue
71 /* file. When a null result buffer pointer is provided, the result
72 /* is written to a private buffer that may be overwritten upon the
73 /* next call.
74 /*
75 /* mail_queue_mkdirs() creates missing parent directories
76 /* for the file named in \fBpath\fR. A non-zero result means
77 /* that the operation failed.
78 /*
79 /* mail_queue_rename() renames a queue file. A non-zero result
80 /* means the operation failed.
81 /*
82 /* mail_queue_remove() removes the named queue file. A non-zero result
83 /* means the operation failed.
84 /*
85 /* mail_queue_name_ok() validates a mail queue name and returns
86 /* non-zero (true) if the name contains no nasty characters.
87 /*
88 /* mail_queue_id_ok() does the same thing for mail queue ID names.
89 /* DIAGNOSTICS
90 /* Panic: invalid queue name or id given to mail_queue_path(),
91 /* mail_queue_rename(), or mail_queue_remove().
92 /* Fatal error: out of memory.
93 /* LICENSE
94 /* .ad
95 /* .fi
96 /* The Secure Mailer license must be distributed with this software.
97 /* AUTHOR(S)
98 /* Wietse Venema
99 /* IBM T.J. Watson Research
100 /* P.O. Box 704
101 /* Yorktown Heights, NY 10598, USA
102 /*--*/
103 
104 /* System library. */
105 
106 #include <sys_defs.h>
107 #include <stdio.h> /* rename() */
108 #include <stdlib.h>
109 #include <ctype.h>
110 #include <stdlib.h>
111 #include <unistd.h>
112 #include <fcntl.h>
113 #include <sys/time.h> /* gettimeofday, not in POSIX */
114 #include <string.h>
115 #include <errno.h>
116 
117 #ifdef STRCASECMP_IN_STRINGS_H
118 #include <strings.h>
119 #endif
120 
121 /* Utility library. */
122 
123 #include <msg.h>
124 #include <vstring.h>
125 #include <vstream.h>
126 #include <mymalloc.h>
127 #include <argv.h>
128 #include <dir_forest.h>
129 #include <make_dirs.h>
130 #include <split_at.h>
131 #include <sane_fsops.h>
132 #include <valid_hostname.h>
133 
134 /* Global library. */
135 
136 #include "file_id.h"
137 #include "mail_params.h"
138 #define MAIL_QUEUE_INTERNAL
139 #include "mail_queue.h"
140 
141 #define STR vstring_str
142 
143 /* mail_queue_dir - construct mail queue directory name */
144 
145 const char *mail_queue_dir(VSTRING *buf, const char *queue_name,
146  const char *queue_id)
147 {
148  const char *myname = "mail_queue_dir";
149  static VSTRING *private_buf = 0;
150  static VSTRING *hash_buf = 0;
151  static ARGV *hash_queue_names = 0;
152  static VSTRING *usec_buf = 0;
153  const char *delim;
154  char **cpp;
155 
156  /*
157  * Sanity checks.
158  */
159  if (mail_queue_name_ok(queue_name) == 0)
160  msg_panic("%s: bad queue name: %s", myname, queue_name);
161  if (mail_queue_id_ok(queue_id) == 0)
162  msg_panic("%s: bad queue id: %s", myname, queue_id);
163 
164  /*
165  * Initialize.
166  */
167  if (buf == 0) {
168  if (private_buf == 0)
169  private_buf = vstring_alloc(100);
170  buf = private_buf;
171  }
172  if (hash_buf == 0) {
173  hash_buf = vstring_alloc(100);
174  hash_queue_names = argv_split(var_hash_queue_names, CHARS_COMMA_SP);
175  }
176 
177  /*
178  * First, put the basic queue directory name into place.
179  */
180  vstring_strcpy(buf, queue_name);
181  vstring_strcat(buf, "/");
182 
183  /*
184  * Then, see if we need to append a little directory forest.
185  */
186  for (cpp = hash_queue_names->argv; *cpp; cpp++) {
187  if (strcasecmp(*cpp, queue_name) == 0) {
188  if (MQID_FIND_LG_INUM_SEPARATOR(delim, queue_id)) {
189  if (usec_buf == 0)
190  usec_buf = vstring_alloc(20);
191  MQID_LG_GET_HEX_USEC(usec_buf, delim);
192  queue_id = STR(usec_buf);
193  }
194  vstring_strcat(buf,
195  dir_forest(hash_buf, queue_id, var_hash_queue_depth));
196  break;
197  }
198  }
199  return (STR(buf));
200 }
201 
202 /* mail_queue_path - map mail queue id to path name */
203 
204 const char *mail_queue_path(VSTRING *buf, const char *queue_name,
205  const char *queue_id)
206 {
207  static VSTRING *private_buf = 0;
208 
209  /*
210  * Initialize.
211  */
212  if (buf == 0) {
213  if (private_buf == 0)
214  private_buf = vstring_alloc(100);
215  buf = private_buf;
216  }
217 
218  /*
219  * Append the queue id to the possibly hashed queue directory.
220  */
221  (void) mail_queue_dir(buf, queue_name, queue_id);
222  vstring_strcat(buf, queue_id);
223  return (STR(buf));
224 }
225 
226 /* mail_queue_mkdirs - fill in missing directories */
227 
228 int mail_queue_mkdirs(const char *path)
229 {
230  const char *myname = "mail_queue_mkdirs";
231  char *saved_path = mystrdup(path);
232  int ret;
233 
234  /*
235  * Truncate a copy of the pathname (for safety sake), and create the
236  * missing directories.
237  */
238  if (split_at_right(saved_path, '/') == 0)
239  msg_panic("%s: no slash in: %s", myname, saved_path);
240  ret = make_dirs(saved_path, 0700);
241  myfree(saved_path);
242  return (ret);
243 }
244 
245 /* mail_queue_rename - move message to another queue */
246 
247 int mail_queue_rename(const char *queue_id, const char *old_queue,
248  const char *new_queue)
249 {
250  VSTRING *old_buf = vstring_alloc(100);
251  VSTRING *new_buf = vstring_alloc(100);
252  int error;
253 
254  /*
255  * Try the operation. If it fails, see if it is because of missing
256  * intermediate directories.
257  */
258  error = sane_rename(mail_queue_path(old_buf, old_queue, queue_id),
259  mail_queue_path(new_buf, new_queue, queue_id));
260  if (error != 0 && mail_queue_mkdirs(STR(new_buf)) == 0)
261  error = sane_rename(STR(old_buf), STR(new_buf));
262 
263  /*
264  * Cleanup.
265  */
266  vstring_free(old_buf);
267  vstring_free(new_buf);
268 
269  return (error);
270 }
271 
272 /* mail_queue_remove - remove mail queue file */
273 
274 int mail_queue_remove(const char *queue_name, const char *queue_id)
275 {
276  return (REMOVE(mail_queue_path((VSTRING *) 0, queue_name, queue_id)));
277 }
278 
279 /* mail_queue_name_ok - validate mail queue name */
280 
281 int mail_queue_name_ok(const char *queue_name)
282 {
283  const char *cp;
284 
285  if (*queue_name == 0 || strlen(queue_name) > 100)
286  return (0);
287 
288  for (cp = queue_name; *cp; cp++)
289  if (!ISALNUM(*cp))
290  return (0);
291  return (1);
292 }
293 
294 /* mail_queue_id_ok - validate mail queue id */
295 
296 int mail_queue_id_ok(const char *queue_id)
297 {
298  const char *cp;
299 
300  /*
301  * A file name is either a queue ID (short alphanumeric string in
302  * time+inum form) or a fast flush service logfile name (destination
303  * domain name with non-alphanumeric characters replaced by "_").
304  */
305  if (*queue_id == 0 || strlen(queue_id) > VALID_HOSTNAME_LEN)
306  return (0);
307 
308  /*
309  * OK if in time+inum form or in host_domain_tld form.
310  */
311  for (cp = queue_id; *cp; cp++)
312  if (!ISALNUM(*cp) && *cp != '_')
313  return (0);
314  return (1);
315 }
316 
317 /* mail_queue_enter - make mail queue entry with locally-unique name */
318 
319 VSTREAM *mail_queue_enter(const char *queue_name, mode_t mode,
320  struct timeval * tp)
321 {
322  const char *myname = "mail_queue_enter";
323  static VSTRING *sec_buf;
324  static VSTRING *usec_buf;
325  static VSTRING *id_buf;
326  static int pid;
327  static VSTRING *path_buf;
328  static VSTRING *temp_path;
329  struct timeval tv;
330  int fd;
331  const char *file_id;
332  VSTREAM *stream;
333  int count;
334 
335  /*
336  * Initialize.
337  */
338  if (id_buf == 0) {
339  pid = getpid();
340  sec_buf = vstring_alloc(10);
341  usec_buf = vstring_alloc(10);
342  id_buf = vstring_alloc(10);
343  path_buf = vstring_alloc(10);
344  temp_path = vstring_alloc(100);
345  }
346  if (tp == 0)
347  tp = &tv;
348 
349  /*
350  * Create a file with a temporary name that does not collide. The process
351  * ID alone is not sufficiently unique: maildrops can be shared via the
352  * network. Not that I recommend using a network-based queue, or having
353  * multiple hosts write to the same queue, but we should try to avoid
354  * losing mail if we can.
355  *
356  * If someone is racing against us, try to win.
357  */
358  for (;;) {
359  GETTIMEOFDAY(tp);
360  vstring_sprintf(temp_path, "%s/%d.%d", queue_name,
361  (int) tp->tv_usec, pid);
362  if ((fd = open(STR(temp_path), O_RDWR | O_CREAT | O_EXCL, mode)) >= 0)
363  break;
364  if (errno == EEXIST || errno == EISDIR)
365  continue;
366  msg_warn("%s: create file %s: %m", myname, STR(temp_path));
367  sleep(10);
368  }
369 
370  /*
371  * Rename the file to something that is derived from the file ID. I saw
372  * this idea first being used in Zmailer. On any reasonable file system
373  * the file ID is guaranteed to be unique. Better let the OS resolve
374  * collisions than doing a worse job in an application. Another
375  * attractive property of file IDs is that they can appear in messages
376  * without leaking a significant amount of system information (unlike
377  * process ids). Not so nice is that files need to be renamed when they
378  * are moved to another file system.
379  *
380  * If someone is racing against us, try to win.
381  */
382  file_id = get_file_id_fd(fd, var_long_queue_ids);
383 
384  /*
385  * XXX Some systems seem to have clocks that correlate with process
386  * scheduling or something. Unfortunately, we cannot add random
387  * quantities to the time, because the non-inode part of a queue ID must
388  * not repeat within the same second. The queue ID is the sole thing that
389  * prevents multiple messages from getting the same Message-ID value.
390  */
391  for (count = 0;; count++) {
392  GETTIMEOFDAY(tp);
393  if (var_long_queue_ids) {
394  vstring_sprintf(id_buf, "%s%s%c%s",
395  MQID_LG_ENCODE_SEC(sec_buf, tp->tv_sec),
396  MQID_LG_ENCODE_USEC(usec_buf, tp->tv_usec),
397  MQID_LG_INUM_SEP, file_id);
398  } else {
399  vstring_sprintf(id_buf, "%s%s",
400  MQID_SH_ENCODE_USEC(usec_buf, tp->tv_usec),
401  file_id);
402  }
403  mail_queue_path(path_buf, queue_name, STR(id_buf));
404  if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
405  break;
406  if (errno == EPERM || errno == EISDIR) /* collision. weird. */
407  continue;
408  if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) {
409  msg_warn("%s: rename %s to %s: %m", myname,
410  STR(temp_path), STR(path_buf));
411  }
412  if (count > 1000) /* XXX whatever */
413  msg_fatal("%s: rename %s to %s: giving up", myname,
414  STR(temp_path), STR(path_buf));
415  }
416 
417  stream = vstream_fdopen(fd, O_RDWR);
419  return (stream);
420 }
421 
422 /* mail_queue_open - open mail queue file */
423 
424 VSTREAM *mail_queue_open(const char *queue_name, const char *queue_id,
425  int flags, mode_t mode)
426 {
427  const char *path = mail_queue_path((VSTRING *) 0, queue_name, queue_id);
428  VSTREAM *fp;
429 
430  /*
431  * Try the operation. If file creation fails, see if it is because of a
432  * missing subdirectory.
433  */
434  if ((fp = vstream_fopen(path, flags, mode)) == 0)
435  if (errno == ENOENT)
436  if ((flags & O_CREAT) == O_CREAT && mail_queue_mkdirs(path) == 0)
437  fp = vstream_fopen(path, flags, mode);
438  return (fp);
439 }
const char * mail_queue_dir(VSTRING *buf, const char *queue_name, const char *queue_id)
Definition: mail_queue.c:145
void myfree(void *ptr)
Definition: mymalloc.c:207
char * mystrdup(const char *str)
Definition: mymalloc.c:225
int mail_queue_id_ok(const char *queue_id)
Definition: mail_queue.c:296
#define VALID_HOSTNAME_LEN
Definition: argv.h:17
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
int REMOVE(const char *path)
Definition: remove.c:52
int mail_queue_mkdirs(const char *path)
Definition: mail_queue.c:228
char ** argv
Definition: argv.h:20
int mail_queue_name_ok(const char *queue_name)
Definition: mail_queue.c:281
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
#define ISALNUM(c)
Definition: sys_defs.h:1745
const char * mail_queue_path(VSTRING *buf, const char *queue_name, const char *queue_id)
Definition: mail_queue.c:204
bool var_long_queue_ids
Definition: mail_params.c:339
char * dir_forest(VSTRING *buf, const char *path, int depth)
Definition: dir_forest.c:62
char * var_hash_queue_names
Definition: mail_params.c:271
#define STR
Definition: mail_queue.c:141
int var_hash_queue_depth
Definition: mail_params.c:272
VSTREAM * mail_queue_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: mail_queue.c:424
int mail_queue_rename(const char *queue_id, const char *old_queue, const char *new_queue)
Definition: mail_queue.c:247
VSTREAM * mail_queue_enter(const char *queue_name, mode_t mode, struct timeval *tp)
Definition: mail_queue.c:319
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define CHARS_COMMA_SP
Definition: sys_defs.h:1761
int mail_queue_remove(const char *queue_name, const char *queue_id)
Definition: mail_queue.c:274
ARGV * argv_split(const char *, const char *)
Definition: argv_split.c:63
const char * get_file_id_fd(int fd, int long_flag)
Definition: file_id.c:80
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
int make_dirs(const char *path, int perms)
Definition: make_dirs.c:52
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define CA_VSTREAM_CTL_PATH(v)
Definition: vstream.h:158
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
char * split_at_right(char *string, int delimiter)
Definition: split_at.c:64
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
Definition: vstring.c:459
VSTREAM * vstream_fdopen(int fd, int flags)
Definition: vstream.c:1204
int WARN_UNUSED_RESULT sane_rename(const char *, const char *)
Definition: sane_rename.c:41