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