1058561cbSjbeck /* 2058561cbSjbeck * Copyright (c) 2006 Sendmail, Inc. and its suppliers. 3058561cbSjbeck * All rights reserved. 4058561cbSjbeck * 5058561cbSjbeck * By using this file, you agree to the terms and conditions set 6058561cbSjbeck * forth in the LICENSE file which can be found at the top level of 7058561cbSjbeck * the sendmail distribution. 8058561cbSjbeck * 9058561cbSjbeck */ 10058561cbSjbeck 11058561cbSjbeck #pragma ident "%Z%%M% %I% %E% SMI" 12058561cbSjbeck 13*7800901eSjbeck #include <sm/gen.h> 14*7800901eSjbeck SM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $") 15058561cbSjbeck #include "libmilter.h" 16058561cbSjbeck 17058561cbSjbeck #if _FFR_THREAD_MONITOR 18058561cbSjbeck 19058561cbSjbeck /* 20058561cbSjbeck ** Thread Monitoring 21058561cbSjbeck ** Todo: more error checking (return code from function calls) 22058561cbSjbeck ** add comments. 23058561cbSjbeck */ 24058561cbSjbeck 25058561cbSjbeck bool Monitor = false; /* use monitoring? */ 26058561cbSjbeck static unsigned int Mon_exec_time = 0; 27058561cbSjbeck 28058561cbSjbeck /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 29058561cbSjbeck static smutex_t Mon_mutex; 30058561cbSjbeck static scond_t Mon_cv; 31058561cbSjbeck 32058561cbSjbeck /* 33058561cbSjbeck ** Current ctx to monitor. 34058561cbSjbeck ** Invariant: 35058561cbSjbeck ** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest 36058561cbSjbeck ** time ago. 37058561cbSjbeck ** 38058561cbSjbeck ** Basically the entries in the list are ordered by time because new 39058561cbSjbeck ** entries are appended at the end. However, due to the concurrent 40058561cbSjbeck ** execution (multi-threaded) and no guaranteed order of wakeups 41058561cbSjbeck ** after a mutex_lock() attempt, the order might not be strict, 42058561cbSjbeck ** i.e., if the list contains e1 and e2 (in that order) then 43058561cbSjbeck ** the the start time of e2 can be (slightly) smaller than that of e1. 44058561cbSjbeck ** However, this slight inaccurracy should not matter for the proper 45058561cbSjbeck ** working of this algorithm. 46058561cbSjbeck */ 47058561cbSjbeck 48058561cbSjbeck static SMFICTX_PTR Mon_cur_ctx = NULL; 49058561cbSjbeck static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */ 50058561cbSjbeck 51058561cbSjbeck /* 52058561cbSjbeck ** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread 53058561cbSjbeck ** 54058561cbSjbeck ** Parameters: 55058561cbSjbeck ** tm -- maximum execution time for a thread 56058561cbSjbeck ** 57058561cbSjbeck ** Returns: 58058561cbSjbeck ** MI_SUCCESS 59058561cbSjbeck */ 60058561cbSjbeck 61058561cbSjbeck int 62058561cbSjbeck smfi_set_max_exec_time(tm) 63058561cbSjbeck unsigned int tm; 64058561cbSjbeck { 65058561cbSjbeck Mon_exec_time = tm; 66058561cbSjbeck return MI_SUCCESS; 67058561cbSjbeck } 68058561cbSjbeck 69058561cbSjbeck /* 70058561cbSjbeck ** MI_MONITOR_THREAD -- monitoring thread 71058561cbSjbeck ** 72058561cbSjbeck ** Parameters: 73058561cbSjbeck ** arg -- ignored (required by pthread_create()) 74058561cbSjbeck ** 75058561cbSjbeck ** Returns: 76058561cbSjbeck ** NULL on termination. 77058561cbSjbeck */ 78058561cbSjbeck 79058561cbSjbeck static void * 80058561cbSjbeck mi_monitor_thread(arg) 81058561cbSjbeck void *arg; 82058561cbSjbeck { 83058561cbSjbeck sthread_t tid; 84058561cbSjbeck int r; 85058561cbSjbeck time_t now, end; 86058561cbSjbeck 87058561cbSjbeck SM_ASSERT(Monitor); 88058561cbSjbeck SM_ASSERT(Mon_exec_time > 0); 89058561cbSjbeck tid = (sthread_t) sthread_get_id(); 90058561cbSjbeck if (pthread_detach(tid) != 0) 91058561cbSjbeck { 92058561cbSjbeck /* log an error */ 93058561cbSjbeck return (void *)1; 94058561cbSjbeck } 95058561cbSjbeck 96058561cbSjbeck /* 97058561cbSjbeck ** NOTE: this is "flow through" code, 98058561cbSjbeck ** do NOT use do { } while ("break" is used here!) 99058561cbSjbeck */ 100058561cbSjbeck 101058561cbSjbeck #define MON_CHK_STOP \ 102058561cbSjbeck now = time(NULL); \ 103058561cbSjbeck end = Mon_cur_ctx->ctx_start + Mon_exec_time; \ 104058561cbSjbeck if (now > end) \ 105058561cbSjbeck { \ 106058561cbSjbeck smi_log(SMI_LOG_ERR, \ 107058561cbSjbeck "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\ 108058561cbSjbeck (long) now, (long) end, \ 109058561cbSjbeck (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\ 110058561cbSjbeck mi_stop_milters(MILTER_STOP); \ 111058561cbSjbeck break; \ 112058561cbSjbeck } 113058561cbSjbeck 114058561cbSjbeck (void) smutex_lock(&Mon_mutex); 115058561cbSjbeck while (mi_stop() == MILTER_CONT) 116058561cbSjbeck { 117058561cbSjbeck if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 118058561cbSjbeck { 119058561cbSjbeck struct timespec abstime; 120058561cbSjbeck 121058561cbSjbeck MON_CHK_STOP; 122058561cbSjbeck abstime.tv_sec = end; 123058561cbSjbeck abstime.tv_nsec = 0; 124058561cbSjbeck r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex, 125058561cbSjbeck &abstime); 126058561cbSjbeck } 127058561cbSjbeck else 128058561cbSjbeck r = pthread_cond_wait(&Mon_cv, &Mon_mutex); 129058561cbSjbeck if (mi_stop() != MILTER_CONT) 130058561cbSjbeck break; 131058561cbSjbeck if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 132058561cbSjbeck { 133058561cbSjbeck MON_CHK_STOP; 134058561cbSjbeck } 135058561cbSjbeck } 136058561cbSjbeck (void) smutex_unlock(&Mon_mutex); 137058561cbSjbeck 138058561cbSjbeck return NULL; 139058561cbSjbeck } 140058561cbSjbeck 141058561cbSjbeck /* 142058561cbSjbeck ** MI_MONITOR_INIT -- initialize monitoring thread 143058561cbSjbeck ** 144058561cbSjbeck ** Parameters: none 145058561cbSjbeck ** 146058561cbSjbeck ** Returns: 147058561cbSjbeck ** MI_SUCCESS/MI_FAILURE 148058561cbSjbeck */ 149058561cbSjbeck 150058561cbSjbeck int 151058561cbSjbeck mi_monitor_init() 152058561cbSjbeck { 153058561cbSjbeck int r; 154058561cbSjbeck sthread_t tid; 155058561cbSjbeck 156058561cbSjbeck SM_ASSERT(!Monitor); 157058561cbSjbeck if (Mon_exec_time <= 0) 158058561cbSjbeck return MI_SUCCESS; 159058561cbSjbeck Monitor = true; 160058561cbSjbeck if (!smutex_init(&Mon_mutex)) 161058561cbSjbeck return MI_FAILURE; 162058561cbSjbeck if (scond_init(&Mon_cv) != 0) 163058561cbSjbeck return MI_FAILURE; 164058561cbSjbeck SM_TAILQ_INIT(&Mon_ctx_head); 165058561cbSjbeck 166058561cbSjbeck r = thread_create(&tid, mi_monitor_thread, (void *)NULL); 167058561cbSjbeck if (r != 0) 168058561cbSjbeck return r; 169058561cbSjbeck return MI_SUCCESS; 170058561cbSjbeck } 171058561cbSjbeck 172058561cbSjbeck /* 173058561cbSjbeck ** MI_MONITOR_WORK_BEGIN -- record start of thread execution 174058561cbSjbeck ** 175058561cbSjbeck ** Parameters: 176058561cbSjbeck ** ctx -- session context 177058561cbSjbeck ** cmd -- milter command char 178058561cbSjbeck ** 179058561cbSjbeck ** Returns: 180058561cbSjbeck ** 0 181058561cbSjbeck */ 182058561cbSjbeck 183058561cbSjbeck int 184058561cbSjbeck mi_monitor_work_begin(ctx, cmd) 185058561cbSjbeck SMFICTX_PTR ctx; 186058561cbSjbeck int cmd; 187058561cbSjbeck { 188058561cbSjbeck (void) smutex_lock(&Mon_mutex); 189058561cbSjbeck if (NULL == Mon_cur_ctx) 190058561cbSjbeck { 191058561cbSjbeck Mon_cur_ctx = ctx; 192058561cbSjbeck (void) scond_signal(&Mon_cv); 193058561cbSjbeck } 194058561cbSjbeck ctx->ctx_start = time(NULL); 195058561cbSjbeck SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link); 196058561cbSjbeck (void) smutex_unlock(&Mon_mutex); 197058561cbSjbeck return 0; 198058561cbSjbeck } 199058561cbSjbeck 200058561cbSjbeck /* 201058561cbSjbeck ** MI_MONITOR_WORK_END -- record end of thread execution 202058561cbSjbeck ** 203058561cbSjbeck ** Parameters: 204058561cbSjbeck ** ctx -- session context 205058561cbSjbeck ** cmd -- milter command char 206058561cbSjbeck ** 207058561cbSjbeck ** Returns: 208058561cbSjbeck ** 0 209058561cbSjbeck */ 210058561cbSjbeck 211058561cbSjbeck int 212058561cbSjbeck mi_monitor_work_end(ctx, cmd) 213058561cbSjbeck SMFICTX_PTR ctx; 214058561cbSjbeck int cmd; 215058561cbSjbeck { 216058561cbSjbeck (void) smutex_lock(&Mon_mutex); 217058561cbSjbeck ctx->ctx_start = 0; 218058561cbSjbeck SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link); 219058561cbSjbeck if (Mon_cur_ctx == ctx) 220058561cbSjbeck { 221058561cbSjbeck if (SM_TAILQ_EMPTY(&Mon_ctx_head)) 222058561cbSjbeck Mon_cur_ctx = NULL; 223058561cbSjbeck else 224058561cbSjbeck Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head); 225058561cbSjbeck } 226058561cbSjbeck (void) smutex_unlock(&Mon_mutex); 227058561cbSjbeck return 0; 228058561cbSjbeck } 229058561cbSjbeck #endif /* _FFR_THREAD_MONITOR */ 230