Postfix3.3.1
log_adhoc.c
[詳解]
1 /*++
2 /* NAME
3 /* log_adhoc 3
4 /* SUMMARY
5 /* ad-hoc delivery event logging
6 /* SYNOPSIS
7 /* #include <log_adhoc.h>
8 /*
9 /* void log_adhoc(id, stats, recipient, relay, dsn, status)
10 /* const char *id;
11 /* MSG_STATS *stats;
12 /* RECIPIENT *recipient;
13 /* const char *relay;
14 /* DSN *dsn;
15 /* const char *status;
16 /* DESCRIPTION
17 /* This module logs delivery events in an ad-hoc manner.
18 /*
19 /* log_adhoc() appends a record to the mail logfile
20 /*
21 /* Arguments:
22 /* .IP queue
23 /* The message queue name of the original message file.
24 /* .IP id
25 /* The queue id of the original message file.
26 /* .IP stats
27 /* Time stamps from different message delivery stages
28 /* and session reuse count.
29 /* .IP recipient
30 /* Recipient information. See recipient_list(3).
31 /* .IP sender
32 /* The sender envelope address.
33 /* .IP relay
34 /* Host we could (not) talk to.
35 /* .IP status
36 /* bounced, deferred, sent, and so on.
37 /* .IP dsn
38 /* Delivery status information. See dsn(3).
39 /* BUGS
40 /* Should be replaced by routines with an attribute-value based
41 /* interface instead of an interface that uses a rigid argument list.
42 /* LICENSE
43 /* .ad
44 /* .fi
45 /* The Secure Mailer license must be distributed with this software.
46 /* AUTHOR(S)
47 /* Wietse Venema
48 /* IBM T.J. Watson Research
49 /* P.O. Box 704
50 /* Yorktown Heights, NY 10598, USA
51 /*--*/
52 
53 /* System library. */
54 
55 #include <sys_defs.h>
56 #include <string.h>
57 
58 /* Utility library. */
59 
60 #include <msg.h>
61 #include <vstring.h>
62 #include <format_tv.h>
63 #include <stringops.h>
64 
65 /* Global library. */
66 
67 #include <log_adhoc.h>
68 #include <mail_params.h>
69 
70  /*
71  * Don't use "struct timeval" for time differences; use explicit signed
72  * types instead. The code below relies on signed values to detect clocks
73  * jumping back.
74  */
75 typedef struct {
76  long dt_sec; /* make sure it's signed */
77  long dt_usec; /* make sure it's signed */
78 } DELTA_TIME;
79 
80 /* log_adhoc - ad-hoc logging */
81 
82 void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient,
83  const char *relay, DSN *dsn,
84  const char *status)
85 {
86  static VSTRING *buf;
87  DELTA_TIME delay; /* end-to-end delay */
88  DELTA_TIME pdelay; /* time before queue manager */
89  DELTA_TIME adelay; /* queue manager latency */
90  DELTA_TIME sdelay; /* connection set-up latency */
91  DELTA_TIME xdelay; /* transmission latency */
92  struct timeval now;
93 
94  /*
95  * Alas, we need an intermediate buffer for the pre-formatted result.
96  * There are several optional fields, and the delay fields are formatted
97  * in a manner that is not supported by vstring_sprintf().
98  */
99  if (buf == 0)
100  buf = vstring_alloc(100);
101 
102  /*
103  * First, critical information that identifies the nature of the
104  * transaction.
105  */
106  vstring_sprintf(buf, "%s: to=<%s>", id, recipient->address);
107  if (recipient->orig_addr && *recipient->orig_addr
108  && strcasecmp_utf8(recipient->address, recipient->orig_addr) != 0)
109  vstring_sprintf_append(buf, ", orig_to=<%s>", recipient->orig_addr);
110  vstring_sprintf_append(buf, ", relay=%s", relay);
111  if (stats->reuse_count > 0)
112  vstring_sprintf_append(buf, ", conn_use=%d", stats->reuse_count + 1);
113 
114  /*
115  * Next, performance statistics.
116  *
117  * Use wall clock time to compute pdelay (before active queue latency) if
118  * there is no time stamp for active queue entry. This happens when mail
119  * is bounced by the cleanup server.
120  *
121  * Otherwise, use wall clock time to compute adelay (active queue latency)
122  * if there is no time stamp for hand-off to delivery agent. This happens
123  * when mail was deferred or bounced by the queue manager.
124  *
125  * Otherwise, use wall clock time to compute xdelay (message transfer
126  * latency) if there is no time stamp for delivery completion. In the
127  * case of multi-recipient deliveries the delivery agent may specify the
128  * delivery completion time, so that multiple recipient records show the
129  * same delay values.
130  *
131  * Don't compute the sdelay (connection setup latency) if there is no time
132  * stamp for connection setup completion.
133  *
134  * XXX Apparently, Solaris gettimeofday() can return out-of-range
135  * microsecond values.
136  */
137 #define DELTA(x, y, z) \
138  do { \
139  (x).dt_sec = (y).tv_sec - (z).tv_sec; \
140  (x).dt_usec = (y).tv_usec - (z).tv_usec; \
141  while ((x).dt_usec < 0) { \
142  (x).dt_usec += 1000000; \
143  (x).dt_sec -= 1; \
144  } \
145  while ((x).dt_usec >= 1000000) { \
146  (x).dt_usec -= 1000000; \
147  (x).dt_sec += 1; \
148  } \
149  if ((x).dt_sec < 0) \
150  (x).dt_sec = (x).dt_usec = 0; \
151  } while (0)
152 
153 #define DELTA_ZERO(x) ((x).dt_sec = (x).dt_usec = 0)
154 
155 #define TIME_STAMPED(x) ((x).tv_sec > 0)
156 
157  if (TIME_STAMPED(stats->deliver_done))
158  now = stats->deliver_done;
159  else
160  GETTIMEOFDAY(&now);
161 
162  DELTA(delay, now, stats->incoming_arrival);
163  DELTA_ZERO(adelay);
164  DELTA_ZERO(sdelay);
165  DELTA_ZERO(xdelay);
166  if (TIME_STAMPED(stats->active_arrival)) {
167  DELTA(pdelay, stats->active_arrival, stats->incoming_arrival);
168  if (TIME_STAMPED(stats->agent_handoff)) {
169  DELTA(adelay, stats->agent_handoff, stats->active_arrival);
170  if (TIME_STAMPED(stats->conn_setup_done)) {
171  DELTA(sdelay, stats->conn_setup_done, stats->agent_handoff);
172  DELTA(xdelay, now, stats->conn_setup_done);
173  } else {
174  /* No network client. */
175  DELTA(xdelay, now, stats->agent_handoff);
176  }
177  } else {
178  /* No delivery agent. */
179  DELTA(adelay, now, stats->active_arrival);
180  }
181  } else {
182  /* No queue manager. */
183  DELTA(pdelay, now, stats->incoming_arrival);
184  }
185 
186  /*
187  * Round off large time values to an integral number of seconds, and
188  * display small numbers with only two significant digits, as long as
189  * they do not exceed the time resolution.
190  */
191 #define SIG_DIGS 2
192 #define PRETTY_FORMAT(b, text, x) \
193  do { \
194  vstring_strcat((b), text); \
195  format_tv((b), (x).dt_sec, (x).dt_usec, SIG_DIGS, var_delay_max_res); \
196  } while (0)
197 
198  PRETTY_FORMAT(buf, ", delay=", delay);
199  PRETTY_FORMAT(buf, ", delays=", pdelay);
200  PRETTY_FORMAT(buf, "/", adelay);
201  PRETTY_FORMAT(buf, "/", sdelay);
202  PRETTY_FORMAT(buf, "/", xdelay);
203 
204  /*
205  * Finally, the delivery status.
206  */
207  vstring_sprintf_append(buf, ", dsn=%s, status=%s (%s)",
208  dsn->status, status, dsn->reason);
209 
210  /*
211  * Ship it off.
212  */
213  msg_info("%s", vstring_str(buf));
214 }
const char * orig_addr
#define PRETTY_FORMAT(b, text, x)
#define DELTA(x, y, z)
const char * address
const char * reason
Definition: dsn.h:20
#define vstring_str(vp)
Definition: vstring.h:71
#define TIME_STAMPED(x)
#define strcasecmp_utf8(s1, s2)
Definition: stringops.h:75
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
struct timeval conn_setup_done
Definition: msg_stats.h:59
long dt_usec
Definition: log_adhoc.c:77
long dt_sec
Definition: log_adhoc.c:76
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
struct timeval incoming_arrival
Definition: msg_stats.h:56
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
void log_adhoc(const char *id, MSG_STATS *stats, RECIPIENT *recipient, const char *relay, DSN *dsn, const char *status)
Definition: log_adhoc.c:82
#define DELTA_ZERO(x)
const char * status
Definition: dsn.h:18
Definition: dsn.h:17
int reuse_count
Definition: msg_stats.h:61
struct timeval deliver_done
Definition: msg_stats.h:60
struct timeval agent_handoff
Definition: msg_stats.h:58
struct timeval active_arrival
Definition: msg_stats.h:57
void msg_info(const char *fmt,...)
Definition: msg.c:199