1168515Sgshapiro/* 2261194Sgshapiro * Copyright (c) 2006 Proofpoint, Inc. and its suppliers. 3168515Sgshapiro * All rights reserved. 4168515Sgshapiro * 5168515Sgshapiro * By using this file, you agree to the terms and conditions set 6168515Sgshapiro * forth in the LICENSE file which can be found at the top level of 7168515Sgshapiro * the sendmail distribution. 8168515Sgshapiro * 9168515Sgshapiro */ 10168515Sgshapiro 11173340Sgshapiro#include <sm/gen.h> 12266527SgshapiroSM_RCSID("@(#)$Id: monitor.c,v 8.8 2013-11-22 20:51:36 ca Exp $") 13168515Sgshapiro#include "libmilter.h" 14168515Sgshapiro 15168515Sgshapiro#if _FFR_THREAD_MONITOR 16168515Sgshapiro 17168515Sgshapiro/* 18168515Sgshapiro** Thread Monitoring 19168515Sgshapiro** Todo: more error checking (return code from function calls) 20168515Sgshapiro** add comments. 21168515Sgshapiro*/ 22168515Sgshapiro 23168515Sgshapirobool Monitor = false; /* use monitoring? */ 24168515Sgshapirostatic unsigned int Mon_exec_time = 0; 25168515Sgshapiro 26168515Sgshapiro/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 27168515Sgshapirostatic smutex_t Mon_mutex; 28168515Sgshapirostatic scond_t Mon_cv; 29168515Sgshapiro 30168515Sgshapiro/* 31168515Sgshapiro** Current ctx to monitor. 32168515Sgshapiro** Invariant: 33168515Sgshapiro** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest 34168515Sgshapiro** time ago. 35168515Sgshapiro** 36168515Sgshapiro** Basically the entries in the list are ordered by time because new 37168515Sgshapiro** entries are appended at the end. However, due to the concurrent 38168515Sgshapiro** execution (multi-threaded) and no guaranteed order of wakeups 39168515Sgshapiro** after a mutex_lock() attempt, the order might not be strict, 40168515Sgshapiro** i.e., if the list contains e1 and e2 (in that order) then 41168515Sgshapiro** the the start time of e2 can be (slightly) smaller than that of e1. 42363466Sgshapiro** However, this slight inaccuracy should not matter for the proper 43168515Sgshapiro** working of this algorithm. 44168515Sgshapiro*/ 45168515Sgshapiro 46168515Sgshapirostatic SMFICTX_PTR Mon_cur_ctx = NULL; 47168515Sgshapirostatic smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */ 48168515Sgshapiro 49168515Sgshapiro/* 50168515Sgshapiro** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread 51168515Sgshapiro** 52168515Sgshapiro** Parameters: 53168515Sgshapiro** tm -- maximum execution time for a thread 54168515Sgshapiro** 55168515Sgshapiro** Returns: 56168515Sgshapiro** MI_SUCCESS 57168515Sgshapiro*/ 58168515Sgshapiro 59168515Sgshapiroint 60168515Sgshapirosmfi_set_max_exec_time(tm) 61168515Sgshapiro unsigned int tm; 62168515Sgshapiro{ 63168515Sgshapiro Mon_exec_time = tm; 64168515Sgshapiro return MI_SUCCESS; 65168515Sgshapiro} 66168515Sgshapiro 67168515Sgshapiro/* 68168515Sgshapiro** MI_MONITOR_THREAD -- monitoring thread 69168515Sgshapiro** 70168515Sgshapiro** Parameters: 71168515Sgshapiro** arg -- ignored (required by pthread_create()) 72168515Sgshapiro** 73168515Sgshapiro** Returns: 74168515Sgshapiro** NULL on termination. 75168515Sgshapiro*/ 76168515Sgshapiro 77168515Sgshapirostatic void * 78168515Sgshapiromi_monitor_thread(arg) 79168515Sgshapiro void *arg; 80168515Sgshapiro{ 81168515Sgshapiro sthread_t tid; 82168515Sgshapiro int r; 83168515Sgshapiro time_t now, end; 84168515Sgshapiro 85168515Sgshapiro SM_ASSERT(Monitor); 86168515Sgshapiro SM_ASSERT(Mon_exec_time > 0); 87168515Sgshapiro tid = (sthread_t) sthread_get_id(); 88168515Sgshapiro if (pthread_detach(tid) != 0) 89168515Sgshapiro { 90168515Sgshapiro /* log an error */ 91168515Sgshapiro return (void *)1; 92168515Sgshapiro } 93168515Sgshapiro 94168515Sgshapiro/* 95168515Sgshapiro** NOTE: this is "flow through" code, 96168515Sgshapiro** do NOT use do { } while ("break" is used here!) 97168515Sgshapiro*/ 98168515Sgshapiro 99168515Sgshapiro#define MON_CHK_STOP \ 100168515Sgshapiro now = time(NULL); \ 101168515Sgshapiro end = Mon_cur_ctx->ctx_start + Mon_exec_time; \ 102168515Sgshapiro if (now > end) \ 103168515Sgshapiro { \ 104168515Sgshapiro smi_log(SMI_LOG_ERR, \ 105168515Sgshapiro "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\ 106168515Sgshapiro (long) now, (long) end, \ 107168515Sgshapiro (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\ 108168515Sgshapiro mi_stop_milters(MILTER_STOP); \ 109168515Sgshapiro break; \ 110168515Sgshapiro } 111168515Sgshapiro 112168515Sgshapiro (void) smutex_lock(&Mon_mutex); 113168515Sgshapiro while (mi_stop() == MILTER_CONT) 114168515Sgshapiro { 115168515Sgshapiro if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 116168515Sgshapiro { 117168515Sgshapiro struct timespec abstime; 118168515Sgshapiro 119168515Sgshapiro MON_CHK_STOP; 120168515Sgshapiro abstime.tv_sec = end; 121168515Sgshapiro abstime.tv_nsec = 0; 122168515Sgshapiro r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex, 123168515Sgshapiro &abstime); 124168515Sgshapiro } 125168515Sgshapiro else 126168515Sgshapiro r = pthread_cond_wait(&Mon_cv, &Mon_mutex); 127168515Sgshapiro if (mi_stop() != MILTER_CONT) 128168515Sgshapiro break; 129168515Sgshapiro if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 130168515Sgshapiro { 131168515Sgshapiro MON_CHK_STOP; 132168515Sgshapiro } 133168515Sgshapiro } 134168515Sgshapiro (void) smutex_unlock(&Mon_mutex); 135168515Sgshapiro 136168515Sgshapiro return NULL; 137168515Sgshapiro} 138168515Sgshapiro 139168515Sgshapiro/* 140168515Sgshapiro** MI_MONITOR_INIT -- initialize monitoring thread 141168515Sgshapiro** 142168515Sgshapiro** Parameters: none 143168515Sgshapiro** 144168515Sgshapiro** Returns: 145168515Sgshapiro** MI_SUCCESS/MI_FAILURE 146168515Sgshapiro*/ 147168515Sgshapiro 148168515Sgshapiroint 149168515Sgshapiromi_monitor_init() 150168515Sgshapiro{ 151168515Sgshapiro int r; 152168515Sgshapiro sthread_t tid; 153168515Sgshapiro 154168515Sgshapiro SM_ASSERT(!Monitor); 155168515Sgshapiro if (Mon_exec_time <= 0) 156168515Sgshapiro return MI_SUCCESS; 157168515Sgshapiro Monitor = true; 158168515Sgshapiro if (!smutex_init(&Mon_mutex)) 159168515Sgshapiro return MI_FAILURE; 160168515Sgshapiro if (scond_init(&Mon_cv) != 0) 161168515Sgshapiro return MI_FAILURE; 162168515Sgshapiro SM_TAILQ_INIT(&Mon_ctx_head); 163168515Sgshapiro 164168515Sgshapiro r = thread_create(&tid, mi_monitor_thread, (void *)NULL); 165168515Sgshapiro if (r != 0) 166168515Sgshapiro return r; 167168515Sgshapiro return MI_SUCCESS; 168168515Sgshapiro} 169168515Sgshapiro 170168515Sgshapiro/* 171168515Sgshapiro** MI_MONITOR_WORK_BEGIN -- record start of thread execution 172168515Sgshapiro** 173168515Sgshapiro** Parameters: 174168515Sgshapiro** ctx -- session context 175168515Sgshapiro** cmd -- milter command char 176168515Sgshapiro** 177168515Sgshapiro** Returns: 178168515Sgshapiro** 0 179168515Sgshapiro*/ 180168515Sgshapiro 181168515Sgshapiroint 182168515Sgshapiromi_monitor_work_begin(ctx, cmd) 183168515Sgshapiro SMFICTX_PTR ctx; 184168515Sgshapiro int cmd; 185168515Sgshapiro{ 186168515Sgshapiro (void) smutex_lock(&Mon_mutex); 187168515Sgshapiro if (NULL == Mon_cur_ctx) 188168515Sgshapiro { 189168515Sgshapiro Mon_cur_ctx = ctx; 190168515Sgshapiro (void) scond_signal(&Mon_cv); 191168515Sgshapiro } 192168515Sgshapiro ctx->ctx_start = time(NULL); 193168515Sgshapiro SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link); 194168515Sgshapiro (void) smutex_unlock(&Mon_mutex); 195168515Sgshapiro return 0; 196168515Sgshapiro} 197168515Sgshapiro 198168515Sgshapiro/* 199168515Sgshapiro** MI_MONITOR_WORK_END -- record end of thread execution 200168515Sgshapiro** 201168515Sgshapiro** Parameters: 202168515Sgshapiro** ctx -- session context 203168515Sgshapiro** cmd -- milter command char 204168515Sgshapiro** 205168515Sgshapiro** Returns: 206168515Sgshapiro** 0 207168515Sgshapiro*/ 208168515Sgshapiro 209168515Sgshapiroint 210168515Sgshapiromi_monitor_work_end(ctx, cmd) 211168515Sgshapiro SMFICTX_PTR ctx; 212168515Sgshapiro int cmd; 213168515Sgshapiro{ 214168515Sgshapiro (void) smutex_lock(&Mon_mutex); 215168515Sgshapiro ctx->ctx_start = 0; 216168515Sgshapiro SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link); 217168515Sgshapiro if (Mon_cur_ctx == ctx) 218168515Sgshapiro { 219168515Sgshapiro if (SM_TAILQ_EMPTY(&Mon_ctx_head)) 220168515Sgshapiro Mon_cur_ctx = NULL; 221168515Sgshapiro else 222168515Sgshapiro Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head); 223168515Sgshapiro } 224168515Sgshapiro (void) smutex_unlock(&Mon_mutex); 225168515Sgshapiro return 0; 226168515Sgshapiro} 227168515Sgshapiro#endif /* _FFR_THREAD_MONITOR */ 228