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