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> 14 SM_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 25 bool Monitor = false; /* use monitoring? */ 26 static unsigned int Mon_exec_time = 0; 27 28 /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 29 static smutex_t Mon_mutex; 30 static 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 48 static SMFICTX_PTR Mon_cur_ctx = NULL; 49 static 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 61 int 62 smfi_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 79 static void * 80 mi_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 150 int 151 mi_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 183 int 184 mi_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 211 int 212 mi_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