示例#1
0
static void qmgr_deliver_update(int unused_event, char *context)
{
    QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
    QMGR_QUEUE *queue = entry->queue;
    QMGR_TRANSPORT *transport = queue->transport;
    QMGR_MESSAGE *message = entry->message;
    static DSN_BUF *dsb;
    int     status;

    /*
     * Release the delivery agent from a "hot" queue entry.
     */
#define QMGR_DELIVER_RELEASE_AGENT(entry) do { \
	event_disable_readwrite(vstream_fileno(entry->stream)); \
	(void) vstream_fclose(entry->stream); \
	entry->stream = 0; \
	qmgr_deliver_concurrency--; \
    } while (0)

    if (dsb == 0)
	dsb = dsb_create();

    /*
     * The message transport has responded. Stop the watchdog timer.
     */
    event_cancel_timer(qmgr_deliver_abort, context);

    /*
     * Retrieve the delivery agent status report. The numerical status code
     * indicates if delivery should be tried again. The reason text is sent
     * only when a site should be avoided for a while, so that the queue
     * manager can log why it does not even try to schedule delivery to the
     * affected recipients.
     */
    status = qmgr_deliver_final_reply(entry->stream, dsb);

    /*
     * The mail delivery process failed for some reason (although delivery
     * may have been successful). Back off with this transport type for a
     * while. Dispose of queue entries for this transport that await
     * selection (the todo lists). Stay away from queue entries that have
     * been selected (the busy lists), or we would have dangling pointers.
     * The queue itself won't go away before we dispose of the current queue
     * entry.
     */
    if (status == DELIVER_STAT_CRASH) {
	message->flags |= DELIVER_STAT_DEFER;
#if 0
	whatsup = concatenate("unknown ", transport->name,
			      " mail transport error", (char *) 0);
	qmgr_transport_throttle(transport,
				DSN_SIMPLE(&dsb->dsn, "4.3.0", whatsup));
	myfree(whatsup);
#else
	qmgr_transport_throttle(transport,
				DSN_SIMPLE(&dsb->dsn, "4.3.0",
					   "unknown mail transport error"));
#endif
	msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
		 transport->name);

	/*
	 * Assume the worst and write a defer logfile record for each
	 * recipient. This omission was already present in the first queue
	 * manager implementation of 199703, and was fixed 200511.
	 * 
	 * To avoid the synchronous qmgr_defer_recipient() operation for each
	 * recipient of this queue entry, release the delivery process and
	 * move the entry back to the todo queue. Let qmgr_defer_transport()
	 * log the recipient asynchronously if possible, and get out of here.
	 * Note: if asynchronous logging is not possible,
	 * qmgr_defer_transport() eventually invokes qmgr_entry_done() and
	 * the entry becomes a dangling pointer.
	 */
	QMGR_DELIVER_RELEASE_AGENT(entry);
	qmgr_entry_unselect(queue, entry);
	qmgr_defer_transport(transport, &dsb->dsn);
	return;
    }

    /*
     * This message must be tried again.
     * 
     * If we have a problem talking to this site, back off with this site for a
     * while; dispose of queue entries for this site that await selection
     * (the todo list); stay away from queue entries that have been selected
     * (the busy list), or we would have dangling pointers. The queue itself
     * won't go away before we dispose of the current queue entry.
     * 
     * XXX Caution: DSN_COPY() will panic on empty status or reason.
     */
