xref: /netbsd-src/external/ibm-public/postfix/dist/src/oqmgr/qmgr_feedback.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: qmgr_feedback.c,v 1.1.1.1 2009/06/23 10:08:50 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	qmgr_feedback 3
6 /* SUMMARY
7 /*	delivery agent feedback management
8 /* SYNOPSIS
9 /*	#include "qmgr.h"
10 /*
11 /*	void	qmgr_feedback_init(fbck_ctl, name_prefix, name_tail,
12 /*					def_name, def_value)
13 /*	QMGR_FEEDBACK *fbck_ctl;
14 /*	const char *name_prefix;
15 /*	const char *name_tail;
16 /*	const char *def_name;
17 /*	const char *def_value;
18 /*
19 /*	double	QMGR_FEEDBACK_VAL(fbck_ctl, concurrency)
20 /*	QMGR_FEEDBACK *fbck_ctl;
21 /*	const int concurrency;
22 /* DESCRIPTION
23 /*	Upon completion of a delivery request, a delivery agent
24 /*	provides a hint that the scheduler should dedicate fewer or
25 /*	more resources to a specific destination.
26 /*
27 /*	qmgr_feedback_init() looks up transport-dependent positive
28 /*	or negative concurrency feedback control information from
29 /*	main.cf, and converts it to internal form.
30 /*
31 /*	QMGR_FEEDBACK_VAL() computes a concurrency adjustment based
32 /*	on a preprocessed feedback control information and the
33 /*	current concurrency window. This is an "unsafe" macro that
34 /*	evaluates some arguments multiple times.
35 /*
36 /*	Arguments:
37 /* .IP fbck_ctl
38 /*	Pointer to QMGR_FEEDBACK structure where the result will
39 /*	be stored.
40 /* .IP name_prefix
41 /*	Mail delivery transport name, used as the initial portion
42 /*	of a transport-dependent concurrency feedback parameter
43 /*	name.
44 /* .IP name_tail
45 /*	The second, and fixed, portion of a transport-dependent
46 /*	concurrency feedback parameter.
47 /* .IP def_name
48 /*	The name of a default feedback parameter.
49 /* .IP def_val
50 /*	The value of the default feedback parameter.
51 /* .IP concurrency
52 /*	Delivery concurrency for concurrency-dependent feedback calculation.
53 /* DIAGNOSTICS
54 /*	Warning: configuration error or unreasonable input. The program
55 /*	uses name_tail feedback instead.
56 /*	Panic: consistency check failure.
57 /* LICENSE
58 /* .ad
59 /* .fi
60 /*	The Secure Mailer license must be distributed with this software.
61 /* AUTHOR(S)
62 /*	Wietse Venema
63 /*	IBM T.J. Watson Research
64 /*	P.O. Box 704
65 /*	Yorktown Heights, NY 10598, USA
66 /*--*/
67 
68 /* System library. */
69 
70 #include <sys_defs.h>
71 #include <stdlib.h>
72 #include <limits.h>			/* INT_MAX */
73 #include <stdio.h>			/* sscanf() */
74 #include <string.h>
75 
76 /* Utility library. */
77 
78 #include <msg.h>
79 #include <name_code.h>
80 #include <stringops.h>
81 #include <mymalloc.h>
82 
83 /* Global library. */
84 
85 #include <mail_params.h>
86 #include <mail_conf.h>
87 
88 /* Application-specific. */
89 
90 #include "qmgr.h"
91 
92  /*
93   * Lookup tables for main.cf feedback method names.
94   */
95 const NAME_CODE qmgr_feedback_map[] = {
96     CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
97 #ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
98     CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
99 #endif
100     0, QMGR_FEEDBACK_IDX_NONE,
101 };
102 
103 /* qmgr_feedback_init - initialize feedback control */
104 
105 void    qmgr_feedback_init(QMGR_FEEDBACK *fb,
106 			           const char *name_prefix,
107 			           const char *name_tail,
108 			           const char *def_name,
109 			           const char *def_val)
110 {
111     double  enum_val;
112     char    denom_str[30 + 1];
113     double  denom_val;
114     char    slash;
115     char    junk;
116     char   *fbck_name;
117     char   *fbck_val;
118 
119     /*
120      * Look up the transport-dependent feedback value.
121      */
122     fbck_name = concatenate(name_prefix, name_tail, (char *) 0);
123     fbck_val = get_mail_conf_str(fbck_name, def_val, 1, 0);
124 
125     /*
126      * We allow users to express feedback as 1/8, as a more user-friendly
127      * alternative to 0.125 (or worse, having users specify the number of
128      * events in a feedback hysteresis cycle).
129      *
130      * We use some sscanf() fu to parse the value into numerator and optional
131      * "/" followed by denominator. We're doing this only a few times during
132      * the process life time, so we strive for convenience instead of speed.
133      */
134 #define INCLUSIVE_BOUNDS(val, low, high) ((val) >= (low) && (val) <= (high))
135 
136     fb->hysteresis = 1;				/* legacy */
137     fb->base = -1;				/* assume error */
138 
139     switch (sscanf(fbck_val, "%lf %1[/] %30s%c",
140 		   &enum_val, &slash, denom_str, &junk)) {
141     case 1:
142 	fb->index = QMGR_FEEDBACK_IDX_NONE;
143 	fb->base = enum_val;
144 	break;
145     case 3:
146 	if ((fb->index = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
147 				   denom_str)) != QMGR_FEEDBACK_IDX_NONE) {
148 	    fb->base = enum_val;
149 	} else if (INCLUSIVE_BOUNDS(enum_val, 0, INT_MAX)
150 		   && sscanf(denom_str, "%lf%c", &denom_val, &junk) == 1
151 		   && INCLUSIVE_BOUNDS(denom_val, 1.0 / INT_MAX, INT_MAX)) {
152 	    fb->base = enum_val / denom_val;
153 	}
154 	break;
155     }
156 
157     /*
158      * Sanity check. If input is bad, we just warn and use a reasonable
159      * default.
160      */
161     if (!INCLUSIVE_BOUNDS(fb->base, 0, 1)) {
162 	msg_warn("%s: ignoring malformed or unreasonable feedback: %s",
163 		 strcmp(fbck_val, def_val) ? fbck_name : def_name, fbck_val);
164 	fb->index = QMGR_FEEDBACK_IDX_NONE;
165 	fb->base = 1;
166     }
167 
168     /*
169      * Performance debugging/analysis.
170      */
171     if (var_conc_feedback_debug)
172 	msg_info("%s: %s feedback type %d value at %d: %g",
173 		 name_prefix, strcmp(fbck_val, def_val) ?
174 		 fbck_name : def_name, fb->index, var_init_dest_concurrency,
175 		 QMGR_FEEDBACK_VAL(*fb, var_init_dest_concurrency));
176 
177     myfree(fbck_name);
178     myfree(fbck_val);
179 }
180