1 /* $NetBSD: qmgr_feedback.c,v 1.2 2022/10/08 16:12:48 christos 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 /* Wietse Venema
68 /* Google, Inc.
69 /* 111 8th Avenue
70 /* New York, NY 10011, USA
71 /*--*/
72
73 /* System library. */
74
75 #include <sys_defs.h>
76 #include <stdlib.h>
77 #include <limits.h> /* INT_MAX */
78 #include <stdio.h> /* sscanf() */
79 #include <string.h>
80
81 /* Utility library. */
82
83 #include <msg.h>
84 #include <name_code.h>
85 #include <stringops.h>
86 #include <mymalloc.h>
87
88 /* Global library. */
89
90 #include <mail_params.h>
91 #include <mail_conf.h>
92
93 /* Application-specific. */
94
95 #include "qmgr.h"
96
97 /*
98 * Lookup tables for main.cf feedback method names.
99 */
100 const NAME_CODE qmgr_feedback_map[] = {
101 CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
102 #ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
103 CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
104 #endif
105 0, QMGR_FEEDBACK_IDX_NONE,
106 };
107
108 /* qmgr_feedback_init - initialize feedback control */
109
qmgr_feedback_init(QMGR_FEEDBACK * fb,const char * name_prefix,const char * name_tail,const char * def_name,const char * def_val)110 void qmgr_feedback_init(QMGR_FEEDBACK *fb,
111 const char *name_prefix,
112 const char *name_tail,
113 const char *def_name,
114 const char *def_val)
115 {
116 double enum_val;
117 char denom_str[30 + 1];
118 double denom_val;
119 char slash[1 + 1];
120 char junk;
121 char *fbck_name;
122 char *fbck_val;
123
124 /*
125 * Look up the transport-dependent feedback value.
126 */
127 fbck_name = concatenate(name_prefix, name_tail, (char *) 0);
128 fbck_val = get_mail_conf_str(fbck_name, def_val, 1, 0);
129
130 /*
131 * We allow users to express feedback as 1/8, as a more user-friendly
132 * alternative to 0.125 (or worse, having users specify the number of
133 * events in a feedback hysteresis cycle).
134 *
135 * We use some sscanf() fu to parse the value into numerator and optional
136 * "/" followed by denominator. We're doing this only a few times during
137 * the process life time, so we strive for convenience instead of speed.
138 */
139 #define INCLUSIVE_BOUNDS(val, low, high) ((val) >= (low) && (val) <= (high))
140
141 fb->hysteresis = 1; /* legacy */
142 fb->base = -1; /* assume error */
143
144 switch (sscanf(fbck_val, "%lf %1[/] %30s%c",
145 &enum_val, slash, denom_str, &junk)) {
146 case 1:
147 fb->index = QMGR_FEEDBACK_IDX_NONE;
148 fb->base = enum_val;
149 break;
150 case 3:
151 if ((fb->index = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
152 denom_str)) != QMGR_FEEDBACK_IDX_NONE) {
153 fb->base = enum_val;
154 } else if (INCLUSIVE_BOUNDS(enum_val, 0, INT_MAX)
155 && sscanf(denom_str, "%lf%c", &denom_val, &junk) == 1
156 && INCLUSIVE_BOUNDS(denom_val, 1.0 / INT_MAX, INT_MAX)) {
157 fb->base = enum_val / denom_val;
158 }
159 break;
160 }
161
162 /*
163 * Sanity check. If input is bad, we just warn and use a reasonable
164 * default.
165 */
166 if (!INCLUSIVE_BOUNDS(fb->base, 0, 1)) {
167 msg_warn("%s: ignoring malformed or unreasonable feedback: %s",
168 strcmp(fbck_val, def_val) ? fbck_name : def_name, fbck_val);
169 fb->index = QMGR_FEEDBACK_IDX_NONE;
170 fb->base = 1;
171 }
172
173 /*
174 * Performance debugging/analysis.
175 */
176 if (var_conc_feedback_debug)
177 msg_info("%s: %s feedback type %d value at %d: %g",
178 name_prefix, strcmp(fbck_val, def_val) ?
179 fbck_name : def_name, fb->index, var_init_dest_concurrency,
180 QMGR_FEEDBACK_VAL(*fb, var_init_dest_concurrency));
181
182 myfree(fbck_name);
183 myfree(fbck_val);
184 }
185