summaryrefslogtreecommitdiff
path: root/gnu/usr.sbin/sendmail/libmilter/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.sbin/sendmail/libmilter/monitor.c')
-rw-r--r--gnu/usr.sbin/sendmail/libmilter/monitor.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/gnu/usr.sbin/sendmail/libmilter/monitor.c b/gnu/usr.sbin/sendmail/libmilter/monitor.c
new file mode 100644
index 00000000000..3d23f0c27e4
--- /dev/null
+++ b/gnu/usr.sbin/sendmail/libmilter/monitor.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include "libmilter.h"
+
+#if _FFR_THREAD_MONITOR
+
+/*
+** Thread Monitoring
+** Todo: more error checking (return code from function calls)
+** add comments.
+*/
+
+bool Monitor = false; /* use monitoring? */
+static unsigned int Mon_exec_time = 0;
+
+/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
+static smutex_t Mon_mutex;
+static scond_t Mon_cv;
+
+/*
+** Current ctx to monitor.
+** Invariant:
+** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
+** time ago.
+**
+** Basically the entries in the list are ordered by time because new
+** entries are appended at the end. However, due to the concurrent
+** execution (multi-threaded) and no guaranteed order of wakeups
+** after a mutex_lock() attempt, the order might not be strict,
+** i.e., if the list contains e1 and e2 (in that order) then
+** the the start time of e2 can be (slightly) smaller than that of e1.
+** However, this slight inaccurracy should not matter for the proper
+** working of this algorithm.
+*/
+
+static SMFICTX_PTR Mon_cur_ctx = NULL;
+static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
+
+/*
+** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
+**
+** Parameters:
+** tm -- maximum execution time for a thread
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_set_max_exec_time(tm)
+ unsigned int tm;
+{
+ Mon_exec_time = tm;
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MONITOR_THREAD -- monitoring thread
+**
+** Parameters:
+** arg -- ignored (required by pthread_create())
+**
+** Returns:
+** NULL on termination.
+*/
+
+static void *
+mi_monitor_thread(arg)
+ void *arg;
+{
+ sthread_t tid;
+ int r;
+ time_t now, end;
+
+ SM_ASSERT(Monitor);
+ SM_ASSERT(Mon_exec_time > 0);
+ tid = (sthread_t) sthread_get_id();
+ if (pthread_detach(tid) != 0)
+ {
+ /* log an error */
+ return (void *)1;
+ }
+
+/*
+** NOTE: this is "flow through" code,
+** do NOT use do { } while ("break" is used here!)
+*/
+
+#define MON_CHK_STOP \
+ now = time(NULL); \
+ end = Mon_cur_ctx->ctx_start + Mon_exec_time; \
+ if (now > end) \
+ { \
+ smi_log(SMI_LOG_ERR, \
+ "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
+ (long) now, (long) end, \
+ (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
+ mi_stop_milters(MILTER_STOP); \
+ break; \
+ }
+
+ (void) smutex_lock(&Mon_mutex);
+ while (mi_stop() == MILTER_CONT)
+ {
+ if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
+ {
+ struct timespec abstime;
+
+ MON_CHK_STOP;
+ abstime.tv_sec = end;
+ abstime.tv_nsec = 0;
+ r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
+ &abstime);
+ }
+ else
+ r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
+ if (mi_stop() != MILTER_CONT)
+ break;
+ if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
+ {
+ MON_CHK_STOP;
+ }
+ }
+ (void) smutex_unlock(&Mon_mutex);
+
+ return NULL;
+}
+
+/*
+** MI_MONITOR_INIT -- initialize monitoring thread
+**
+** Parameters: none
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_monitor_init()
+{
+ int r;
+ sthread_t tid;
+
+ SM_ASSERT(!Monitor);
+ if (Mon_exec_time <= 0)
+ return MI_SUCCESS;
+ Monitor = true;
+ if (!smutex_init(&Mon_mutex))
+ return MI_FAILURE;
+ if (scond_init(&Mon_cv) != 0)
+ return MI_FAILURE;
+ SM_TAILQ_INIT(&Mon_ctx_head);
+
+ r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
+ if (r != 0)
+ return r;
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MONITOR_WORK_BEGIN -- record start of thread execution
+**
+** Parameters:
+** ctx -- session context
+** cmd -- milter command char
+**
+** Returns:
+** 0
+*/
+
+int
+mi_monitor_work_begin(ctx, cmd)
+ SMFICTX_PTR ctx;
+ int cmd;
+{
+ (void) smutex_lock(&Mon_mutex);
+ if (NULL == Mon_cur_ctx)
+ {
+ Mon_cur_ctx = ctx;
+ (void) scond_signal(&Mon_cv);
+ }
+ ctx->ctx_start = time(NULL);
+ SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
+ (void) smutex_unlock(&Mon_mutex);
+ return 0;
+}
+
+/*
+** MI_MONITOR_WORK_END -- record end of thread execution
+**
+** Parameters:
+** ctx -- session context
+** cmd -- milter command char
+**
+** Returns:
+** 0
+*/
+
+int
+mi_monitor_work_end(ctx, cmd)
+ SMFICTX_PTR ctx;
+ int cmd;
+{
+ (void) smutex_lock(&Mon_mutex);
+ ctx->ctx_start = 0;
+ SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
+ if (Mon_cur_ctx == ctx)
+ {
+ if (SM_TAILQ_EMPTY(&Mon_ctx_head))
+ Mon_cur_ctx = NULL;
+ else
+ Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
+ }
+ (void) smutex_unlock(&Mon_mutex);
+ return 0;
+}
+#endif /* _FFR_THREAD_MONITOR */