~ubuntu-branches/ubuntu/hoary/postfix/hoary-security

« back to all changes in this revision

Viewing changes to src/oqmgr/qmgr_entry.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2004-10-06 11:50:33 UTC
  • Revision ID: james.westby@ubuntu.com-20041006115033-ooo6yfg6kmoteu04
Tags: upstream-2.1.3
ImportĀ upstreamĀ versionĀ 2.1.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      qmgr_entry 3
 
4
/* SUMMARY
 
5
/*      per-site queue entries
 
6
/* SYNOPSIS
 
7
/*      #include "qmgr.h"
 
8
/*
 
9
/*      QMGR_ENTRY *qmgr_entry_create(queue, message)
 
10
/*      QMGR_QUEUE *queue;
 
11
/*      QMGR_MESSAGE *message;
 
12
/*
 
13
/*      void    qmgr_entry_done(entry, which)
 
14
/*      QMGR_ENTRY *entry;
 
15
/*      int     which;
 
16
/*
 
17
/*      QMGR_ENTRY *qmgr_entry_select(queue)
 
18
/*      QMGR_QUEUE *queue;
 
19
/*
 
20
/*      void    qmgr_entry_unselect(queue, entry)
 
21
/*      QMGR_QUEUE *queue;
 
22
/*      QMGR_ENTRY *entry;
 
23
/* DESCRIPTION
 
24
/*      These routines add/delete/manipulate per-site message
 
25
/*      delivery requests.
 
26
/*
 
27
/*      qmgr_entry_create() creates an entry for the named queue and
 
28
/*      message, and appends the entry to the queue's todo list.
 
29
/*      Filling in and cleaning up the recipients is the responsibility
 
30
/*      of the caller.
 
31
/*
 
32
/*      qmgr_entry_done() discards a per-site queue entry.  The
 
33
/*      \fIwhich\fR argument is either QMGR_QUEUE_BUSY for an entry
 
34
/*      of the site's `busy' list (i.e. queue entries that have been
 
35
/*      selected for actual delivery), or QMGR_QUEUE_TODO for an entry
 
36
/*      of the site's `todo' list (i.e. queue entries awaiting selection
 
37
/*      for actual delivery).
 
38
/*
 
39
/*      qmgr_entry_done() triggers cleanup of the per-site queue when
 
40
/*      the site has no pending deliveries, and the site is either
 
41
/*      alive, or the site is dead and the number of in-core queues
 
42
/*      exceeds a configurable limit (see qmgr_queue_done()).
 
43
/*
 
44
/*      qmgr_entry_done() triggers special action when the last in-core
 
45
/*      queue entry for a message is done with: either read more
 
46
/*      recipients from the queue file, delete the queue file, or move
 
47
/*      the queue file to the deferred queue; send bounce reports to the
 
48
/*      message originator (see qmgr_active_done()).
 
49
/*
 
50
/*      qmgr_entry_select() selects the next entry from the named
 
51
/*      per-site queue's `todo' list for actual delivery. The entry is
 
52
/*      moved to the queue's `busy' list: the list of messages being
 
53
/*      delivered.
 
54
/*
 
55
/*      qmgr_entry_unselect() takes the named entry off the named
 
56
/*      per-site queue's `busy' list and moves it to the queue's
 
57
/*      `todo' list.
 
58
/* DIAGNOSTICS
 
59
/*      Panic: interface violations, internal inconsistencies.
 
60
/* LICENSE
 
61
/* .ad
 
62
/* .fi
 
63
/*      The Secure Mailer license must be distributed with this software.
 
64
/* AUTHOR(S)
 
65
/*      Wietse Venema
 
66
/*      IBM T.J. Watson Research
 
67
/*      P.O. Box 704
 
68
/*      Yorktown Heights, NY 10598, USA
 
69
/*--*/
 
70
 
 
71
/* System library. */
 
72
 
 
73
#include <sys_defs.h>
 
74
#include <stdlib.h>
 
75
#include <time.h>
 
76
 
 
77
/* Utility library. */
 
78
 
 
79
#include <msg.h>
 
80
#include <mymalloc.h>
 
81
#include <events.h>
 
82
#include <vstream.h>
 
83
 
 
84
/* Global library. */
 
85
 
 
86
#include <mail_params.h>
 
87
 
 
88
/* Application-specific. */
 
89
 
 
90
#include "qmgr.h"
 
91
 
 
92
/* qmgr_entry_select - select queue entry for delivery */
 
