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 "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 23 bool Monitor = false; /* use monitoring? */ 24 static unsigned int Mon_exec_time = 0; 25 26 /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */ 27 static smutex_t Mon_mutex; 28 static 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 inaccurracy should not matter for the proper 43 ** working of this algorithm. 44 */ 45 46 static SMFICTX_PTR Mon_cur_ctx = NULL; 47 static 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 59 int 60 smfi_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 77 static void * 78 mi_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 148 int 149 mi_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 181 int 182 mi_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 209 int 210 mi_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