1d0cef73dSGregory Neil Shapiro /* 2*5dd76dd0SGregory Neil Shapiro * Copyright (c) 2006 Proofpoint, Inc. and its suppliers. 3d0cef73dSGregory Neil Shapiro * All rights reserved. 4d0cef73dSGregory Neil Shapiro * 5d0cef73dSGregory Neil Shapiro * By using this file, you agree to the terms and conditions set 6d0cef73dSGregory Neil Shapiro * forth in the LICENSE file which can be found at the top level of 7d0cef73dSGregory Neil Shapiro * the sendmail distribution. 8d0cef73dSGregory Neil Shapiro * 9d0cef73dSGregory Neil Shapiro */ 10d0cef73dSGregory Neil Shapiro 11ffb83623SGregory Neil Shapiro #include <sm/gen.h> 12*5dd76dd0SGregory Neil Shapiro SM_RCSID("@(#)$Id: monitor.c,v 8.8 2013/11/22 20:51:36 ca Exp $") 13d0cef73dSGregory Neil Shapiro #include "libmilter.h" 14d0cef73dSGregory Neil Shapiro 15d0cef73dSGregory Neil Shapiro #if _FFR_THREAD_MONITOR 16d0cef73dSGregory Neil Shapiro 17d0cef73dSGregory Neil Shapiro /* 18d0cef73dSGregory Neil Shapiro ** Thread Monitoring 19d0cef73dSGregory Neil Shapiro ** Todo: more error checking (return code from function calls) 20d0cef73dSGregory Neil Shapiro ** add comments. 21d0cef73dSGregory Neil Shapiro */ 22d0cef73dSGregory Neil Shapiro 23d0cef73dSGregory Neil Shapiro bool Monitor = false; /* use monitoring? */ 24d0cef73dSGregory Neil Shapiro static unsigned int Mon_exec_time = 0; 25d0cef73dSGregory Neil Shapiro 26d0cef73dSGregory Neil Shapiro /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 27d0cef73dSGregory Neil Shapiro static smutex_t Mon_mutex; 28d0cef73dSGregory Neil Shapiro static scond_t Mon_cv; 29d0cef73dSGregory Neil Shapiro 30d0cef73dSGregory Neil Shapiro /* 31d0cef73dSGregory Neil Shapiro ** Current ctx to monitor. 32d0cef73dSGregory Neil Shapiro ** Invariant: 33d0cef73dSGregory Neil Shapiro ** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest 34d0cef73dSGregory Neil Shapiro ** time ago. 35d0cef73dSGregory Neil Shapiro ** 36d0cef73dSGregory Neil Shapiro ** Basically the entries in the list are ordered by time because new 37d0cef73dSGregory Neil Shapiro ** entries are appended at the end. However, due to the concurrent 38d0cef73dSGregory Neil Shapiro ** execution (multi-threaded) and no guaranteed order of wakeups 39d0cef73dSGregory Neil Shapiro ** after a mutex_lock() attempt, the order might not be strict, 40d0cef73dSGregory Neil Shapiro ** i.e., if the list contains e1 and e2 (in that order) then 41d0cef73dSGregory Neil Shapiro ** the the start time of e2 can be (slightly) smaller than that of e1. 42d0cef73dSGregory Neil Shapiro ** However, this slight inaccurracy should not matter for the proper 43d0cef73dSGregory Neil Shapiro ** working of this algorithm. 44d0cef73dSGregory Neil Shapiro */ 45d0cef73dSGregory Neil Shapiro 46d0cef73dSGregory Neil Shapiro static SMFICTX_PTR Mon_cur_ctx = NULL; 47d0cef73dSGregory Neil Shapiro static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */ 48d0cef73dSGregory Neil Shapiro 49d0cef73dSGregory Neil Shapiro /* 50d0cef73dSGregory Neil Shapiro ** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread 51d0cef73dSGregory Neil Shapiro ** 52d0cef73dSGregory Neil Shapiro ** Parameters: 53d0cef73dSGregory Neil Shapiro ** tm -- maximum execution time for a thread 54d0cef73dSGregory Neil Shapiro ** 55d0cef73dSGregory Neil Shapiro ** Returns: 56d0cef73dSGregory Neil Shapiro ** MI_SUCCESS 57d0cef73dSGregory Neil Shapiro */ 58d0cef73dSGregory Neil Shapiro 59d0cef73dSGregory Neil Shapiro int 60d0cef73dSGregory Neil Shapiro smfi_set_max_exec_time(tm) 61d0cef73dSGregory Neil Shapiro unsigned int tm; 62d0cef73dSGregory Neil Shapiro { 63d0cef73dSGregory Neil Shapiro Mon_exec_time = tm; 64d0cef73dSGregory Neil Shapiro return MI_SUCCESS; 65d0cef73dSGregory Neil Shapiro } 66d0cef73dSGregory Neil Shapiro 67d0cef73dSGregory Neil Shapiro /* 68d0cef73dSGregory Neil Shapiro ** MI_MONITOR_THREAD -- monitoring thread 69d0cef73dSGregory Neil Shapiro ** 70d0cef73dSGregory Neil Shapiro ** Parameters: 71d0cef73dSGregory Neil Shapiro ** arg -- ignored (required by pthread_create()) 72d0cef73dSGregory Neil Shapiro ** 73d0cef73dSGregory Neil Shapiro ** Returns: 74d0cef73dSGregory Neil Shapiro ** NULL on termination. 75d0cef73dSGregory Neil Shapiro */ 76d0cef73dSGregory Neil Shapiro 77d0cef73dSGregory Neil Shapiro static void * 78d0cef73dSGregory Neil Shapiro mi_monitor_thread(arg) 79d0cef73dSGregory Neil Shapiro void *arg; 80d0cef73dSGregory Neil Shapiro { 81d0cef73dSGregory Neil Shapiro sthread_t tid; 82d0cef73dSGregory Neil Shapiro int r; 83d0cef73dSGregory Neil Shapiro time_t now, end; 84d0cef73dSGregory Neil Shapiro 85d0cef73dSGregory Neil Shapiro SM_ASSERT(Monitor); 86d0cef73dSGregory Neil Shapiro SM_ASSERT(Mon_exec_time > 0); 87d0cef73dSGregory Neil Shapiro tid = (sthread_t) sthread_get_id(); 88d0cef73dSGregory Neil Shapiro if (pthread_detach(tid) != 0) 89d0cef73dSGregory Neil Shapiro { 90d0cef73dSGregory Neil Shapiro /* log an error */ 91d0cef73dSGregory Neil Shapiro return (void *)1; 92d0cef73dSGregory Neil Shapiro } 93d0cef73dSGregory Neil Shapiro 94d0cef73dSGregory Neil Shapiro /* 95d0cef73dSGregory Neil Shapiro ** NOTE: this is "flow through" code, 96d0cef73dSGregory Neil Shapiro ** do NOT use do { } while ("break" is used here!) 97d0cef73dSGregory Neil Shapiro */ 98d0cef73dSGregory Neil Shapiro 99d0cef73dSGregory Neil Shapiro #define MON_CHK_STOP \ 100d0cef73dSGregory Neil Shapiro now = time(NULL); \ 101d0cef73dSGregory Neil Shapiro end = Mon_cur_ctx->ctx_start + Mon_exec_time; \ 102d0cef73dSGregory Neil Shapiro if (now > end) \ 103d0cef73dSGregory Neil Shapiro { \ 104d0cef73dSGregory Neil Shapiro smi_log(SMI_LOG_ERR, \ 105d0cef73dSGregory Neil Shapiro "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\ 106d0cef73dSGregory Neil Shapiro (long) now, (long) end, \ 107d0cef73dSGregory Neil Shapiro (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\ 108d0cef73dSGregory Neil Shapiro mi_stop_milters(MILTER_STOP); \ 109d0cef73dSGregory Neil Shapiro break; \ 110d0cef73dSGregory Neil Shapiro } 111d0cef73dSGregory Neil Shapiro 112d0cef73dSGregory Neil Shapiro (void) smutex_lock(&Mon_mutex); 113d0cef73dSGregory Neil Shapiro while (mi_stop() == MILTER_CONT) 114d0cef73dSGregory Neil Shapiro { 115d0cef73dSGregory Neil Shapiro if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 116d0cef73dSGregory Neil Shapiro { 117d0cef73dSGregory Neil Shapiro struct timespec abstime; 118d0cef73dSGregory Neil Shapiro 119d0cef73dSGregory Neil Shapiro MON_CHK_STOP; 120d0cef73dSGregory Neil Shapiro abstime.tv_sec = end; 121d0cef73dSGregory Neil Shapiro abstime.tv_nsec = 0; 122d0cef73dSGregory Neil Shapiro r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex, 123d0cef73dSGregory Neil Shapiro &abstime); 124d0cef73dSGregory Neil Shapiro } 125d0cef73dSGregory Neil Shapiro else 126d0cef73dSGregory Neil Shapiro r = pthread_cond_wait(&Mon_cv, &Mon_mutex); 127d0cef73dSGregory Neil Shapiro if (mi_stop() != MILTER_CONT) 128d0cef73dSGregory Neil Shapiro break; 129d0cef73dSGregory Neil Shapiro if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 130d0cef73dSGregory Neil Shapiro { 131d0cef73dSGregory Neil Shapiro MON_CHK_STOP; 132d0cef73dSGregory Neil Shapiro } 133d0cef73dSGregory Neil Shapiro } 134d0cef73dSGregory Neil Shapiro (void) smutex_unlock(&Mon_mutex); 135d0cef73dSGregory Neil Shapiro 136d0cef73dSGregory Neil Shapiro return NULL; 137d0cef73dSGregory Neil Shapiro } 138d0cef73dSGregory Neil Shapiro 139d0cef73dSGregory Neil Shapiro /* 140d0cef73dSGregory Neil Shapiro ** MI_MONITOR_INIT -- initialize monitoring thread 141d0cef73dSGregory Neil Shapiro ** 142d0cef73dSGregory Neil Shapiro ** Parameters: none 143d0cef73dSGregory Neil Shapiro ** 144d0cef73dSGregory Neil Shapiro ** Returns: 145d0cef73dSGregory Neil Shapiro ** MI_SUCCESS/MI_FAILURE 146d0cef73dSGregory Neil Shapiro */ 147d0cef73dSGregory Neil Shapiro 148d0cef73dSGregory Neil Shapiro int 149d0cef73dSGregory Neil Shapiro mi_monitor_init() 150d0cef73dSGregory Neil Shapiro { 151d0cef73dSGregory Neil Shapiro int r; 152d0cef73dSGregory Neil Shapiro sthread_t tid; 153d0cef73dSGregory Neil Shapiro 154d0cef73dSGregory Neil Shapiro SM_ASSERT(!Monitor); 155d0cef73dSGregory Neil Shapiro if (Mon_exec_time <= 0) 156d0cef73dSGregory Neil Shapiro return MI_SUCCESS; 157d0cef73dSGregory Neil Shapiro Monitor = true; 158d0cef73dSGregory Neil Shapiro if (!smutex_init(&Mon_mutex)) 159d0cef73dSGregory Neil Shapiro return MI_FAILURE; 160d0cef73dSGregory Neil Shapiro if (scond_init(&Mon_cv) != 0) 161d0cef73dSGregory Neil Shapiro return MI_FAILURE; 162d0cef73dSGregory Neil Shapiro SM_TAILQ_INIT(&Mon_ctx_head); 163d0cef73dSGregory Neil Shapiro 164d0cef73dSGregory Neil Shapiro r = thread_create(&tid, mi_monitor_thread, (void *)NULL); 165d0cef73dSGregory Neil Shapiro if (r != 0) 166d0cef73dSGregory Neil Shapiro return r; 167d0cef73dSGregory Neil Shapiro return MI_SUCCESS; 168d0cef73dSGregory Neil Shapiro } 169d0cef73dSGregory Neil Shapiro 170d0cef73dSGregory Neil Shapiro /* 171d0cef73dSGregory Neil Shapiro ** MI_MONITOR_WORK_BEGIN -- record start of thread execution 172d0cef73dSGregory Neil Shapiro ** 173d0cef73dSGregory Neil Shapiro ** Parameters: 174d0cef73dSGregory Neil Shapiro ** ctx -- session context 175d0cef73dSGregory Neil Shapiro ** cmd -- milter command char 176d0cef73dSGregory Neil Shapiro ** 177d0cef73dSGregory Neil Shapiro ** Returns: 178d0cef73dSGregory Neil Shapiro ** 0 179d0cef73dSGregory Neil Shapiro */ 180d0cef73dSGregory Neil Shapiro 181d0cef73dSGregory Neil Shapiro int 182d0cef73dSGregory Neil Shapiro mi_monitor_work_begin(ctx, cmd) 183d0cef73dSGregory Neil Shapiro SMFICTX_PTR ctx; 184d0cef73dSGregory Neil Shapiro int cmd; 185d0cef73dSGregory Neil Shapiro { 186d0cef73dSGregory Neil Shapiro (void) smutex_lock(&Mon_mutex); 187d0cef73dSGregory Neil Shapiro if (NULL == Mon_cur_ctx) 188d0cef73dSGregory Neil Shapiro { 189d0cef73dSGregory Neil Shapiro Mon_cur_ctx = ctx; 190d0cef73dSGregory Neil Shapiro (void) scond_signal(&Mon_cv); 191d0cef73dSGregory Neil Shapiro } 192d0cef73dSGregory Neil Shapiro ctx->ctx_start = time(NULL); 193d0cef73dSGregory Neil Shapiro SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link); 194d0cef73dSGregory Neil Shapiro (void) smutex_unlock(&Mon_mutex); 195d0cef73dSGregory Neil Shapiro return 0; 196d0cef73dSGregory Neil Shapiro } 197d0cef73dSGregory Neil Shapiro 198d0cef73dSGregory Neil Shapiro /* 199d0cef73dSGregory Neil Shapiro ** MI_MONITOR_WORK_END -- record end of thread execution 200d0cef73dSGregory Neil Shapiro ** 201d0cef73dSGregory Neil Shapiro ** Parameters: 202d0cef73dSGregory Neil Shapiro ** ctx -- session context 203d0cef73dSGregory Neil Shapiro ** cmd -- milter command char 204d0cef73dSGregory Neil Shapiro ** 205d0cef73dSGregory Neil Shapiro ** Returns: 206d0cef73dSGregory Neil Shapiro ** 0 207d0cef73dSGregory Neil Shapiro */ 208d0cef73dSGregory Neil Shapiro 209d0cef73dSGregory Neil Shapiro int 210d0cef73dSGregory Neil Shapiro mi_monitor_work_end(ctx, cmd) 211d0cef73dSGregory Neil Shapiro SMFICTX_PTR ctx; 212d0cef73dSGregory Neil Shapiro int cmd; 213d0cef73dSGregory Neil Shapiro { 214d0cef73dSGregory Neil Shapiro (void) smutex_lock(&Mon_mutex); 215d0cef73dSGregory Neil Shapiro ctx->ctx_start = 0; 216d0cef73dSGregory Neil Shapiro SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link); 217d0cef73dSGregory Neil Shapiro if (Mon_cur_ctx == ctx) 218d0cef73dSGregory Neil Shapiro { 219d0cef73dSGregory Neil Shapiro if (SM_TAILQ_EMPTY(&Mon_ctx_head)) 220d0cef73dSGregory Neil Shapiro Mon_cur_ctx = NULL; 221d0cef73dSGregory Neil Shapiro else 222d0cef73dSGregory Neil Shapiro Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head); 223d0cef73dSGregory Neil Shapiro } 224d0cef73dSGregory Neil Shapiro (void) smutex_unlock(&Mon_mutex); 225d0cef73dSGregory Neil Shapiro return 0; 226d0cef73dSGregory Neil Shapiro } 227d0cef73dSGregory Neil Shapiro #endif /* _FFR_THREAD_MONITOR */ 228