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