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