Home | History | Annotate | Download | only in libmilter
      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