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