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