93
 
 
94
QMGR_ENTRY *qmgr_entry_select(QMGR_QUEUE *queue)
 
95
{
 
96
    QMGR_ENTRY *entry;
 
97
 
 
98
    if ((entry = queue->todo.prev) != 0) {
 
99
        QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry);
 
100
        queue->todo_refcount--;
 
101
        QMGR_LIST_APPEND(queue->busy, entry);
 
102
        queue->busy_refcount++;
 
103
    }
 
104
    return (entry);
 
105
}
 
106
 
 
107
/* qmgr_entry_unselect - unselect queue entry for delivery */
 
108
 
 
109
void    qmgr_entry_unselect(QMGR_QUEUE *queue, QMGR_ENTRY *entry)
 
110
{
 
111
    QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry);
 
112
    queue->busy_refcount--;
 
113
    QMGR_LIST_APPEND(queue->todo, entry);
 
114
    queue->todo_refcount++;
 
115
}
 
116
 
 
117
/* qmgr_entry_done - dispose of queue entry */
 
118
 
 
119
void    qmgr_entry_done(QMGR_ENTRY *entry, int which)
 
120
{
 
121
    QMGR_QUEUE *queue = entry->queue;
 
122
    QMGR_MESSAGE *message = entry->message;
 
123
 
 
124
    /*
 
125
     * Take this entry off the in-core queue.
 
126
     */
 
127
    if (entry->stream != 0)
 
128
        msg_panic("qmgr_entry_done: file is open");
 
129
    if (which == QMGR_QUEUE_BUSY) {
 
130
        QMGR_LIST_UNLINK(queue->busy, QMGR_ENTRY *, entry);
 
131
        queue->busy_refcount--;
 
132
    } else if (which == QMGR_QUEUE_TODO) {
 
133
        QMGR_LIST_UNLINK(queue->todo, QMGR_ENTRY *, entry);
 
134
        queue->todo_refcount--;
 
135
    } else {
 
136
        msg_panic("qmgr_entry_done: bad queue spec: %d", which);
 
137
    }
 
138
 
 
139
    /*
 
140
     * Free the recipient list and decrease the in-core recipient count
 
141
     * accordingly.
 
142
     */
 
143
    qmgr_recipient_count -= entry->rcpt_list.len;
 
144
    qmgr_rcpt_list_free(&entry->rcpt_list);
 
145
 
 
146
    myfree((char *) entry);
 
147
 
 
148
    /*
 
149
     * When the in-core queue for this site is empty and when this site is
 
150
     * not dead, discard the in-core queue. When this site is dead, but the
 
151
     * number of in-core queues exceeds some threshold, get rid of this
 
152
     * in-core queue anyway, in order to avoid running out of memory.
 
153
     */
 
154
    if (queue->todo.next == 0 && queue->busy.next == 0) {
 
155
        if (queue->window == 0 && qmgr_queue_count > 2 * var_qmgr_rcpt_limit)
 
156
            qmgr_queue_unthrottle(queue);
 
157
        if (queue->window > 0)
 
158
            qmgr_queue_done(queue);
 
159
    }
 
160
 
 
161
    /*
 
162
     * Update the in-core message reference count. When the in-core message
 
163
     * structure has no more references, dispose of the message.
 
164
     * 
 
165
     * When the in-core recipient count falls below a threshold, and this
 
166
     * message has more recipients, read more recipients now. If we read more
 
167
     * recipients as soon as the recipient count falls below the in-core
 
168
     * recipient limit, we do not give other messages a chance until this
 
169
     * message is delivered. That's good for mailing list deliveries, bad for
 
170
     * one-to-one mail. If we wait until the in-core recipient count drops
 
171
     * well below the in-core recipient limit, we give other mail a chance,
 
172
     * but we also allow list deliveries to become interleaved. In the worst
 
173
     * case, people near the start of a mailing list get a burst of postings
 
174
     * today, while people near the end of the list get that same burst of
 
175
     * postings a whole day later.
 
176
     */
 
177
#define FUDGE(x)        ((x) * (var_qmgr_fudge / 100.0))
 
178
    message->refcount--;
 
179
    if (message->rcpt_offset > 0
 
180
        && qmgr_recipient_count < FUDGE(var_qmgr_rcpt_limit))
 
181
        qmgr_message_realloc(message);
 
182
    if (message->refcount == 0)
 
183
        qmgr_active_done(message);
 
184
}
 
185
 
 
186
/* qmgr_entry_create - create queue todo entry */
 
187
 
 
188
QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message)
 
