1 /* $NetBSD: mail_stream.c,v 1.1.1.1 2009/06/23 10:08:47 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 132 /* Global library. */ 133 134 #include <cleanup_user.h> 135 #include <mail_proto.h> 136 #include <mail_queue.h> 137 #include <opened.h> 138 #include <mail_params.h> 139 #include <mail_stream.h> 140 141 /* Application-specific. */ 142 143 static VSTRING *id_buf; 144 145 #define FREE_AND_WIPE(free, arg) do { if (arg) free(arg); arg = 0; } while (0) 146 147 #define STR(x) vstring_str(x) 148 149 /* mail_stream_cleanup - clean up after success or failure */ 150 151 void mail_stream_cleanup(MAIL_STREAM *info) 152 { 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); 159 } 160 161 #if defined(HAS_FUTIMES_AT) 162 #define CAN_STAMP_BY_STREAM 163 164 /* stamp_stream - update open file [am]time stamp */ 165 166 static int stamp_stream(VSTREAM *fp, time_t when) 167 { 168 struct timeval tv[2]; 169 170 if (when != 0) { 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)); 174 } else { 175 return (futimesat(vstream_fileno(fp), (char *) 0, (struct timeval *) 0)); 176 } 177 } 178 179 #elif defined(HAS_FUTIMES) 180 #define CAN_STAMP_BY_STREAM 181 182 /* stamp_stream - update open file [am]time stamp */ 183 184 static int stamp_stream(VSTREAM *fp, time_t when) 185 { 186 struct timeval tv[2]; 187 188 if (when != 0) { 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)); 192 } else { 193 return (futimes(vstream_fileno(fp), (struct timeval *) 0)); 194 } 195 } 196 197 #endif 198 199 /* stamp_path - update file [am]time stamp by pathname */ 200 201 static int stamp_path(const char *path, time_t when) 202 { 203 struct utimbuf tbuf; 204 205 if (when != 0) { 206 tbuf.actime = tbuf.modtime = when; 207 return (utime(path, &tbuf)); 208 } else { 209 return (utime(path, (struct utimbuf *) 0)); 210 } 211 } 212 213 /* mail_stream_finish_file - finish file mail stream */ 214 215 static int mail_stream_finish_file(MAIL_STREAM *info, VSTRING *unused_why) 216 { 217 int status = CLEANUP_STAT_OK; 218 static char wakeup[] = {TRIGGER_REQ_WAKEUP}; 219 struct stat st; 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; 224 int err; 225 time_t want_stamp; 226 time_t expect_stamp; 227 228 /* 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. 239 * 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). 244 * 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 247 * file is closed. 248 * 249 * Optionally set a cooldown time. 250 * 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 255 * queue_run_delay. 256 * 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. 261 * 262 * XXX Don't run the clock skew tests with Postfix sendmail submissions. 263 * Don't whine against unsuspecting users or applications. 264 */ 265 check_incoming_fs_clock = 266 (!incoming_fs_clock_ok && !strcmp(info->queue, MAIL_QUEUE_INCOMING)); 267 268 #ifdef DELAY_ACTION 269 if (strcmp(info->queue, MAIL_QUEUE_DEFERRED) != 0) 270 info->delay = 0; 271 if (info->delay > 0) 272 want_stamp = time((time_t *) 0) + info->delay; 273 else 274 #endif 275 want_stamp = 0; 276 277 /* 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 280 * clock skew. 281 * 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 284 * expensive. 285 * 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. 289 */ 290 if (vstream_fflush(info->stream) 291 #ifdef CAN_STAMP_BY_STREAM 292 || stamp_stream(info->stream, want_stamp) 293 #else 294 || (want_stamp && stamp_path(VSTREAM_PATH(info->stream), want_stamp)) 295 #endif 296 || fchmod(vstream_fileno(info->stream), 0700 | info->mode) 297 #ifdef HAS_FSYNC 298 || fsync(vstream_fileno(info->stream)) 299 #endif 300 || (check_incoming_fs_clock 301 && fstat(vstream_fileno(info->stream), &st) < 0) 302 ) 303 status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE); 304 #ifdef TEST 305 st.st_mtime += 10; 306 #endif 307 308 /* 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. 313 */ 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; 324 } 325 } else { 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; 330 } 331 } 332 333 /* 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 339 * reasons. 340 */ 341 err = info->close(info->stream); 342 info->stream = 0; 343 if (status == CLEANUP_STAT_OK && err != 0) 344 status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE); 345 346 /* 347 * Work around file system clocks that are ahead of local time. 348 */ 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); 353 } 354 myfree(path_to_reset); 355 } 356 357 /* 358 * When all is well, notify the next service that a new message has been 359 * queued. 360 */ 361 if (status == CLEANUP_STAT_OK && info->class && info->service) 362 mail_trigger(info->class, info->service, wakeup, sizeof(wakeup)); 363 364 /* 365 * Cleanup. 366 */ 367 mail_stream_cleanup(info); 368 return (status); 369 } 370 371 /* mail_stream_finish_ipc - finish IPC mail stream */ 372 373 static int mail_stream_finish_ipc(MAIL_STREAM *info, VSTRING *why) 374 { 375 int status = CLEANUP_STAT_WRITE; 376 377 /* 378 * Receive the peer's completion status. 379 */ 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, 383 ATTR_TYPE_END) != 2) 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; 388 389 /* 390 * Cleanup. 391 */ 392 mail_stream_cleanup(info); 393 return (status); 394 } 395 396 /* mail_stream_finish - finish action */ 397 398 int mail_stream_finish(MAIL_STREAM *info, VSTRING *why) 399 { 400 return (info->finish(info, why)); 401 } 402 403 /* mail_stream_file - destination is file */ 404 405 MAIL_STREAM *mail_stream_file(const char *queue, const char *class, 406 const char *service, int mode) 407 { 408 struct timeval tv; 409 MAIL_STREAM *info; 410 VSTREAM *stream; 411 412 stream = mail_queue_enter(queue, 0600 | mode, &tv); 413 if (msg_verbose) 414 msg_info("open %s", VSTREAM_PATH(stream)); 415 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); 424 info->mode = mode; 425 #ifdef DELAY_ACTION 426 info->delay = 0; 427 #endif 428 info->ctime = tv; 429 return (info); 430 } 431 432 /* mail_stream_service - destination is service */ 433 434 MAIL_STREAM *mail_stream_service(const char *class, const char *name) 435 { 436 VSTREAM *stream; 437 MAIL_STREAM *info; 438 439 if (id_buf == 0) 440 id_buf = vstring_alloc(10); 441 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); 446 return (0); 447 } else { 448 info = (MAIL_STREAM *) mymalloc(sizeof(*info)); 449 info->stream = stream; 450 info->finish = mail_stream_finish_ipc; 451 info->close = vstream_fclose; 452 info->queue = 0; 453 info->id = mystrdup(vstring_str(id_buf)); 454 info->class = 0; 455 info->service = 0; 456 return (info); 457 } 458 } 459 460 /* mail_stream_command - destination is command */ 461 462 MAIL_STREAM *mail_stream_command(const char *command) 463 { 464 VSTREAM *stream; 465 MAIL_STREAM *info; 466 ARGV *export_env; 467 int status; 468 469 if (id_buf == 0) 470 id_buf = vstring_alloc(10); 471 472 /* 473 * Treat fork() failure as a transient problem. Treat bad handshake as a 474 * permanent error. 475 * 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. 481 */ 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"); 488 sleep(10); 489 } 490 argv_free(export_env); 491 vstream_control(stream, 492 VSTREAM_CTL_PATH, command, 493 VSTREAM_CTL_END); 494 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); 499 return (0); 500 } else { 501 info = (MAIL_STREAM *) mymalloc(sizeof(*info)); 502 info->stream = stream; 503 info->finish = mail_stream_finish_ipc; 504 info->close = vstream_pclose; 505 info->queue = 0; 506 info->id = mystrdup(vstring_str(id_buf)); 507 info->class = 0; 508 info->service = 0; 509 return (info); 510 } 511 } 512 513 /* mail_stream_ctl - update file-based mail stream properties */ 514 515 void mail_stream_ctl(MAIL_STREAM *info, int op,...) 516 { 517 const char *myname = "mail_stream_ctl"; 518 va_list ap; 519 char *new_queue = 0; 520 char *string_value; 521 522 /* 523 * Sanity check. None of the attributes below are applicable unless the 524 * target is a file-based stream. 525 */ 526 if (info->finish != mail_stream_finish_file) 527 msg_panic("%s: attempt to update non-file stream %s", 528 myname, info->id); 529 530 for (va_start(ap, op); op != MAIL_STREAM_CTL_END; op = va_arg(ap, int)) { 531 532 switch (op) { 533 534 /* 535 * Change the queue directory. We do this at the end of this 536 * call. 537 */ 538 case MAIL_STREAM_CTL_QUEUE: 539 if ((new_queue = va_arg(ap, char *)) == 0) 540 msg_panic("%s: NULL queue", 541 myname); 542 break; 543 544 /* 545 * Change the service that needs to be notified. 546 */ 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); 551 break; 552 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); 557 break; 558 559 /* 560 * Change the (finished) file access mode. 561 */ 562 case MAIL_STREAM_CTL_MODE: 563 info->mode = va_arg(ap, int); 564 break; 565 566 /* 567 * Advance the (finished) file modification time. 568 */ 569 #ifdef DELAY_ACTION 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); 573 break; 574 #endif 575 576 default: 577 msg_panic("%s: bad op code %d", myname, op); 578 } 579 } 580 va_end(ap); 581 582 /* 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). 586 * 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 590 * pointers. 591 * 592 * This code duplicates mail_queue_rename(), except that we need the new 593 * path to update the stream pathname. 594 */ 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); 599 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), 603 VSTREAM_CTL_END); 604 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)) { 608 if (msg_verbose) 609 msg_info("%s: placed in %s queue", info->id, info->queue); 610 } else { 611 msg_fatal("%s: move to %s queue failed: %m", info->id, 612 info->queue); 613 } 614 615 myfree(saved_path); 616 myfree(saved_queue); 617 vstring_free(new_path); 618 } 619 } 620