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