Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postfix/src/global/   (Postfix Mailserver Version 3.11©)  Datei vom 25.9.2020 mit Größe 19 kB image not shown  

Quelle  mail_stream.c

  Sprache: C
 

/*++
/* NAME
/* mail_stream 3
/* SUMMARY
/* mail stream management
/* SYNOPSIS
/* #include <mail_stream.h>
/*
/* typedef struct {
/* .in +4
/* VSTREAM *stream; /* read/write stream */

/* char *id; /* queue ID */
/* struct timeval ctime; /* create time */
/* private members...
/* .in -4
/* } MAIL_STREAM;
/*
/* MAIL_STREAM *mail_stream_file(queue, class, service, mode)
/* const char *queue;
/* const char *class;
/* const char *service;
/* int mode;
/*
/* MAIL_STREAM *mail_stream_service(class, service)
/* const char *class;
/* const char *service;
/*
/* MAIL_STREAM *mail_stream_command(command)
/* const char *command;
/*
/* void mail_stream_cleanup(info)
/* MAIL_STREAM *info;
/*
/* int mail_stream_finish(info, why)
/* MAIL_STREAM *info;
/* VSTRING *why;
/*
/* void mail_stream_ctl(info, op, ...)
/* MAIL_STREAM *info;
/* int op;
/* DESCRIPTION
/* This module provides a generic interface to Postfix queue file
/* format messages to file, to Postfix server, or to external command.
/* The routines that open a stream return a handle with an initialized
/* stream and queue id member. The handle is either given to a cleanup
/* routine, to dispose of a failed request, or to a finish routine, to
/* complete the request.
/*
/* mail_stream_file() opens a mail stream to a newly-created file and
/* arranges for trigger delivery at finish time. This call never fails.
/* But it may take forever. The mode argument specifies additional
/* file permissions that will be OR-ed in when the file is finished.
/* While embryonic files have mode 0600, finished files have mode 0700.
/*
/* mail_stream_command() opens a mail stream to external command,
/* and receives queue ID information from the command. The result
/* is a null pointer when the initial handshake fails. The command
/* is given to the shell only when necessary. At finish time, the
/* command is expected to send a completion status.
/*
/* mail_stream_service() opens a mail stream to Postfix service,
/* and receives queue ID information from the command. The result
/* is a null pointer when the initial handshake fails. At finish
/* time, the daemon is expected to send a completion status.
/*
/* mail_stream_cleanup() cancels the operation that was started with
/* any of the mail_stream_xxx() routines, and destroys the argument.
/* It is up to the caller to remove incomplete file objects.
/*
/* mail_stream_finish() completes the operation that was started with
/* any of the mail_stream_xxx() routines, and destroys the argument.
/* The result is any of the status codes defined in <cleanup_user.h>.
/* It is up to the caller to remove incomplete file objects.
/* The why argument can be a null pointer.
/*
/* mail_stream_ctl() selectively overrides information that
/* was specified with mail_stream_file(); none of the attributes
/* are applicable for other mail stream types.  The arguments
/* are a list macros with arguments, terminated with
/* CA_MAIL_STREAM_CTL_END which has none.  The following lists
/* the macros and the types of the corresponding arguments.
/* .IP "CA_MAIL_STREAM_CTL_QUEUE(const char *)"
/* The argument specifies an alternate destination queue. The
/* queue file is moved to the specified queue before the call
/* returns. Failure to rename the queue file results in a fatal
/* error.
/* .IP "CA_MAIL_STREAM_CTL_CLASS(const char *)"
/* The argument specifies an alternate trigger class.
/* .IP "CA_MAIL_STREAM_CTL_SERVICE(const char *)"
/* The argument specifies an alternate trigger service.
/* .IP "CA_MAIL_STREAM_CTL_MODE(int)"
/* The argument specifies alternate permissions that override
/* the permissions specified with mail_stream_file().
/* .IP "CA_MAIL_STREAM_CTL_DELAY(int)"
/* Attempt to postpone initial delivery by advancing the queue
/* file modification time stamp by this amount.  This has
/* effect only within the deferred mail queue.
/* This feature may have no effect with remote file systems.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/


/* System library. */

