xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/mail_stream.c (revision 62f324d0121177eaf2e0384f92fd9ca2a751c795)
1 /*	$NetBSD: mail_stream.c,v 1.1.1.2 2013/01/02 18:58:58 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mail_stream 3
6 /* SUMMARY
7 /*	mail stream management
8 /* SYNOPSIS
9 /*	#include <mail_stream.h>
10 /*
11 /*	typedef struct {
12 /* .in +4
13 /*		VSTREAM	*stream;	/* read/write stream */
14 /*		char	*id;		/* queue ID */
15 /*		struct timeval ctime;	/* create time */
16 /*		private members...
17 /* .in -4
18 /*	} MAIL_STREAM;
19 /*
20 /*	MAIL_STREAM *mail_stream_file(queue, class, service, mode)
21 /*	const char *queue;
22 /*	const char *class;
23 /*	const char *service;
24 /*	int	mode;
25 /*
26 /*	MAIL_STREAM *mail_stream_service(class, service)
27 /*	const char *class;
28 /*	const char *service;
29 /*
30 /*	MAIL_STREAM *mail_stream_command(command)
31 /*	const char *command;
32 /*
33 /*	void	mail_stream_cleanup(info)
34 /*	MAIL_STREAM *info;
35 /*
36 /*	int	mail_stream_finish(info, why)
37 /*	MAIL_STREAM *info;
38 /*	VSTRING	*why;
39 /*
40 /*	void	mail_stream_ctl(info, op, ...)
41 /*	MAIL_STREAM *info;
42 /*	int	op;
43 /* DESCRIPTION
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.
50 /*
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.
56 /*
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.
62 /*
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.
67 /*
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.
71 /*
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.
77 /*
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
88 /*	error.
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.
101 /* LICENSE
102 /* .ad
103 /* .fi
104 /*	The Secure Mailer license must be distributed with this software.
105 /* AUTHOR(S)
106 /*	Wietse Venema
107 /*	IBM T.J. Watson Research
108 /*	P.O. Box 704
109 /*	Yorktown Heights, NY 10598, USA
110 /*--*/
111 
112 /* System library. */
113 
114 #include <sys_defs.h>
115 #include <sys/stat.h>
116 #include <unistd.h>
117 #include <errno.h>
118 #include <utime.h>
119 #include <string.h>
120 #include <stdarg.h>
121 
122 /* Utility library. */
123 
124 #include <msg.h>
125 #include <mymalloc.h>
126 #include <vstring.h>
127 #include <vstream.h>
128 #include <stringops.h>
129 #include <argv.h>
130 #include <sane_fsops.h>
131 #include <warn_stat.h>
132 
133 /* Global library. */
134 
135 #include <cleanup_user.h>
136 #include <mail_proto.h>
137 #include <mail_queue.h>
138 #include <opened.h>
139 #include <mail_params.h>
140 #include <mail_stream.h>
141 
142 /* Application-specific. */
143 
144 static VSTRING *id_buf;
145 
146 #define FREE_AND_WIPE(free, arg) do { if (arg) free(arg); arg = 0; } while (0)
147 
148 #define STR(x)	vstring_str(x)
149 
150 /* mail_stream_cleanup - clean up after success or failure */
151 
152 void    mail_stream_cleanup(MAIL_STREAM *info)
153 {
154     FREE_AND_WIPE(info->close, info->stream);
155     FREE_AND_WIPE(myfree, info->queue);
156     FREE_AND_WIPE(myfree, info->id);
157     FREE_AND_WIPE(myfree, info->class);
158     FREE_AND_WIPE(myfree, info->service);
159     myfree((char *) info);
160 }
161 
162 #if defined(HAS_FUTIMES_AT)
163 #define CAN_STAMP_BY_STREAM
164 
165 /* stamp_stream - update open file [am]time stamp */
166 
167 static int stamp_stream(VSTREAM *fp, time_t when)
168 {
169     struct timeval tv[2];
170 
171     if (when != 0) {
172 	tv[0].tv_sec = tv[1].tv_sec = when;
173 	tv[0].tv_usec = tv[1].tv_usec = 0;
174 	return (futimesat(vstream_fileno(fp), (char *) 0, tv));
175     } else {
176 	return (futimesat(vstream_fileno(fp), (char *) 0, (struct timeval *) 0));
177     }
178 }
179 
180 #elif defined(HAS_FUTIMES)
181 #define CAN_STAMP_BY_STREAM
182 
183 /* stamp_stream - update open file [am]time stamp */
184 
185 static int stamp_stream(VSTREAM *fp, time_t when)
186 {
187     struct timeval tv[2];
188 
189     if (when != 0) {
190 	tv[0].tv_sec = tv[1].tv_sec = when;
191 	tv[0].tv_usec = tv[1].tv_usec = 0;
192 	return (futimes(vstream_fileno(fp), tv));
193     } else {
194 	return (futimes(vstream_fileno(fp), (struct timeval *) 0));
195     }
196 }
197 
198 #endif
199 
200 /* stamp_path - update file [am]time stamp by pathname */
201 
202 static int stamp_path(const char *path, time_t when)
203 {
204     struct utimbuf tbuf;
205 
206     if (when != 0) {
207 	tbuf.actime = tbuf.modtime = when;
208 	return (utime(path, &tbuf));
209     } else {
210 	return (utime(path, (struct utimbuf *) 0));
211     }
212 }
213 
214 /* mail_stream_finish_file - finish file mail stream */
215 
216 static int mail_stream_finish_file(MAIL_STREAM *info, VSTRING *unused_why)
217 {
218     int     status = CLEANUP_STAT_OK;
219     static char wakeup[] = {TRIGGER_REQ_WAKEUP};
220     struct stat st;
221     char   *path_to_reset = 0;
222     static int incoming_fs_clock_ok = 0;
223     static int incoming_clock_warned = 0;
224     int     check_incoming_fs_clock;
225     int     err;
226     time_t  want_stamp;
227     time_t  expect_stamp;
228 
229     /*
230      * Make sure the message makes it to file. Set the execute bit when no
231      * write error was detected. Some people believe that this code has a
232      * problem if the system crashes before fsync() returns; fchmod() could
233      * take effect before all the data blocks are written. Wietse claims that
234      * this is not a problem. Postfix rejects incomplete queue files, even
235      * when the +x attribute is set. Every Postfix queue file record has a
236      * type code and a length field. Files with missing records are rejected,
237      * as are files with unknown record type codes. Every Postfix queue file
238      * must end with an explicit END record. Postfix queue files without END
239      * record are discarded.
240      *
241      * Attempt to detect file system clocks that are ahead of local time, but
242      * don't check the file system clock all the time. The effect of file
243      * system clock drift can be difficult to understand (Postfix ignores new
244      * mail until the local clock catches up with the file mtime stamp).
245      *
246      * This clock drift detection code may not work with file systems that work
247      * on a local copy of the file and that update the server only after the
248      * file is closed.
249      *
250      * Optionally set a cooldown time.
251      *
252      * XXX: We assume that utime() does control the file modification time even
253      * when followed by an fchmod(), fsync(), close() sequence. This may fail
254      * with remote file systems when fsync() actually updates the file. Even
255      * then, we still delay the average message by 1/2 of the
256      * queue_run_delay.
257      *
258      * XXX: Victor does not like running utime() after the close(), since this
259      * creates a race even with local filesystems. But Wietse is not
260      * confident that utime() before fsync() and close() will work reliably
261      * with remote file systems.
262      *
263      * XXX Don't run the clock skew tests with Postfix sendmail submissions.
264      * Don't whine against unsuspecting users or applications.
265      */
266     check_incoming_fs_clock =
267 	(!incoming_fs_clock_ok && !strcmp(info->queue, MAIL_QUEUE_INCOMING));
268 
269 #ifdef DELAY_ACTION
270     if (strcmp(info->queue, MAIL_QUEUE_DEFERRED) != 0)
271 	info->delay = 0;
272     if (info->delay > 0)
273 	want_stamp = time((time_t *) 0) + info->delay;
274     else
275 #endif
276 	want_stamp = 0;
277 
278     /*
279      * If we can cheaply set the file time stamp (no pathname lookup) do it
280      * anyway, so that we can avoid whining later about file server/client
281      * clock skew.
282      *
283      * Otherwise, if we must set the file time stamp for delayed delivery, use
284      * whatever means we have to get the job done, no matter if it is
285      * expensive.
286      *
287      * XXX Unfortunately, Linux futimes() is not usable because it uses /proc.
288      * This may not be available because of chroot, or because of access
289      * restrictions after a process changes privileges.
290      */
291     if (vstream_fflush(info->stream)
292 #ifdef CAN_STAMP_BY_STREAM
293 	|| stamp_stream(info->stream, want_stamp)
294 #else
295 	|| (want_stamp && stamp_path(VSTREAM_PATH(info->stream), want_stamp))
296 #endif
297 	|| fchmod(vstream_fileno(info->stream), 0700 | info->mode)
298 #ifdef HAS_FSYNC
299 	|| fsync(vstream_fileno(info->stream))
300 #endif
301 	|| (check_incoming_fs_clock
302 	    && fstat(vstream_fileno(info->stream), &st) < 0)
303 	)
304 	status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
305 #ifdef TEST
306     st.st_mtime += 10;
307 #endif
308 
309     /*
310      * Work around file system clock skew. If the file system clock is ahead
311      * of the local clock, Postfix won't deliver mail immediately, which is
312      * bad for performance. If the file system clock falls behind the local
313      * clock, it just looks silly in mail headers.
314      */
315     if (status == CLEANUP_STAT_OK && check_incoming_fs_clock) {
316 	/* Do NOT use time() result from before fsync(). */
317 	expect_stamp = want_stamp ? want_stamp : time((time_t *) 0);
318 	if (st.st_mtime > expect_stamp) {
319 	    path_to_reset = mystrdup(VSTREAM_PATH(info->stream));
320 	    if (incoming_clock_warned == 0) {
321 		msg_warn("file system clock is %d seconds ahead of local clock",
322 			 (int) (st.st_mtime - expect_stamp));
323 		msg_warn("resetting file time stamps - this hurts performance");
324 		incoming_clock_warned = 1;
325 	    }
326 	} else {
327 	    if (st.st_mtime < expect_stamp - 100)
328 		msg_warn("file system clock is %d seconds behind local clock",
329 			 (int) (expect_stamp - st.st_mtime));
330 	    incoming_fs_clock_ok = 1;
331 	}
332     }
333 
334     /*
335      * Close the queue file and mark it as closed. Be prepared for
336      * vstream_fclose() to fail even after vstream_fflush() and fsync()
337      * reported no error. Reason: after a file is closed, some networked file
338      * systems copy the file out to another machine. Running the queue on a
339      * remote file system is not recommended, if only for performance
340      * reasons.
341      */
342     err = info->close(info->stream);
343     info->stream = 0;
344     if (status == CLEANUP_STAT_OK && err != 0)
345 	status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
346 
347     /*
348      * Work around file system clocks that are ahead of local time.
349      */
350     if (path_to_reset != 0) {
351 	if (status == CLEANUP_STAT_OK) {
352 	    if (stamp_path(path_to_reset, expect_stamp) < 0 && errno != ENOENT)
353 		msg_fatal("%s: update file time stamps: %m", info->id);
354 	}
355 	myfree(path_to_reset);
356     }
357 
358     /*
359      * When all is well, notify the next service that a new message has been
360      * queued.
361      */
362     if (status == CLEANUP_STAT_OK && info->class && info->service)
363 	mail_trigger(info->class, info->service, wakeup, sizeof(wakeup));
364 
365     /*
366      * Cleanup.
367      */
368     mail_stream_cleanup(info);
369     return (status);
370 }
371 
372 /* mail_stream_finish_ipc - finish IPC mail stream */
373 
374 static int mail_stream_finish_ipc(MAIL_STREAM *info, VSTRING *why)
375 {
376     int     status = CLEANUP_STAT_WRITE;
377 
378     /*
379      * Receive the peer's completion status.
380      */
381     if ((why && attr_scan(info->stream, ATTR_FLAG_STRICT,
382 			  ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
383 			  ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
384 			  ATTR_TYPE_END) != 2)
385 	|| (!why && attr_scan(info->stream, ATTR_FLAG_MISSING,
386 			      ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
387 			      ATTR_TYPE_END) != 1))
388 	status = CLEANUP_STAT_WRITE;
389 
390     /*
391      * Cleanup.
392      */
393     mail_stream_cleanup(info);
394     return (status);
395 }
396 
397 /* mail_stream_finish - finish action */
398 
399 int     mail_stream_finish(MAIL_STREAM *info, VSTRING *why)
400 {
401     return (info->finish(info, why));
402 }
403 
404 /* mail_stream_file - destination is file */
405 
406 MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
407 			              const char *service, int mode)
408 {
409     struct timeval tv;
410     MAIL_STREAM *info;
411     VSTREAM *stream;
412 
413     stream = mail_queue_enter(queue, 0600 | mode, &tv);
414     if (msg_verbose)
415 	msg_info("open %s", VSTREAM_PATH(stream));
416 
417     info = (MAIL_STREAM *) mymalloc(sizeof(*info));
418     info->stream = stream;
419     info->finish = mail_stream_finish_file;
420     info->close = vstream_fclose;
421     info->queue = mystrdup(queue);
422     info->id = mystrdup(basename(VSTREAM_PATH(stream)));
423     info->class = mystrdup(class);
424     info->service = mystrdup(service);
425     info->mode = mode;
426 #ifdef DELAY_ACTION
427     info->delay = 0;
428 #endif
429     info->ctime = tv;
430     return (info);
431 }
432 
433 /* mail_stream_service - destination is service */
434 
435 MAIL_STREAM *mail_stream_service(const char *class, const char *name)
436 {
437     VSTREAM *stream;
438     MAIL_STREAM *info;
439 
440     if (id_buf == 0)
441 	id_buf = vstring_alloc(10);
442 
443     stream = mail_connect_wait(class, name);
444     if (attr_scan(stream, ATTR_FLAG_MISSING,
445 		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
446 	vstream_fclose(stream);
447 	return (0);
448     } else {
449 	info = (MAIL_STREAM *) mymalloc(sizeof(*info));
450 	info->stream = stream;
451 	info->finish = mail_stream_finish_ipc;
452 	info->close = vstream_fclose;
453 	info->queue = 0;
454 	info->id = mystrdup(vstring_str(id_buf));
455 	info->class = 0;
456 	info->service = 0;
457 	return (info);
458     }
459 }
460 
461 /* mail_stream_command - destination is command */
462 
463 MAIL_STREAM *mail_stream_command(const char *command)
464 {
465     VSTREAM *stream;
466     MAIL_STREAM *info;
467     ARGV   *export_env;
468     int     status;
469 
470     if (id_buf == 0)
471 	id_buf = vstring_alloc(10);
472 
473     /*
474      * Treat fork() failure as a transient problem. Treat bad handshake as a
475      * permanent error.
476      *
477      * XXX Are we invoking a Postfix process or a non-Postfix process? In the
478      * former case we can share the full environment; in the latter case only
479      * a restricted environment should be propagated. Even though we are
480      * talking a Postfix-internal protocol there is no way we can tell what
481      * is being executed except by duplicating a lot of existing code.
482      */
483     export_env = argv_split(var_export_environ, ", \t\r\n");
484     while ((stream = vstream_popen(O_RDWR,
485 				   VSTREAM_POPEN_COMMAND, command,
486 				   VSTREAM_POPEN_EXPORT, export_env->argv,
487 				   VSTREAM_POPEN_END)) == 0) {
488 	msg_warn("fork: %m");
489 	sleep(10);
490     }
491     argv_free(export_env);
492     vstream_control(stream,
493 		    VSTREAM_CTL_PATH, command,
494 		    VSTREAM_CTL_END);
495 
496     if (attr_scan(stream, ATTR_FLAG_MISSING,
497 		  ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id_buf, 0) != 1) {
498 	if ((status = vstream_pclose(stream)) != 0)
499 	    msg_warn("command \"%s\" exited with status %d", command, status);
500 	return (0);
501     } else {
502 	info = (MAIL_STREAM *) mymalloc(sizeof(*info));
503 	info->stream = stream;
504 	info->finish = mail_stream_finish_ipc;
505 	info->close = vstream_pclose;
506 	info->queue = 0;
507 	info->id = mystrdup(vstring_str(id_buf));
508 	info->class = 0;
509 	info->service = 0;
510 	return (info);
511     }
512 }
513 
514 /* mail_stream_ctl - update file-based mail stream properties */
515 
516 void    mail_stream_ctl(MAIL_STREAM *info, int op,...)
517 {
518     const char *myname = "mail_stream_ctl";
519     va_list ap;
520     char   *new_queue = 0;
521     char   *string_value;
522 
523     /*
524      * Sanity check. None of the attributes below are applicable unless the
525      * target is a file-based stream.
526      */
527     if (info->finish != mail_stream_finish_file)
528 	msg_panic("%s: attempt to update non-file stream %s",
529 		  myname, info->id);
530 
531     for (va_start(ap, op); op != MAIL_STREAM_CTL_END; op = va_arg(ap, int)) {
532 
533 	switch (op) {
534 
535 	    /*
536 	     * Change the queue directory. We do this at the end of this
537 	     * call.
538 	     */
539 	case MAIL_STREAM_CTL_QUEUE:
540 	    if ((new_queue = va_arg(ap, char *)) == 0)
541 		msg_panic("%s: NULL queue",
542 			  myname);
543 	    break;
544 
545 	    /*
546 	     * Change the service that needs to be notified.
547 	     */
548 	case MAIL_STREAM_CTL_CLASS:
549 	    FREE_AND_WIPE(myfree, info->class);
550 	    if ((string_value = va_arg(ap, char *)) != 0)
551 		info->class = mystrdup(string_value);
552 	    break;
553 
554 	case MAIL_STREAM_CTL_SERVICE:
555 	    FREE_AND_WIPE(myfree, info->service);
556 	    if ((string_value = va_arg(ap, char *)) != 0)
557 		info->service = mystrdup(string_value);
558 	    break;
559 
560 	    /*
561 	     * Change the (finished) file access mode.
562 	     */
563 	case MAIL_STREAM_CTL_MODE:
564 	    info->mode = va_arg(ap, int);
565 	    break;
566 
567 	    /*
568 	     * Advance the (finished) file modification time.
569 	     */
570 #ifdef DELAY_ACTION
571 	case MAIL_STREAM_CTL_DELAY:
572 	    if ((info->delay = va_arg(ap, int)) < 0)
573 		msg_panic("%s: bad delay time %d", myname, info->delay);
574 	    break;
575 #endif
576 
577 	default:
578 	    msg_panic("%s: bad op code %d", myname, op);
579 	}
580     }
581     va_end(ap);
582 
583     /*
584      * Rename the queue file after allocating memory for new information, so
585      * that the caller can still remove an embryonic file when memory
586      * allocation fails (there is no risk of deleting the wrong file).
587      *
588      * Wietse opposed the idea to update run-time error handler information
589      * here, because this module wasn't designed to defend against internal
590      * concurrency issues with error handlers that attempt to follow dangling
591      * pointers.
592      *
593      * This code duplicates mail_queue_rename(), except that we need the new
594      * path to update the stream pathname.
595      */
596     if (new_queue != 0 && strcmp(info->queue, new_queue) != 0) {
597 	char   *saved_queue = info->queue;
598 	char   *saved_path = mystrdup(VSTREAM_PATH(info->stream));
599 	VSTRING *new_path = vstring_alloc(100);
600 
601 	(void) mail_queue_path(new_path, new_queue, info->id);
602 	info->queue = mystrdup(new_queue);
603 	vstream_control(info->stream, VSTREAM_CTL_PATH, STR(new_path),
604 			VSTREAM_CTL_END);
605 
606 	if (sane_rename(saved_path, STR(new_path)) == 0
607 	    || (mail_queue_mkdirs(STR(new_path)) == 0
608 		&& sane_rename(saved_path, STR(new_path)) == 0)) {
609 	    if (msg_verbose)
610 		msg_info("%s: placed in %s queue", info->id, info->queue);
611 	} else {
612 	    msg_fatal("%s: move to %s queue failed: %m", info->id,
613 		      info->queue);
614 	}
615 
616 	myfree(saved_path);
617 	myfree(saved_queue);
618 	vstring_free(new_path);
619     }
620 }
621