7 /* mail stream management
9 /* #include <mail_stream.h>
13 /* VSTREAM *stream; /* read/write stream */
14 /* char *id; /* queue ID */
15 /* struct timeval ctime; /* create time */
20 /* MAIL_STREAM *mail_stream_file(queue, class, service, mode)
23 /* const char *service;
26 /* MAIL_STREAM *mail_stream_service(class, service)
28 /* const char *service;
30 /* MAIL_STREAM *mail_stream_command(command)
31 /* const char *command;
33 /* void mail_stream_cleanup(info)
36 /* int mail_stream_finish(info, why)
40 /* void mail_stream_ctl(info, op, ...)
44 /* This module provides a generic interface to Postfix queue file
45 /* format messages to file, to Postfix server, or to external command.
46 /* The routines that open a stream return a handle with an initialized
47 /* stream and queue id member. The handle is either given to a cleanup
48 /* routine, to dispose of a failed request, or to a finish routine, to
49 /* complete the request.
51 /* mail_stream_file() opens a mail stream to a newly-created file and
52 /* arranges for trigger delivery at finish time. This call never fails.
53 /* But it may take forever. The mode argument specifies additional
54 /* file permissions that will be OR-ed in when the file is finished.
55 /* While embryonic files have mode 0600, finished files have mode 0700.
57 /* mail_stream_command() opens a mail stream to external command,
58 /* and receives queue ID information from the command. The result
59 /* is a null pointer when the initial handshake fails. The command
60 /* is given to the shell only when necessary. At finish time, the
61 /* command is expected to send a completion status.
63 /* mail_stream_service() opens a mail stream to Postfix service,
64 /* and receives queue ID information from the command. The result
65 /* is a null pointer when the initial handshake fails. At finish
66 /* time, the daemon is expected to send a completion status.
68 /* mail_stream_cleanup() cancels the operation that was started with
69 /* any of the mail_stream_xxx() routines, and destroys the argument.
70 /* It is up to the caller to remove incomplete file objects.
72 /* mail_stream_finish() completes the operation that was started with
73 /* any of the mail_stream_xxx() routines, and destroys the argument.
74 /* The result is any of the status codes defined in <cleanup_user.h>.
75 /* It is up to the caller to remove incomplete file objects.
76 /* The why argument can be a null pointer.
78 /* mail_stream_ctl() selectively overrides information that
79 /* was specified with mail_stream_file(); none of the attributes
80 /* are applicable for other mail stream types. The arguments
81 /* are a list of (operation, value) pairs, terminated with
82 /* MAIL_STREAM_CTL_END. The following lists the operation
83 /* codes and the types of the corresponding value arguments.
84 /* .IP "MAIL_STREAM_CTL_QUEUE (char *)"
85 /* The argument specifies an alternate destination queue. The
86 /* queue file is moved to the specified queue before the call
87 /* returns. Failure to rename the queue file results in a fatal
89 /* .IP "MAIL_STREAM_CTL_CLASS (char *)"
90 /* The argument specifies an alternate trigger class.
91 /* .IP "MAIL_STREAM_CTL_SERVICE (char *)"
92 /* The argument specifies an alternate trigger service.
93 /* .IP "MAIL_STREAM_CTL_MODE (int)"
94 /* The argument specifies alternate permissions that override
95 /* the permissions specified with mail_stream_file().
96 /* .IP "MAIL_STREAM_CTL_DELAY (int)"
97 /* Attempt to postpone initial delivery by advancing the queue
98 /* file modification time stamp by this amount. This has
99 /* effect only within the deferred mail queue.
100 /* This feature may have no effect with remote file systems.
104 /* The Secure Mailer license must be distributed with this software.
107 /* IBM T.J. Watson Research
109 /* Yorktown Heights, NY 10598, USA
112 /* System library. */
114 #include <sys_defs.h>
115 #include <sys/stat.h>
122 /* Utility library. */
125 #include <mymalloc.h>
128 #include <stringops.h>
130 #include <sane_fsops.h>
132 /* Global library. */
134 #include <cleanup_user.h>
135 #include <mail_proto.h>
136 #include <mail_queue.h>
138 #include <mail_params.h>
139 #include <mail_stream.h>
141 /* Application-specific. */
143 static VSTRING
*id_buf
;
145 #define FREE_AND_WIPE(free, arg) do { if (arg) free(arg); arg = 0; } while (0)
147 #define STR(x) vstring_str(x)
149 /* mail_stream_cleanup - clean up after success or failure */
151 void mail_stream_cleanup(MAIL_STREAM
*info
)
153 FREE_AND_WIPE(info
->close
, info
->stream
);
154 FREE_AND_WIPE(myfree
, info
->queue
);
155 FREE_AND_WIPE(myfree
, info
->id
);
156 FREE_AND_WIPE(myfree
, info
->class);
157 FREE_AND_WIPE(myfree
, info
->service
);
158 myfree((char *) info
);
161 #if defined(HAS_FUTIMES_AT)
162 #define CAN_STAMP_BY_STREAM
164 /* stamp_stream - update open file [am]time stamp */
166 static int stamp_stream(VSTREAM
*fp
, time_t when
)
168 struct timeval tv
[2];
171 tv
[0].tv_sec
= tv
[1].tv_sec
= when
;
172 tv
[0].tv_usec
= tv
[1].tv_usec
= 0;
173 return (futimesat(vstream_fileno(fp
), (char *) 0, tv
));
175 return (futimesat(vstream_fileno(fp
), (char *) 0, (struct timeval
*) 0));
179 #elif defined(HAS_FUTIMES)
180 #define CAN_STAMP_BY_STREAM
182 /* stamp_stream - update open file [am]time stamp */
184 static int stamp_stream(VSTREAM
*fp
, time_t when
)
186 struct timeval tv
[2];
189 tv
[0].tv_sec
= tv
[1].tv_sec
= when
;
190 tv
[0].tv_usec
= tv
[1].tv_usec
= 0;
191 return (futimes(vstream_fileno(fp
), tv
));
193 return (futimes(vstream_fileno(fp
), (struct timeval
*) 0));
199 /* stamp_path - update file [am]time stamp by pathname */
201 static int stamp_path(const char *path
, time_t when
)
206 tbuf
.actime
= tbuf
.modtime
= when
;
207 return (utime(path
, &tbuf
));
209 return (utime(path
, (struct utimbuf
*) 0));
213 /* mail_stream_finish_file - finish file mail stream */
215 static int mail_stream_finish_file(MAIL_STREAM
*info
, VSTRING
*unused_why
)
217 int status
= CLEANUP_STAT_OK
;
218 static char wakeup
[] = {TRIGGER_REQ_WAKEUP
};
220 char *path_to_reset
= 0;
221 static int incoming_fs_clock_ok
= 0;
222 static int incoming_clock_warned
= 0;
223 int check_incoming_fs_clock
;
229 * Make sure the message makes it to file. Set the execute bit when no
230 * write error was detected. Some people believe that this code has a
231 * problem if the system crashes before fsync() returns; fchmod() could
232 * take effect before all the data blocks are written. Wietse claims that
233 * this is not a problem. Postfix rejects incomplete queue files, even
234 * when the +x attribute is set. Every Postfix queue file record has a
235 * type code and a length field. Files with missing records are rejected,
236 * as are files with unknown record type codes. Every Postfix queue file
237 * must end with an explicit END record. Postfix queue files without END
238 * record are discarded.
240 * Attempt to detect file system clocks that are ahead of local time, but
241 * don't check the file system clock all the time. The effect of file
242 * system clock drift can be difficult to understand (Postfix ignores new
243 * mail until the local clock catches up with the file mtime stamp).
245 * This clock drift detection code may not work with file systems that work
246 * on a local copy of the file and that update the server only after the
249 * Optionally set a cooldown time.
251 * XXX: We assume that utime() does control the file modification time even
252 * when followed by an fchmod(), fsync(), close() sequence. This may fail
253 * with remote file systems when fsync() actually updates the file. Even
254 * then, we still delay the average message by 1/2 of the
257 * XXX: Victor does not like running utime() after the close(), since this
258 * creates a race even with local filesystems. But Wietse is not
259 * confident that utime() before fsync() and close() will work reliably
260 * with remote file systems.
262 * XXX Don't run the clock skew tests with Postfix sendmail submissions.
263 * Don't whine against unsuspecting users or applications.
265 check_incoming_fs_clock
=
266 (!incoming_fs_clock_ok
&& !strcmp(info
->queue
, MAIL_QUEUE_INCOMING
));
269 if (strcmp(info
->queue
, MAIL_QUEUE_DEFERRED
) != 0)
272 want_stamp
= time((time_t *) 0) + info
->delay
;
278 * If we can cheaply set the file time stamp (no pathname lookup) do it
279 * anyway, so that we can avoid whining later about file server/client
282 * Otherwise, if we must set the file time stamp for delayed delivery, use
283 * whatever means we have to get the job done, no matter if it is
286 * XXX Unfortunately, Linux futimes() is not usable because it uses /proc.
287 * This may not be available because of chroot, or because of access
288 * restrictions after a process changes privileges.
290 if (vstream_fflush(info
->stream
)
291 #ifdef CAN_STAMP_BY_STREAM
292 || stamp_stream(info
->stream
, want_stamp
)
294 || (want_stamp
&& stamp_path(VSTREAM_PATH(info
->stream
), want_stamp
))
296 || fchmod(vstream_fileno(info
->stream
), 0700 | info
->mode
)
298 || fsync(vstream_fileno(info
->stream
))
300 || (check_incoming_fs_clock
301 && fstat(vstream_fileno(info
->stream
), &st
) < 0)
303 status
= (errno
== EFBIG
? CLEANUP_STAT_SIZE
: CLEANUP_STAT_WRITE
);
309 * Work around file system clock skew. If the file system clock is ahead
310 * of the local clock, Postfix won't deliver mail immediately, which is
311 * bad for performance. If the file system clock falls behind the local
312 * clock, it just looks silly in mail headers.
314 if (status
== CLEANUP_STAT_OK
&& check_incoming_fs_clock
) {
315 /* Do NOT use time() result from before fsync(). */
316 expect_stamp
= want_stamp
? want_stamp
: time((time_t *) 0);
317 if (st
.st_mtime
> expect_stamp
) {
318 path_to_reset
= mystrdup(VSTREAM_PATH(info
->stream
));
319 if (incoming_clock_warned
== 0) {
320 msg_warn("file system clock is %d seconds ahead of local clock",
321 (int) (st
.st_mtime
- expect_stamp
));
322 msg_warn("resetting file time stamps - this hurts performance");
323 incoming_clock_warned
= 1;
326 if (st
.st_mtime
< expect_stamp
- 100)
327 msg_warn("file system clock is %d seconds behind local clock",
328 (int) (expect_stamp
- st
.st_mtime
));
329 incoming_fs_clock_ok
= 1;
334 * Close the queue file and mark it as closed. Be prepared for
335 * vstream_fclose() to fail even after vstream_fflush() and fsync()
336 * reported no error. Reason: after a file is closed, some networked file
337 * systems copy the file out to another machine. Running the queue on a
338 * remote file system is not recommended, if only for performance
341 err
= info
->close(info
->stream
);
343 if (status
== CLEANUP_STAT_OK
&& err
!= 0)
344 status
= (errno
== EFBIG
? CLEANUP_STAT_SIZE
: CLEANUP_STAT_WRITE
);
347 * Work around file system clocks that are ahead of local time.
349 if (path_to_reset
!= 0) {
350 if (status
== CLEANUP_STAT_OK
) {
351 if (stamp_path(path_to_reset
, expect_stamp
) < 0 && errno
!= ENOENT
)
352 msg_fatal("%s: update file time stamps: %m", info
->id
);
354 myfree(path_to_reset
);
358 * When all is well, notify the next service that a new message has been
361 if (status
== CLEANUP_STAT_OK
&& info
->class && info
->service
)
362 mail_trigger(info
->class, info
->service
, wakeup
, sizeof(wakeup
));
367 mail_stream_cleanup(info
);
371 /* mail_stream_finish_ipc - finish IPC mail stream */
373 static int mail_stream_finish_ipc(MAIL_STREAM
*info
, VSTRING
*why
)
375 int status
= CLEANUP_STAT_WRITE
;
378 * Receive the peer's completion status.
380 if ((why
&& attr_scan(info
->stream
, ATTR_FLAG_STRICT
,
381 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, &status
,
382 ATTR_TYPE_STR
, MAIL_ATTR_WHY
, why
,
384 || (!why
&& attr_scan(info
->stream
, ATTR_FLAG_MISSING
,
385 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, &status
,
386 ATTR_TYPE_END
) != 1))
387 status
= CLEANUP_STAT_WRITE
;
392 mail_stream_cleanup(info
);
396 /* mail_stream_finish - finish action */
398 int mail_stream_finish(MAIL_STREAM
*info
, VSTRING
*why
)
400 return (info
->finish(info
, why
));
403 /* mail_stream_file - destination is file */
405 MAIL_STREAM
*mail_stream_file(const char *queue
, const char *class,
406 const char *service
, int mode
)
412 stream
= mail_queue_enter(queue
, 0600 | mode
, &tv
);
414 msg_info("open %s", VSTREAM_PATH(stream
));
416 info
= (MAIL_STREAM
*) mymalloc(sizeof(*info
));
417 info
->stream
= stream
;
418 info
->finish
= mail_stream_finish_file
;
419 info
->close
= vstream_fclose
;
420 info
->queue
= mystrdup(queue
);
421 info
->id
= mystrdup(basename(VSTREAM_PATH(stream
)));
422 info
->class = mystrdup(class);
423 info
->service
= mystrdup(service
);
432 /* mail_stream_service - destination is service */
434 MAIL_STREAM
*mail_stream_service(const char *class, const char *name
)
440 id_buf
= vstring_alloc(10);
442 stream
= mail_connect_wait(class, name
);
443 if (attr_scan(stream
, ATTR_FLAG_MISSING
,
444 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, id_buf
, 0) != 1) {
445 vstream_fclose(stream
);
448 info
= (MAIL_STREAM
*) mymalloc(sizeof(*info
));
449 info
->stream
= stream
;
450 info
->finish
= mail_stream_finish_ipc
;
451 info
->close
= vstream_fclose
;
453 info
->id
= mystrdup(vstring_str(id_buf
));
460 /* mail_stream_command - destination is command */
462 MAIL_STREAM
*mail_stream_command(const char *command
)
470 id_buf
= vstring_alloc(10);
473 * Treat fork() failure as a transient problem. Treat bad handshake as a
476 * XXX Are we invoking a Postfix process or a non-Postfix process? In the
477 * former case we can share the full environment; in the latter case only
478 * a restricted environment should be propagated. Even though we are
479 * talking a Postfix-internal protocol there is no way we can tell what
480 * is being executed except by duplicating a lot of existing code.
482 export_env
= argv_split(var_export_environ
, ", \t\r\n");
483 while ((stream
= vstream_popen(O_RDWR
,
484 VSTREAM_POPEN_COMMAND
, command
,
485 VSTREAM_POPEN_EXPORT
, export_env
->argv
,
486 VSTREAM_POPEN_END
)) == 0) {
487 msg_warn("fork: %m");
490 argv_free(export_env
);
491 vstream_control(stream
,
492 VSTREAM_CTL_PATH
, command
,
495 if (attr_scan(stream
, ATTR_FLAG_MISSING
,
496 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, id_buf
, 0) != 1) {
497 if ((status
= vstream_pclose(stream
)) != 0)
498 msg_warn("command \"%s\" exited with status %d", command
, status
);
501 info
= (MAIL_STREAM
*) mymalloc(sizeof(*info
));
502 info
->stream
= stream
;
503 info
->finish
= mail_stream_finish_ipc
;
504 info
->close
= vstream_pclose
;
506 info
->id
= mystrdup(vstring_str(id_buf
));
513 /* mail_stream_ctl - update file-based mail stream properties */
515 void mail_stream_ctl(MAIL_STREAM
*info
, int op
,...)
517 const char *myname
= "mail_stream_ctl";
523 * Sanity check. None of the attributes below are applicable unless the
524 * target is a file-based stream.
526 if (info
->finish
!= mail_stream_finish_file
)
527 msg_panic("%s: attempt to update non-file stream %s",
530 for (va_start(ap
, op
); op
!= MAIL_STREAM_CTL_END
; op
= va_arg(ap
, int)) {
535 * Change the queue directory. We do this at the end of this
538 case MAIL_STREAM_CTL_QUEUE
:
539 if ((new_queue
= va_arg(ap
, char *)) == 0)
540 msg_panic("%s: NULL queue",
545 * Change the service that needs to be notified.
547 case MAIL_STREAM_CTL_CLASS
:
548 FREE_AND_WIPE(myfree
, info
->class);
549 if ((string_value
= va_arg(ap
, char *)) != 0)
550 info
->class = mystrdup(string_value
);
553 case MAIL_STREAM_CTL_SERVICE
:
554 FREE_AND_WIPE(myfree
, info
->service
);
555 if ((string_value
= va_arg(ap
, char *)) != 0)
556 info
->service
= mystrdup(string_value
);
560 * Change the (finished) file access mode.
562 case MAIL_STREAM_CTL_MODE
:
563 info
->mode
= va_arg(ap
, int);
567 * Advance the (finished) file modification time.
570 case MAIL_STREAM_CTL_DELAY
:
571 if ((info
->delay
= va_arg(ap
, int)) < 0)
572 msg_panic("%s: bad delay time %d", myname
, info
->delay
);
577 msg_panic("%s: bad op code %d", myname
, op
);
583 * Rename the queue file after allocating memory for new information, so
584 * that the caller can still remove an embryonic file when memory
585 * allocation fails (there is no risk of deleting the wrong file).
587 * Wietse opposed the idea to update run-time error handler information
588 * here, because this module wasn't designed to defend against internal
589 * concurrency issues with error handlers that attempt to follow dangling
592 * This code duplicates mail_queue_rename(), except that we need the new
593 * path to update the stream pathname.
595 if (new_queue
!= 0 && strcmp(info
->queue
, new_queue
) != 0) {
596 char *saved_queue
= info
->queue
;
597 char *saved_path
= mystrdup(VSTREAM_PATH(info
->stream
));
598 VSTRING
*new_path
= vstring_alloc(100);
600 (void) mail_queue_path(new_path
, new_queue
, info
->id
);
601 info
->queue
= mystrdup(new_queue
);
602 vstream_control(info
->stream
, VSTREAM_CTL_PATH
, STR(new_path
),
605 if (sane_rename(saved_path
, STR(new_path
)) == 0
606 || (mail_queue_mkdirs(STR(new_path
)) == 0
607 && sane_rename(saved_path
, STR(new_path
)) == 0)) {
609 msg_info("%s: placed in %s queue", info
->id
, info
->queue
);
611 msg_fatal("%s: move to %s queue failed: %m", info
->id
,
617 vstring_free(new_path
);