#include <sys_defs.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <utime.h>
#include <string.h>
#include <stdarg.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <stringops.h>
#include <argv.h>
#include <sane_fsops.h>
#include <warn_stat.h>

/* Global library. */

#include <cleanup_user.h>
#include <mail_proto.h>
#include <mail_queue.h>
#include <opened.h>
#include <mail_params.h>
#include <mail_stream.h>
#include <mail_parm_split.h>

/* Application-specific. */

static VSTRING *id_buf;

#define FREE_AND_WIPE(free, arg) do { if (arg) free(arg); arg = 0; } while (0)

#define STR(x) vstring_str(x)

/* mail_stream_cleanup - clean up after success or failure */

void    mail_stream_cleanup(MAIL_STREAM *info)
{
    if (info->stream && info->close(info->stream))
 msg_warn("mail_stream_cleanup: close error");
    FREE_AND_WIPE(myfree, info->queue);
    FREE_AND_WIPE(myfree, info->id);
    FREE_AND_WIPE(myfree, info->class);
    FREE_AND_WIPE(myfree, info->service);
    myfree((void *) info);
}

#if defined(HAS_FUTIMES_AT)
#define CAN_STAMP_BY_STREAM

/* stamp_stream - update open file [am]time stamp */

static int stamp_stream(VSTREAM *fp, time_t when)
{
    struct timeval tv[2];

    if (when != 0) {
 tv[0].tv_sec = tv[1].tv_sec = when;
 tv[0].tv_usec = tv[1].tv_usec = 0;
 return (futimesat(vstream_fileno(fp), (char *) 0, tv));
    } else {
 return (futimesat(vstream_fileno(fp), (char *) 0, (struct timeval *) 0));
    }
}

#elif defined(HAS_FUTIMES)
#define CAN_STAMP_BY_STREAM

/* stamp_stream - update open file [am]time stamp */

static int stamp_stream(VSTREAM *fp, time_t when)
{
    struct timeval tv[2];

    if (when != 0) {
 tv[0].tv_sec = tv[1].tv_sec = when;
 tv[0].tv_usec = tv[1].tv_usec = 0;
 return (futimes(vstream_fileno(fp), tv));
    } else {
 return (futimes(vstream_fileno(fp), (struct timeval *) 0));
    }
}

#endif

/* stamp_path - update file [am]time stamp by pathname */

static int stamp_path(const char *path, time_t when)
{
    struct utimbuf tbuf;

    if (when != 0) {
 tbuf.actime = tbuf.modtime = when;
 return (utime(path, &tbuf));
    } else {
 return (utime(path, (struct utimbuf *) 0));
    }
}

/* mail_stream_finish_file - finish file mail stream */

