diff options
Diffstat (limited to 'gnu/usr.sbin/sendmail/libmilter/monitor.c')
-rw-r--r-- | gnu/usr.sbin/sendmail/libmilter/monitor.c | 225 |
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 */ |