xref: /onnv-gate/usr/src/cmd/sendmail/libmilter/monitor.c (revision 5402:a17d5e4e8666)
13544Sjbeck /*
23544Sjbeck  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
33544Sjbeck  *	All rights reserved.
43544Sjbeck  *
53544Sjbeck  * By using this file, you agree to the terms and conditions set
63544Sjbeck  * forth in the LICENSE file which can be found at the top level of
73544Sjbeck  * the sendmail distribution.
83544Sjbeck  *
93544Sjbeck  */
103544Sjbeck 
113544Sjbeck #pragma ident	"%Z%%M%	%I%	%E% SMI"
123544Sjbeck 
13*5402Sjbeck #include <sm/gen.h>
14*5402Sjbeck SM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $")
153544Sjbeck #include "libmilter.h"
163544Sjbeck 
173544Sjbeck #if _FFR_THREAD_MONITOR
183544Sjbeck 
193544Sjbeck /*
203544Sjbeck **  Thread Monitoring
213544Sjbeck **  Todo: more error checking (return code from function calls)
223544Sjbeck **  add comments.
233544Sjbeck */
243544Sjbeck 
253544Sjbeck bool Monitor = false; /* use monitoring? */
263544Sjbeck static unsigned int Mon_exec_time = 0;
273544Sjbeck 
283544Sjbeck /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
293544Sjbeck static smutex_t Mon_mutex;
303544Sjbeck static scond_t Mon_cv;
313544Sjbeck 
323544Sjbeck /*
333544Sjbeck **  Current ctx to monitor.
343544Sjbeck **  Invariant:
353544Sjbeck **  Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
363544Sjbeck **	time ago.
373544Sjbeck **
383544Sjbeck **  Basically the entries in the list are ordered by time because new
393544Sjbeck **	entries are appended at the end. However, due to the concurrent
403544Sjbeck **	execution (multi-threaded) and no guaranteed order of wakeups
413544Sjbeck **	after a mutex_lock() attempt, the order might not be strict,
423544Sjbeck **	i.e., if the list contains e1 and e2 (in that order) then
433544Sjbeck **	the the start time of e2 can be (slightly) smaller than that of e1.
443544Sjbeck **	However, this slight inaccurracy should not matter for the proper
453544Sjbeck **	working of this algorithm.
463544Sjbeck */
473544Sjbeck 
483544Sjbeck static SMFICTX_PTR Mon_cur_ctx = NULL;
493544Sjbeck static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
503544Sjbeck 
513544Sjbeck /*
523544Sjbeck **  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
533544Sjbeck **
543544Sjbeck **	Parameters:
553544Sjbeck **		tm -- maximum execution time for a thread
563544Sjbeck **
573544Sjbeck **	Returns:
583544Sjbeck **		MI_SUCCESS
593544Sjbeck */
603544Sjbeck 
613544Sjbeck int
smfi_set_max_exec_time(tm)623544Sjbeck smfi_set_max_exec_time(tm)
633544Sjbeck 	unsigned int tm;
643544Sjbeck {
653544Sjbeck 	Mon_exec_time = tm;
663544Sjbeck 	return MI_SUCCESS;
673544Sjbeck }
683544Sjbeck 
693544Sjbeck /*
703544Sjbeck **  MI_MONITOR_THREAD -- monitoring thread
713544Sjbeck **
723544Sjbeck **	Parameters:
733544Sjbeck **		arg -- ignored (required by pthread_create())
743544Sjbeck **
753544Sjbeck **	Returns:
763544Sjbeck **		NULL on termination.
773544Sjbeck */
783544Sjbeck 
793544Sjbeck static void *
mi_monitor_thread(arg)803544Sjbeck mi_monitor_thread(arg)
813544Sjbeck 	void *arg;
823544Sjbeck {
833544Sjbeck 	sthread_t tid;
843544Sjbeck 	int r;
853544Sjbeck 	time_t now, end;
863544Sjbeck 
873544Sjbeck 	SM_ASSERT(Monitor);
883544Sjbeck 	SM_ASSERT(Mon_exec_time > 0);
893544Sjbeck 	tid = (sthread_t) sthread_get_id();
903544Sjbeck 	if (pthread_detach(tid) != 0)
913544Sjbeck 	{
923544Sjbeck 		/* log an error */
933544Sjbeck 		return (void *)1;
943544Sjbeck 	}
953544Sjbeck 
963544Sjbeck /*
973544Sjbeck **  NOTE: this is "flow through" code,
983544Sjbeck **  do NOT use do { } while ("break" is used here!)
993544Sjbeck */
1003544Sjbeck 
1013544Sjbeck #define MON_CHK_STOP							\
1023544Sjbeck 	now = time(NULL);						\
1033544Sjbeck 	end = Mon_cur_ctx->ctx_start + Mon_exec_time;			\
1043544Sjbeck 	if (now > end)							\
1053544Sjbeck 	{								\
1063544Sjbeck 		smi_log(SMI_LOG_ERR,					\
1073544Sjbeck 			"WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
1083544Sjbeck 			(long) now, (long) end,				\
1093544Sjbeck 			(long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
1103544Sjbeck 		mi_stop_milters(MILTER_STOP);				\
1113544Sjbeck 		break;							\
1123544Sjbeck 	}
1133544Sjbeck 
1143544Sjbeck 	(void) smutex_lock(&Mon_mutex);
1153544Sjbeck 	while (mi_stop() == MILTER_CONT)
1163544Sjbeck 	{
1173544Sjbeck 		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
1183544Sjbeck 		{
1193544Sjbeck 			struct timespec abstime;
1203544Sjbeck 
1213544Sjbeck 			MON_CHK_STOP;
1223544Sjbeck 			abstime.tv_sec = end;
1233544Sjbeck 			abstime.tv_nsec = 0;
1243544Sjbeck 			r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
1253544Sjbeck 					&abstime);
1263544Sjbeck 		}
1273544Sjbeck 		else
1283544Sjbeck 			r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
1293544Sjbeck 		if (mi_stop() != MILTER_CONT)
1303544Sjbeck 			break;
1313544Sjbeck 		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
1323544Sjbeck 		{
1333544Sjbeck 			MON_CHK_STOP;
1343544Sjbeck 		}
1353544Sjbeck 	}
1363544Sjbeck 	(void) smutex_unlock(&Mon_mutex);
1373544Sjbeck 
1383544Sjbeck 	return NULL;
1393544Sjbeck }
1403544Sjbeck 
1413544Sjbeck /*
1423544Sjbeck **  MI_MONITOR_INIT -- initialize monitoring thread
1433544Sjbeck **
1443544Sjbeck **	Parameters: none
1453544Sjbeck **
1463544Sjbeck **	Returns:
1473544Sjbeck **		MI_SUCCESS/MI_FAILURE
1483544Sjbeck */
1493544Sjbeck 
1503544Sjbeck int
mi_monitor_init()1513544Sjbeck mi_monitor_init()
1523544Sjbeck {
1533544Sjbeck 	int r;
1543544Sjbeck 	sthread_t tid;
1553544Sjbeck 
1563544Sjbeck 	SM_ASSERT(!Monitor);
1573544Sjbeck 	if (Mon_exec_time <= 0)
1583544Sjbeck 		return MI_SUCCESS;
1593544Sjbeck 	Monitor = true;
1603544Sjbeck 	if (!smutex_init(&Mon_mutex))
1613544Sjbeck 		return MI_FAILURE;
1623544Sjbeck 	if (scond_init(&Mon_cv) != 0)
1633544Sjbeck 		return MI_FAILURE;
1643544Sjbeck 	SM_TAILQ_INIT(&Mon_ctx_head);
1653544Sjbeck 
1663544Sjbeck 	r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
1673544Sjbeck 	if (r != 0)
1683544Sjbeck 		return r;
1693544Sjbeck 	return MI_SUCCESS;
1703544Sjbeck }
1713544Sjbeck 
1723544Sjbeck /*
1733544Sjbeck **  MI_MONITOR_WORK_BEGIN -- record start of thread execution
1743544Sjbeck **
1753544Sjbeck **	Parameters:
1763544Sjbeck **		ctx -- session context
1773544Sjbeck **		cmd -- milter command char
1783544Sjbeck **
1793544Sjbeck **	Returns:
1803544Sjbeck **		0
1813544Sjbeck */
1823544Sjbeck 
1833544Sjbeck int
mi_monitor_work_begin(ctx,cmd)1843544Sjbeck mi_monitor_work_begin(ctx, cmd)
1853544Sjbeck 	SMFICTX_PTR ctx;
1863544Sjbeck 	int cmd;
1873544Sjbeck {
1883544Sjbeck 	(void) smutex_lock(&Mon_mutex);
1893544Sjbeck 	if (NULL == Mon_cur_ctx)
1903544Sjbeck 	{
1913544Sjbeck 		Mon_cur_ctx = ctx;
1923544Sjbeck 		(void) scond_signal(&Mon_cv);
1933544Sjbeck 	}
1943544Sjbeck 	ctx->ctx_start = time(NULL);
1953544Sjbeck 	SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
1963544Sjbeck 	(void) smutex_unlock(&Mon_mutex);
1973544Sjbeck 	return 0;
1983544Sjbeck }
1993544Sjbeck 
2003544Sjbeck /*
2013544Sjbeck **  MI_MONITOR_WORK_END -- record end of thread execution
2023544Sjbeck **
2033544Sjbeck **	Parameters:
2043544Sjbeck **		ctx -- session context
2053544Sjbeck **		cmd -- milter command char
2063544Sjbeck **
2073544Sjbeck **	Returns:
2083544Sjbeck **		0
2093544Sjbeck */
2103544Sjbeck 
2113544Sjbeck int
mi_monitor_work_end(ctx,cmd)2123544Sjbeck mi_monitor_work_end(ctx, cmd)
2133544Sjbeck 	SMFICTX_PTR ctx;
2143544Sjbeck 	int cmd;
2153544Sjbeck {
2163544Sjbeck 	(void) smutex_lock(&Mon_mutex);
2173544Sjbeck 	ctx->ctx_start = 0;
2183544Sjbeck 	SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
2193544Sjbeck 	if (Mon_cur_ctx == ctx)
2203544Sjbeck 	{
2213544Sjbeck 		if (SM_TAILQ_EMPTY(&Mon_ctx_head))
2223544Sjbeck 			Mon_cur_ctx = NULL;
2233544Sjbeck 		else
2243544Sjbeck 			Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
2253544Sjbeck 	}
2263544Sjbeck 	(void) smutex_unlock(&Mon_mutex);
2273544Sjbeck 	return 0;
2283544Sjbeck }
2293544Sjbeck #endif /* _FFR_THREAD_MONITOR */
230