static int mail_stream_finish_file(MAIL_STREAM *info, VSTRING *unused_why)
{
    int     status = CLEANUP_STAT_OK;
    static char wakeup[] = {TRIGGER_REQ_WAKEUP};
    struct stat st;
    char   *path_to_reset = 0;
    static int incoming_fs_clock_ok = 0;
    static int incoming_clock_warned = 0;
    int     check_incoming_fs_clock;
    int     err;
    time_t  want_stamp;
    time_t  expect_stamp;

    /*
     * Make sure the message makes it to file. Set the execute bit when no
     * write error was detected. Some people believe that this code has a
     * problem if the system crashes before fsync() returns; fchmod() could
     * take effect before all the data blocks are written. Wietse claims that
     * this is not a problem. Postfix rejects incomplete queue files, even
     * when the +x attribute is set. Every Postfix queue file record has a
     * type code and a length field. Files with missing records are rejected,
     * as are files with unknown record type codes. Every Postfix queue file
     * must end with an explicit END record. Postfix queue files without END
     * record are discarded.
     * 
     * Attempt to detect file system clocks that are ahead of local time, but
     * don't check the file system clock all the time. The effect of file
     * system clock drift can be difficult to understand (Postfix ignores new
     * mail until the local clock catches up with the file mtime stamp).
     * 
     * This clock drift detection code may not work with file systems that work
     * on a local copy of the file and that update the server only after the
     * file is closed.
     * 
     * Optionally set a cooldown time.
     * 
     * XXX: We assume that utime() does control the file modification time even
     * when followed by an fchmod(), fsync(), close() sequence. This may fail
     * with remote file systems when fsync() actually updates the file. Even
     * then, we still delay the average message by 1/2 of the
     * queue_run_delay.
     * 
     * XXX: Victor does not like running utime() after the close(), since this
     * creates a race even with local filesystems. But Wietse is not
     * confident that utime() before fsync() and close() will work reliably
     * with remote file systems.
     * 
     * XXX Don't run the clock skew tests with Postfix sendmail submissions.
     * Don't whine against unsuspecting users or applications.
     */

    check_incoming_fs_clock =
 (!incoming_fs_clock_ok && !strcmp(info->queue, MAIL_QUEUE_INCOMING));

#ifdef DELAY_ACTION
    if (strcmp(info->queue, MAIL_QUEUE_DEFERRED) != 0)
 info->delay = 0;
    if (info->delay > 0)
 want_stamp = time((time_t *) 0) + info->delay;
    else
#endif
 want_stamp = 0;

    /*
     * If we can cheaply set the file time stamp (no pathname lookup) do it
     * anyway, so that we can avoid whining later about file server/client
     * clock skew.
     * 
     * Otherwise, if we must set the file time stamp for delayed delivery, use
     * whatever means we have to get the job done, no matter if it is
     * expensive.
     * 
     * XXX Unfortunately, Linux futimes() is not usable because it uses /proc.
     * This may not be available because of chroot, or because of access
     * restrictions after a process changes privileges.
     */

    if (vstream_fflush(info->stream)
#ifdef CAN_STAMP_BY_STREAM
 || stamp_stream(info->stream, want_stamp)
#else
 || (want_stamp && stamp_path(VSTREAM_PATH(info->stream), want_stamp))
#endif
 || fchmod(vstream_fileno(info->stream), 0700 | info->mode)
#ifdef HAS_FSYNC
 || fsync(vstream_fileno(info->stream))
#endif
 || (check_incoming_fs_clock
     && fstat(vstream_fileno(info->stream), &st) < 0)
 )
 status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);
#ifdef TEST
    st.st_mtime += 10;
#endif

    /*
     * Work around file system clock skew. If the file system clock is ahead
     * of the local clock, Postfix won't deliver mail immediately, which is
     * bad for performance. If the file system clock falls behind the local
     * clock, it just looks silly in mail headers.
     */

    if (status == CLEANUP_STAT_OK && check_incoming_fs_clock) {
 /* Do NOT use time() result from before fsync(). */
 expect_stamp = want_stamp ? want_stamp : time((time_t *) 0);
 if (st.st_mtime > expect_stamp) {
     path_to_reset = mystrdup(VSTREAM_PATH(info->stream));
     if (incoming_clock_warned == 0) {
  msg_warn("file system clock is %d seconds ahead of local clock",
    (int) (st.st_mtime - expect_stamp));
  msg_warn("resetting file time stamps - this hurts performance");
  incoming_clock_warned = 1;
     }
 } else {
     if (st.st_mtime < expect_stamp - 100)
  msg_warn("file system clock is %d seconds behind local clock",
    (int) (expect_stamp - st.st_mtime));
     incoming_fs_clock_ok = 1;
 }
    }

    /*
     * Close the queue file and mark it as closed. Be prepared for
     * vstream_fclose() to fail even after vstream_fflush() and fsync()
     * reported no error. Reason: after a file is closed, some networked file
     * systems copy the file out to another machine. Running the queue on a
     * remote file system is not recommended, if only for performance
     * reasons.
     */

    err = info->close(info->stream);
    info->stream = 0;
    if (status == CLEANUP_STAT_OK && err != 0)
 status = (errno == EFBIG ? CLEANUP_STAT_SIZE : CLEANUP_STAT_WRITE);

    /*
     * Work around file system clocks that are ahead of local time.
     */

    if (path_to_reset != 0) {
 if (status == CLEANUP_STAT_OK) {
     if (stamp_path(path_to_reset, expect_stamp) < 0 && errno != ENOENT)
  msg_fatal("%s: update file time stamps: %m", info->id);
 }
 myfree(path_to_reset);
    }

    /*
     * When all is well, notify the next service that a new message has been
     * queued.
     */

    if (status == CLEANUP_STAT_OK && info->class && info->service)
 mail_trigger(info->class, info->service, wakeup, sizeof(wakeup));

    /*
     * Cleanup.
     */

    mail_stream_cleanup(info);
    return (status);
}