189
{
 
190
    QMGR_ENTRY *entry;
 
191
 
 
192
    /*
 
193
     * Sanity check.
 
194
     */
 
195
    if (queue->window == 0)
 
196
        msg_panic("qmgr_entry_create: dead queue: %s", queue->name);
 
197
 
 
198
    /*
 
199
     * Create the delivery request.
 
200
     */
 
201
    entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY));
 
202
    entry->stream = 0;
 
203
    entry->message = message;
 
204
    qmgr_rcpt_list_init(&entry->rcpt_list);
 
205
    message->refcount++;
 
206
    entry->queue = queue;
 
207
    QMGR_LIST_APPEND(queue->todo, entry);
 
208
    queue->todo_refcount++;
 
209
 
 
210
    /*
 
211
     * Warn if a destination is falling behind while the active queue
 
212
     * contains a non-trivial amount of single-recipient email. When a
 
213
     * destination takes up more and more space in the active queue, then
 
214
     * other mail will not get through and delivery performance will suffer.
 
215
     * 
 
216
     * XXX At this point in the code, the busy reference count is still less
 
217
     * than the concurrency limit (otherwise this code would not be invoked
 
218
     * in the first place) so we have to make make some awkward adjustments
 
219
     * below.
 
220
     * 
 
221
     * XXX The queue length test below looks at the active queue share of an
 
222
     * individual destination. This catches the case where mail for one
 
223
     * destination is falling behind because it has to round-robin compete
 
224
     * with many other destinations. However, Postfix will also perform
 
225
     * poorly when most of the active queue is tied up by a small number of
 
226
     * concurrency limited destinations. The queue length test below detects
 
227
     * such conditions only indirectly.
 
228
     * 
 
229
     * XXX This code does not detect the case that the active queue is being
 
230
     * starved because incoming mail is pounding the disk.
 
231
     */
 
232
    if (var_helpful_warnings && var_qmgr_clog_warn_time > 0) {
 
233
        int     queue_length = queue->todo_refcount + queue->busy_refcount;
 
234
        time_t  now;
 
235
        QMGR_TRANSPORT *transport;
 
236
        double  active_share;
 
237
 
 
238
        if (queue_length > var_qmgr_active_limit / 5
 
239
            && (now = event_time()) >= queue->clog_time_to_warn) {
 
240
            active_share = queue_length / (double) qmgr_message_count;
 
241
            msg_warn("mail for %s is using up %d of %d active queue entries",
 
242
                     queue->nexthop, queue_length, qmgr_message_count);
 
243
            if (active_share < 0.9)
 
244
                msg_warn("this may slow down other mail deliveries");
 
245
            transport = queue->transport;
 
246
            if (transport->dest_concurrency_limit > 0
 
247
            && transport->dest_concurrency_limit <= queue->busy_refcount + 1)
 
248
                msg_warn("you may need to increase the main.cf %s%s from %d",
 
249
                         transport->name, _DEST_CON_LIMIT,
 
250
                         transport->dest_concurrency_limit);
 
251
            else if (queue->window > var_qmgr_active_limit * active_share)
 
252
                msg_warn("you may need to increase the main.cf %s from %d",
 
253
                         VAR_QMGR_ACT_LIMIT, var_qmgr_active_limit);
 
254
            else if (queue->peers.next != queue->peers.prev)
 
255
                msg_warn("you may need a separate master.cf transport for %s",
 
256
                         queue->nexthop);
 
257
            else {
 
258
                msg_warn("you may need to reduce %s connect and helo timeouts",
 
259
                         transport->name);
 
260
                msg_warn("so that Postfix quickly skips unavailable hosts");
 
261
                msg_warn("you may need to increase the main.cf %s and %s",
 
262
                         VAR_MIN_BACKOFF_TIME, VAR_MAX_BACKOFF_TIME);
 
263
                msg_warn("so that Postfix wastes less time on undeliverable mail");
 
264
                msg_warn("you may need to increase the master.cf %s process limit",
 
265
                         transport->name);
 
266
            }
 
267
            msg_warn("please avoid flushing the whole queue when you have");
 
268
            msg_warn("lots of deferred mail, that is bad for performance");
 
269
            msg_warn("to turn off these warnings specify: %s = 0",
 
270
                     VAR_QMGR_CLOG_WARN_TIME);
 
271
            queue->clog_time_to_warn = now + var_qmgr_clog_warn_time;
 
272
        }
 
273
    }
 
274
    return (entry);
 
275
}