summaryrefslogtreecommitdiffstats
path: root/src/qmgr/qmgr_feedback.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmgr/qmgr_feedback.c')
-rw-r--r--src/qmgr/qmgr_feedback.c177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/qmgr/qmgr_feedback.c b/src/qmgr/qmgr_feedback.c
new file mode 100644
index 0000000..f341b95
--- /dev/null
+++ b/src/qmgr/qmgr_feedback.c
@@ -0,0 +1,177 @@
+/*++
+/* NAME
+/* qmgr_feedback 3
+/* SUMMARY
+/* delivery agent feedback management
+/* SYNOPSIS
+/* #include "qmgr.h"
+/*
+/* void qmgr_feedback_init(fbck_ctl, name_prefix, name_tail,
+/* def_name, def_value)
+/* QMGR_FEEDBACK *fbck_ctl;
+/* const char *name_prefix;
+/* const char *name_tail;
+/* const char *def_name;
+/* const char *def_value;
+/*
+/* double QMGR_FEEDBACK_VAL(fbck_ctl, concurrency)
+/* QMGR_FEEDBACK *fbck_ctl;
+/* const int concurrency;
+/* DESCRIPTION
+/* Upon completion of a delivery request, a delivery agent
+/* provides a hint that the scheduler should dedicate fewer or
+/* more resources to a specific destination.
+/*
+/* qmgr_feedback_init() looks up transport-dependent positive
+/* or negative concurrency feedback control information from
+/* main.cf, and converts it to internal form.
+/*
+/* QMGR_FEEDBACK_VAL() computes a concurrency adjustment based
+/* on a preprocessed feedback control information and the
+/* current concurrency window. This is an "unsafe" macro that
+/* evaluates some arguments multiple times.
+/*
+/* Arguments:
+/* .IP fbck_ctl
+/* Pointer to QMGR_FEEDBACK structure where the result will
+/* be stored.
+/* .IP name_prefix
+/* Mail delivery transport name, used as the initial portion
+/* of a transport-dependent concurrency feedback parameter
+/* name.
+/* .IP name_tail
+/* The second, and fixed, portion of a transport-dependent
+/* concurrency feedback parameter.
+/* .IP def_name
+/* The name of a default feedback parameter.
+/* .IP def_val
+/* The value of the default feedback parameter.
+/* .IP concurrency
+/* Delivery concurrency for concurrency-dependent feedback calculation.
+/* DIAGNOSTICS
+/* Warning: configuration error or unreasonable input. The program
+/* uses name_tail feedback instead.
+/* Panic: consistency check failure.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>
+#include <limits.h> /* INT_MAX */
+#include <stdio.h> /* sscanf() */
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <name_code.h>
+#include <stringops.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+
+/* Application-specific. */
+
+#include "qmgr.h"
+
+ /*
+ * Lookup tables for main.cf feedback method names.
+ */
+const NAME_CODE qmgr_feedback_map[] = {
+ CONC_FDBACK_NAME_WIN, QMGR_FEEDBACK_IDX_WIN,
+#ifdef QMGR_FEEDBACK_IDX_SQRT_WIN
+ CONC_FDBACK_NAME_SQRT_WIN, QMGR_FEEDBACK_IDX_SQRT_WIN,
+#endif
+ 0, QMGR_FEEDBACK_IDX_NONE,
+};
+
+/* qmgr_feedback_init - initialize feedback control */
+
+void qmgr_feedback_init(QMGR_FEEDBACK *fb,
+ const char *name_prefix,
+ const char *name_tail,
+ const char *def_name,
+ const char *def_val)
+{
+ double enum_val;
+ char denom_str[30 + 1];
+ double denom_val;
+ char slash;
+ char junk;
+ char *fbck_name;
+ char *fbck_val;
+
+ /*
+ * Look up the transport-dependent feedback value.
+ */
+ fbck_name = concatenate(name_prefix, name_tail, (char *) 0);
+ fbck_val = get_mail_conf_str(fbck_name, def_val, 1, 0);
+
+ /*
+ * We allow users to express feedback as 1/8, as a more user-friendly
+ * alternative to 0.125 (or worse, having users specify the number of
+ * events in a feedback hysteresis cycle).
+ *
+ * We use some sscanf() fu to parse the value into numerator and optional
+ * "/" followed by denominator. We're doing this only a few times during
+ * the process life time, so we strive for convenience instead of speed.
+ */
+#define INCLUSIVE_BOUNDS(val, low, high) ((val) >= (low) && (val) <= (high))
+
+ fb->hysteresis = 1; /* legacy */
+ fb->base = -1; /* assume error */
+
+ switch (sscanf(fbck_val, "%lf %1[/] %30s%c",
+ &enum_val, &slash, denom_str, &junk)) {
+ case 1:
+ fb->index = QMGR_FEEDBACK_IDX_NONE;
+ fb->base = enum_val;
+ break;
+ case 3:
+ if ((fb->index = name_code(qmgr_feedback_map, NAME_CODE_FLAG_NONE,
+ denom_str)) != QMGR_FEEDBACK_IDX_NONE) {
+ fb->base = enum_val;
+ } else if (INCLUSIVE_BOUNDS(enum_val, 0, INT_MAX)
+ && sscanf(denom_str, "%lf%c", &denom_val, &junk) == 1
+ && INCLUSIVE_BOUNDS(denom_val, 1.0 / INT_MAX, INT_MAX)) {
+ fb->base = enum_val / denom_val;
+ }
+ break;
+ }
+
+ /*
+ * Sanity check. If input is bad, we just warn and use a reasonable
+ * default.
+ */
+ if (!INCLUSIVE_BOUNDS(fb->base, 0, 1)) {
+ msg_warn("%s: ignoring malformed or unreasonable feedback: %s",
+ strcmp(fbck_val, def_val) ? fbck_name : def_name, fbck_val);
+ fb->index = QMGR_FEEDBACK_IDX_NONE;
+ fb->base = 1;
+ }
+
+ /*
+ * Performance debugging/analysis.
+ */
+ if (var_conc_feedback_debug)
+ msg_info("%s: %s feedback type %d value at %d: %g",
+ name_prefix, strcmp(fbck_val, def_val) ?
+ fbck_name : def_name, fb->index, var_init_dest_concurrency,
+ QMGR_FEEDBACK_VAL(*fb, var_init_dest_concurrency));
+
+ myfree(fbck_name);
+ myfree(fbck_val);
+}