/* mail_stream_finish_ipc - finish IPC mail stream */

static int mail_stream_finish_ipc(MAIL_STREAM *info, VSTRING *why)
{
    int     status = CLEANUP_STAT_WRITE;

    /*
     * Receive the peer's completion status.
     */

    if ((why && attr_scan(info->stream, ATTR_FLAG_STRICT,
     RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
     RECV_ATTR_STR(MAIL_ATTR_WHY, why),
     ATTR_TYPE_END) != 2)
 || (!why && attr_scan(info->stream, ATTR_FLAG_MISSING,
         RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
         ATTR_TYPE_END) != 1))
 status = CLEANUP_STAT_WRITE;

    /*
     * Cleanup.
     */

    mail_stream_cleanup(info);
    return (status);
}

/* mail_stream_finish - finish action */

int     mail_stream_finish(MAIL_STREAM *info, VSTRING *why)
{
    return (info->finish(info, why));
}

/* mail_stream_file - destination is file */

MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
                 const char *service, int mode)
{
    struct timeval tv;
    MAIL_STREAM *info;
    VSTREAM *stream;

    stream = mail_queue_enter(queue, 0600 | mode, &tv);
    if (msg_verbose)
 msg_info("open %s", VSTREAM_PATH(stream));

    info = (MAIL_STREAM *) mymalloc(sizeof(*info));
    info->stream = stream;
    info->finish = mail_stream_finish_file;
    info->close = vstream_fclose;
    info->queue = mystrdup(queue);
    info->id = mystrdup(basename(VSTREAM_PATH(stream)));
    info->class = mystrdup(class);
    info->service = mystrdup(service);
    info->mode = mode;
#ifdef DELAY_ACTION
    info->delay = 0;
#endif
    info->ctime = tv;
    return (info);
}

/* mail_stream_service - destination is service */

MAIL_STREAM *mail_stream_service(const char *classconst char *name)
{
    VSTREAM *stream;
    MAIL_STREAM *info;

    if (id_buf == 0)
 id_buf = vstring_alloc(10);

    stream = mail_connect_wait(class, name);
    if (attr_scan(stream, ATTR_FLAG_STRICT,
    RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_CLEANUP),
    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, id_buf), 0) != 1) {
 vstream_fclose(stream);
 return (0);
    } else {
 info = (MAIL_STREAM *) mymalloc(sizeof(*info));
 info->stream = stream;
 info->finish = mail_stream_finish_ipc;
 info->close = vstream_fclose;
 info->queue = 0;
 info->id = mystrdup(vstring_str(id_buf));
 info->class = 0;
 info->service = 0;
 return (info);
    }
}

/* mail_stream_command - destination is command */

MAIL_STREAM *mail_stream_command(const char *command)
{
    VSTREAM *stream;
    MAIL_STREAM *info;
    ARGV   *export_env;
    int     status;

    if (id_buf == 0)
 id_buf = vstring_alloc(10);

    /*
     * Treat fork() failure as a transient problem. Treat bad handshake as a
     * permanent error.
     * 
     * XXX Are we invoking a Postfix process or a non-Postfix process? In the
     * former case we can share the full environment; in the latter case only
     * a restricted environment should be propagated. Even though we are
     * talking a Postfix-internal protocol there is no way we can tell what
     * is being executed except by duplicating a lot of existing code.
     */

    export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ);
    while ((stream = vstream_popen(O_RDWR,
       CA_VSTREAM_POPEN_COMMAND(command),
       CA_VSTREAM_POPEN_EXPORT(export_env->argv),
       CA_VSTREAM_POPEN_END)) == 0) {
 msg_warn("fork: %m");
 sleep(10);
    }
    argv_free(export_env);
    vstream_control(stream,
      CA_VSTREAM_CTL_PATH(command),
      CA_VSTREAM_CTL_END);

    if (attr_scan(stream, ATTR_FLAG_STRICT,
    RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_POSTDROP),
    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, id_buf), 0) != 1) {
 if ((status = vstream_pclose(stream)) != 0)
     msg_warn("command \"%s\" exited with status %d", command, status);
 return (0);
    } else {
 info = (MAIL_STREAM *) mymalloc(sizeof(*info));
 info->stream = stream;
 info->finish = mail_stream_finish_ipc;
 info->close = vstream_pclose;
 info->queue = 0;
 info->id = mystrdup(vstring_str(id_buf));
 info->class = 0;
 info->service = 0;
 return (info);
    }
}

