monitor.c revision 173341
126219Swpaul/* 226219Swpaul * Copyright (c) 2006 Sendmail, Inc. and its suppliers. 326219Swpaul * All rights reserved. 426219Swpaul * 526219Swpaul * By using this file, you agree to the terms and conditions set 626219Swpaul * forth in the LICENSE file which can be found at the top level of 726219Swpaul * the sendmail distribution. 826219Swpaul * 926219Swpaul */ 1026219Swpaul 1126219Swpaul#include <sm/gen.h> 1226219SwpaulSM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $") 1326219Swpaul#include "libmilter.h" 1426219Swpaul 1526219Swpaul#if _FFR_THREAD_MONITOR 1626219Swpaul 1726219Swpaul/* 1826219Swpaul** Thread Monitoring 1926219Swpaul** Todo: more error checking (return code from function calls) 2026219Swpaul** add comments. 2126219Swpaul*/ 2226219Swpaul 2326219Swpaulbool Monitor = false; /* use monitoring? */ 2426219Swpaulstatic unsigned int Mon_exec_time = 0; 2526219Swpaul 2626219Swpaul/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 2726219Swpaulstatic smutex_t Mon_mutex; 2826219Swpaulstatic scond_t Mon_cv; 2965220Sache 3065220Sache/* 3126219Swpaul** Current ctx to monitor. 3226219Swpaul** Invariant: 3326219Swpaul** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest 3426219Swpaul** time ago. 3526219Swpaul** 3626219Swpaul** Basically the entries in the list are ordered by time because new 3726219Swpaul** entries are appended at the end. However, due to the concurrent 3826219Swpaul** execution (multi-threaded) and no guaranteed order of wakeups 3926219Swpaul** after a mutex_lock() attempt, the order might not be strict, 4026219Swpaul** i.e., if the list contains e1 and e2 (in that order) then 4174462Salfred** the the start time of e2 can be (slightly) smaller than that of e1. 4226219Swpaul** However, this slight inaccurracy should not matter for the proper 4326219Swpaul** working of this algorithm. 4426219Swpaul*/ 4526219Swpaul 4626219Swpaulstatic SMFICTX_PTR Mon_cur_ctx = NULL; 4726219Swpaulstatic smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */ 4826219Swpaul 4926219Swpaul/* 5026219Swpaul** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread 5126219Swpaul** 5226219Swpaul** Parameters: 5326219Swpaul** tm -- maximum execution time for a thread 5426219Swpaul** 5526219Swpaul** Returns: 5674462Salfred** MI_SUCCESS 5726219Swpaul*/ 5826219Swpaul 5926219Swpaulint 6026219Swpaulsmfi_set_max_exec_time(tm) 6126219Swpaul unsigned int tm; 6226219Swpaul{ 6326219Swpaul Mon_exec_time = tm; 6426219Swpaul return MI_SUCCESS; 6526219Swpaul} 6626219Swpaul 6726219Swpaul/* 6826219Swpaul** MI_MONITOR_THREAD -- monitoring thread 6926219Swpaul** 7026219Swpaul** Parameters: 7126219Swpaul** arg -- ignored (required by pthread_create()) 7226219Swpaul** 7326219Swpaul** Returns: 7426219Swpaul** NULL on termination. 7526219Swpaul*/ 7626219Swpaul 7726219Swpaulstatic void * 7826219Swpaulmi_monitor_thread(arg) 7926219Swpaul void *arg; 8026219Swpaul{ 8126219Swpaul sthread_t tid; 8226219Swpaul int r; 8337300Sbde time_t now, end; 8426219Swpaul 8526219Swpaul SM_ASSERT(Monitor); 8626219Swpaul SM_ASSERT(Mon_exec_time > 0); 8726219Swpaul tid = (sthread_t) sthread_get_id(); 8826219Swpaul if (pthread_detach(tid) != 0) 8926219Swpaul { 9026219Swpaul /* log an error */ 9126219Swpaul return (void *)1; 9265220Sache } 9365220Sache 9465220Sache/* 9526219Swpaul** NOTE: this is "flow through" code, 9626219Swpaul** do NOT use do { } while ("break" is used here!) 9765220Sache*/ 9865220Sache 9926219Swpaul#define MON_CHK_STOP \ 10026219Swpaul now = time(NULL); \ 10126219Swpaul end = Mon_cur_ctx->ctx_start + Mon_exec_time; \ 10265220Sache if (now > end) \ 10326219Swpaul { \ 10426219Swpaul smi_log(SMI_LOG_ERR, \ 10565220Sache "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\ 10626219Swpaul (long) now, (long) end, \ 10726219Swpaul (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\ 10826219Swpaul mi_stop_milters(MILTER_STOP); \ 10926219Swpaul break; \ 11026219Swpaul } 11126219Swpaul 11226219Swpaul (void) smutex_lock(&Mon_mutex); 11326219Swpaul while (mi_stop() == MILTER_CONT) 11426219Swpaul { 11526219Swpaul if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 11626219Swpaul { 11726219Swpaul struct timespec abstime; 11826219Swpaul 11926219Swpaul MON_CHK_STOP; 12026219Swpaul abstime.tv_sec = end; 12126219Swpaul abstime.tv_nsec = 0; 12226219Swpaul r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex, 12326219Swpaul &abstime); 12426219Swpaul } 12526219Swpaul else 12626219Swpaul r = pthread_cond_wait(&Mon_cv, &Mon_mutex); 12726219Swpaul if (mi_stop() != MILTER_CONT) 12826219Swpaul break; 12990271Salfred if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0) 13026219Swpaul { 13126219Swpaul MON_CHK_STOP; 13226219Swpaul } 13326219Swpaul } 13426219Swpaul (void) smutex_unlock(&Mon_mutex); 13526219Swpaul 13637300Sbde return NULL; 13726219Swpaul} 13837300Sbde 13937300Sbde/* 14026219Swpaul** MI_MONITOR_INIT -- initialize monitoring thread 14126219Swpaul** 14226219Swpaul** Parameters: none 14326219Swpaul** 14426219Swpaul** Returns: 14526219Swpaul** MI_SUCCESS/MI_FAILURE 14626219Swpaul*/ 14726219Swpaul 14826219Swpaulint 14926219Swpaulmi_monitor_init() 15026219Swpaul{ 15126219Swpaul int r; 15226219Swpaul sthread_t tid; 15326219Swpaul 15426219Swpaul SM_ASSERT(!Monitor); 15526219Swpaul if (Mon_exec_time <= 0) 15626219Swpaul return MI_SUCCESS; 15726219Swpaul Monitor = true; 15826219Swpaul if (!smutex_init(&Mon_mutex)) 15926219Swpaul return MI_FAILURE; 16092889Sobrien if (scond_init(&Mon_cv) != 0) 16192889Sobrien return MI_FAILURE; 16292889Sobrien SM_TAILQ_INIT(&Mon_ctx_head); 16326219Swpaul 16426219Swpaul r = thread_create(&tid, mi_monitor_thread, (void *)NULL); 16526219Swpaul if (r != 0) 16626219Swpaul return r; 16726219Swpaul return MI_SUCCESS; 16826219Swpaul} 16926219Swpaul 17026219Swpaul/* 17126219Swpaul** MI_MONITOR_WORK_BEGIN -- record start of thread execution 17226219Swpaul** 17326219Swpaul** Parameters: 17426219Swpaul** ctx -- session context 17526219Swpaul** cmd -- milter command char 17626219Swpaul** 17726219Swpaul** Returns: 17826219Swpaul** 0 17926219Swpaul*/ 18026219Swpaul 18126219Swpaulint 18226219Swpaulmi_monitor_work_begin(ctx, cmd) 18326219Swpaul SMFICTX_PTR ctx; 18426219Swpaul int cmd; 18526219Swpaul{ 18626219Swpaul (void) smutex_lock(&Mon_mutex); 18726219Swpaul if (NULL == Mon_cur_ctx) 18826219Swpaul { 18926219Swpaul Mon_cur_ctx = ctx; 19026219Swpaul (void) scond_signal(&Mon_cv); 19126219Swpaul } 19226219Swpaul ctx->ctx_start = time(NULL); 19326219Swpaul SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link); 19426219Swpaul (void) smutex_unlock(&Mon_mutex); 19526219Swpaul return 0; 19626219Swpaul} 19726219Swpaul 19826219Swpaul/* 19926219Swpaul** MI_MONITOR_WORK_END -- record end of thread execution 20026219Swpaul** 20126219Swpaul** Parameters: 20226219Swpaul** ctx -- session context 20326219Swpaul** cmd -- milter command char 20426219Swpaul** 20526219Swpaul** Returns: 20626219Swpaul** 0 20726219Swpaul*/ 20826219Swpaul 20926219Swpaulint 21026219Swpaulmi_monitor_work_end(ctx, cmd) 21126219Swpaul SMFICTX_PTR ctx; 21226219Swpaul int cmd; 21326219Swpaul{ 21426219Swpaul (void) smutex_lock(&Mon_mutex); 21526219Swpaul ctx->ctx_start = 0; 21626219Swpaul SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link); 21726219Swpaul if (Mon_cur_ctx == ctx) 21826219Swpaul { 21926219Swpaul if (SM_TAILQ_EMPTY(&Mon_ctx_head)) 22026219Swpaul Mon_cur_ctx = NULL; 22126219Swpaul else 22226219Swpaul Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head); 22326219Swpaul } 22426219Swpaul (void) smutex_unlock(&Mon_mutex); 22526219Swpaul return 0; 22626219Swpaul} 22726219Swpaul#endif /* _FFR_THREAD_MONITOR */ 22826219Swpaul