#define SUSPENDED	"delivery temporarily suspended: "

    if (status == DELIVER_STAT_DEFER) {
	message->flags |= DELIVER_STAT_DEFER;
	if (VSTRING_LEN(dsb->status)) {
	    /* Sanitize the DSN status/reason from the delivery agent. */
	    if (!dsn_valid(vstring_str(dsb->status)))
		vstring_strcpy(dsb->status, "4.0.0");
	    if (VSTRING_LEN(dsb->reason) == 0)
		vstring_strcpy(dsb->reason, "unknown error");
	    vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
	    if (QMGR_QUEUE_READY(queue)) {
		qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(dsb));
		if (QMGR_QUEUE_THROTTLED(queue))
		    qmgr_defer_todo(queue, &dsb->dsn);
	    }
	}
    }

    /*
     * No problems detected. Mark the transport and queue as alive. The queue
     * itself won't go away before we dispose of the current queue entry.
     */
    if (status != DELIVER_STAT_CRASH && VSTRING_LEN(dsb->reason) == 0) {
	qmgr_transport_unthrottle(transport);
	qmgr_queue_unthrottle(queue);
    }

    /*
     * Release the delivery process, and give some other queue entry a chance
     * to be delivered. When all recipients for a message have been tried,
     * decide what to do next with this message: defer, bounce, delete.
     */
    QMGR_DELIVER_RELEASE_AGENT(entry);
    qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
}
示例#2
0
static void qmgr_deliver_update(int unused_event, char *context)
{
    QMGR_ENTRY *entry = (QMGR_ENTRY *) context;
    QMGR_QUEUE *queue = entry->queue;
    QMGR_TRANSPORT *transport = queue->transport;
    QMGR_MESSAGE *message = entry->message;
    VSTRING *reason = vstring_alloc(1);
    int     status;

    /*
     * The message transport has responded. Stop the watchdog timer.
     */
    event_cancel_timer(qmgr_deliver_abort, context);

    /*
     * Retrieve the delivery agent status report. The numerical status code
     * indicates if delivery should be tried again. The reason text is sent
     * only when a site should be avoided for a while, so that the queue
     * manager can log why it does not even try to schedule delivery to the
     * affected recipients.
     */
    status = qmgr_deliver_final_reply(entry->stream, reason);

    /*
     * The mail delivery process failed for some reason (although delivery
     * may have been successful). Back off with this transport type for a
     * while. Dispose of queue entries for this transport that await
     * selection (the todo lists). Stay away from queue entries that have
     * been selected (the busy lists), or we would have dangling pointers.
     * The queue itself won't go away before we dispose of the current queue
     * entry.
     */
    if (status == DELIVER_STAT_CRASH) {
	message->flags |= DELIVER_STAT_DEFER;
	qmgr_transport_throttle(transport, "unknown mail transport error");
	msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
		 transport->name);
	qmgr_defer_transport(transport, transport->reason);
    }

    /*
     * This message must be tried again.
     * 
     * If we have a problem talking to this site, back off with this site for a
     * while; dispose of queue entries for this site that await selection
     * (the todo list); stay away from queue entries that have been selected
     * (the busy list), or we would have dangling pointers. The queue itself
     * won't go away before we dispose of the current queue entry.
     */
    if (status == DELIVER_STAT_DEFER) {
	message->flags |= DELIVER_STAT_DEFER;
	if (VSTRING_LEN(reason)) {
	    qmgr_queue_throttle(queue, vstring_str(reason));
	    if (queue->window == 0)
		qmgr_defer_todo(queue, queue->reason);
	}
    }

    /*
     * No problems detected. Mark the transport and queue as alive. The queue
     * itself won't go away before we dispose of the current queue entry.
     */
    if (VSTRING_LEN(reason) == 0) {
	qmgr_transport_unthrottle(transport);
	qmgr_queue_unthrottle(queue);
    }

    /*
     * Release the delivery process, and give some other queue entry a chance
     * to be delivered. When all recipients for a message have been tried,
     * decide what to do next with this message: defer, bounce, delete.
     */
    event_disable_readwrite(vstream_fileno(entry->stream));
    if (vstream_fclose(entry->stream) != 0)
	msg_warn("qmgr_deliver_update: close delivery stream: %m");
    entry->stream = 0;
    qmgr_deliver_concurrency--;
    qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
    vstring_free(reason);
}