1/*++
2/* NAME
3/*	msg_rate_delay 3
4/* SUMMARY
5/*	diagnostic interface
6/* SYNOPSIS
7/*	#include <msg.h>
8/*
9/*	void	msg_rate_delay(stamp, delay, log_fn, fmt, ...)
10/*	time_t	*stamp;
11/*	int	delay;
12/*	void	(*log_fn)(const char *fmt, ...);
13/*	const char *fmt;
14/* DESCRIPTION
15/*	msg_rate_delay() produces log output at a reduced rate: no
16/*	more than one message per 'delay' seconds. It discards log
17/*	output that would violate the output rate policy.
18/*
19/*	This is typically used to log errors accessing a cache with
20/*	high-frequency access but low-value information, to avoid
21/*	spamming the logfile with the same kind of message.
22/*
23/*	Arguments:
24/* .IP stamp
25/*	Time stamp of last log output; specify a zero time stamp
26/*	on the first call.  This is an input-output parameter.
27/*	This parameter is ignored when verbose logging is enabled
28/*	or when the delay value is zero.
29/* .IP delay
30/*	The minimum time between log outputs; specify zero to log
31/*	all output for debugging purposes.  This parameter is ignored
32/*	when verbose logging is enabled.
33/* .IP log_fn
34/*	The function that produces log output. Typically, this will
35/*	be msg_info() or msg_warn().
36/* .IP fmt
37/*	Format string as used with msg(3) routines.
38/* SEE ALSO
39/*	msg(3) diagnostics interface
40/* DIAGNOSTICS
41/*	Fatal errors: memory allocation problem.
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
54/* System library. */
55
56#include <sys_defs.h>
57#include <time.h>
58
59/* Utility library. */
60
61#include <msg.h>
62#include <vstring.h>
63#include <events.h>
64
65/* SLMs. */
66
67#define STR(x) vstring_str(x)
68
69/* msg_rate_delay - rate-limit message logging */
70
71void    msg_rate_delay(time_t *stamp, int delay,
72		               void (*log_fn) (const char *,...),
73		               const char *fmt,...)
74{
75    const char *myname = "msg_rate_delay";
76    static time_t saved_event_time;
77    time_t  now;
78    VSTRING *buf;
79    va_list ap;
80
81    /*
82     * Sanity check.
83     */
84    if (delay < 0)
85	msg_panic("%s: bad message rate delay: %d", myname, delay);
86
87    /*
88     * This function may be called frequently. Avoid an unnecessary syscall
89     * if possible. Deal with the possibility that a program does not use the
90     * events(3) engine, so that event_time() always produces the same
91     * result.
92     */
93    if (msg_verbose == 0 && delay > 0) {
94	if (saved_event_time == 0)
95	    now = saved_event_time = event_time();
96	else if ((now = event_time()) == saved_event_time)
97	    now = time((time_t *) 0);
98
99	/*
100	 * Don't log if time is too early.
101	 */
102	if (*stamp + delay > now)
103	    return;
104	*stamp = now;
105    }
106
107    /*
108     * OK to log. This is a low-rate event, so we can afford some overhead.
109     */
110    buf = vstring_alloc(100);
111    va_start(ap, fmt);
112    vstring_vsprintf(buf, fmt, ap);
113    va_end(ap);
114    log_fn("%s", STR(buf));
115    vstring_free(buf);
116}
117
118#ifdef TEST
119
120 /*
121  * Proof-of-concept test program: log messages but skip messages during a
122  * two-second gap.
123  */
124#include <unistd.h>
125
126int     main(int argc, char **argv)
127{
128    int     n;
129    time_t  stamp = 0;
130
131    for (n = 0; n < 6; n++) {
132	msg_rate_delay(&stamp, 2, msg_info, "text here %d", n);
133	sleep(1);
134    }
135    return (0);
136}
137
138#endif
139