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