/* mail_stream_ctl - update file-based mail stream properties */

void    mail_stream_ctl(MAIL_STREAM *info, int op,...)
{
    const char *myname = "mail_stream_ctl";
    va_list ap;
    char   *new_queue = 0;
    char   *string_value;

    /*
     * Sanity check. None of the attributes below are applicable unless the
     * target is a file-based stream.
     */

    if (info->finish != mail_stream_finish_file)
 msg_panic("%s: attempt to update non-file stream %s",
    myname, info->id);

    for (va_start(ap, op); op != MAIL_STREAM_CTL_END; op = va_arg(ap, int)) {

 switch (op) {

     /*
      * Change the queue directory. We do this at the end of this
      * call.
     */

 case MAIL_STREAM_CTL_QUEUE:
     if ((new_queue = va_arg(ap, char *)) == 0)
  msg_panic("%s: NULL queue",
     myname);
     break;

     /*
      * Change the service that needs to be notified.
     */

 case MAIL_STREAM_CTL_CLASS:
     FREE_AND_WIPE(myfree, info->class);
     if ((string_value = va_arg(ap, char *)) != 0)
  info->class = mystrdup(string_value);
     break;

 case MAIL_STREAM_CTL_SERVICE:
     FREE_AND_WIPE(myfree, info->service);
     if ((string_value = va_arg(ap, char *)) != 0)
  info->service = mystrdup(string_value);
     break;

     /*
      * Change the (finished) file access mode.
     */

 case MAIL_STREAM_CTL_MODE:
     info->mode = va_arg(ap, int);
     break;

     /*
      * Advance the (finished) file modification time.
     */

#ifdef DELAY_ACTION
 case MAIL_STREAM_CTL_DELAY:
     if ((info->delay = va_arg(ap, int)) < 0)
  msg_panic("%s: bad delay time %d", myname, info->delay);
     break;
#endif

 default:
     msg_panic("%s: bad op code %d", myname, op);
 }
    }
    va_end(ap);

    /*
     * Rename the queue file after allocating memory for new information, so
     * that the caller can still remove an embryonic file when memory
     * allocation fails (there is no risk of deleting the wrong file).
     * 
     * Wietse opposed the idea to update run-time error handler information
     * here, because this module wasn't designed to defend against internal
     * concurrency issues with error handlers that attempt to follow dangling
     * pointers.
     * 
     * This code duplicates mail_queue_rename(), except that we need the new
     * path to update the stream pathname.
     */

    if (new_queue != 0 && strcmp(info->queue, new_queue) != 0) {
 char   *saved_queue = info->queue;
 char   *saved_path = mystrdup(VSTREAM_PATH(info->stream));
 VSTRING *new_path = vstring_alloc(100);

 (void) mail_queue_path(new_path, new_queue, info->id);
 info->queue = mystrdup(new_queue);
 vstream_control(info->stream, CA_VSTREAM_CTL_PATH(STR(new_path)),
   CA_VSTREAM_CTL_END);

 if (sane_rename(saved_path, STR(new_path)) == 0
     || (mail_queue_mkdirs(STR(new_path)) == 0
  && sane_rename(saved_path, STR(new_path)) == 0)) {
     if (msg_verbose)
  msg_info("%s: placed in %s queue", info->id, info->queue);
 } else {
     msg_fatal("%s: move to %s queue failed: %m", info->id,
        info->queue);
 }

 myfree(saved_path);
 myfree(saved_queue);
 vstring_free(new_path);
    }
}

Messung V0.5 in Prozent
C=71 H=95 G=83

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.