monitor.c revision 173341
126219Swpaul/*
226219Swpaul *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
326219Swpaul *	All rights reserved.
426219Swpaul *
526219Swpaul * By using this file, you agree to the terms and conditions set
626219Swpaul * forth in the LICENSE file which can be found at the top level of
726219Swpaul * the sendmail distribution.
826219Swpaul *
926219Swpaul */
1026219Swpaul
1126219Swpaul#include <sm/gen.h>
1226219SwpaulSM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $")
1326219Swpaul#include "libmilter.h"
1426219Swpaul
1526219Swpaul#if _FFR_THREAD_MONITOR
1626219Swpaul
1726219Swpaul/*
1826219Swpaul**  Thread Monitoring
1926219Swpaul**  Todo: more error checking (return code from function calls)
2026219Swpaul**  add comments.
2126219Swpaul*/
2226219Swpaul
2326219Swpaulbool Monitor = false; /* use monitoring? */
2426219Swpaulstatic unsigned int Mon_exec_time = 0;
2526219Swpaul
2626219Swpaul/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
2726219Swpaulstatic smutex_t Mon_mutex;
2826219Swpaulstatic scond_t Mon_cv;
2965220Sache
3065220Sache/*
3126219Swpaul**  Current ctx to monitor.
3226219Swpaul**  Invariant:
3326219Swpaul**  Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
3426219Swpaul**	time ago.
3526219Swpaul**
3626219Swpaul**  Basically the entries in the list are ordered by time because new
3726219Swpaul**	entries are appended at the end. However, due to the concurrent
3826219Swpaul**	execution (multi-threaded) and no guaranteed order of wakeups
3926219Swpaul**	after a mutex_lock() attempt, the order might not be strict,
4026219Swpaul**	i.e., if the list contains e1 and e2 (in that order) then
4174462Salfred**	the the start time of e2 can be (slightly) smaller than that of e1.
4226219Swpaul**	However, this slight inaccurracy should not matter for the proper
4326219Swpaul**	working of this algorithm.
4426219Swpaul*/
4526219Swpaul
4626219Swpaulstatic SMFICTX_PTR Mon_cur_ctx = NULL;
4726219Swpaulstatic smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
4826219Swpaul
4926219Swpaul/*
5026219Swpaul**  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
5126219Swpaul**
5226219Swpaul**	Parameters:
5326219Swpaul**		tm -- maximum execution time for a thread
5426219Swpaul**
5526219Swpaul**	Returns:
5674462Salfred**		MI_SUCCESS
5726219Swpaul*/
5826219Swpaul
5926219Swpaulint
6026219Swpaulsmfi_set_max_exec_time(tm)
6126219Swpaul	unsigned int tm;
6226219Swpaul{
6326219Swpaul	Mon_exec_time = tm;
6426219Swpaul	return MI_SUCCESS;
6526219Swpaul}
6626219Swpaul
6726219Swpaul/*
6826219Swpaul**  MI_MONITOR_THREAD -- monitoring thread
6926219Swpaul**
7026219Swpaul**	Parameters:
7126219Swpaul**		arg -- ignored (required by pthread_create())
7226219Swpaul**
7326219Swpaul**	Returns:
7426219Swpaul**		NULL on termination.
7526219Swpaul*/
7626219Swpaul
7726219Swpaulstatic void *
7826219Swpaulmi_monitor_thread(arg)
7926219Swpaul	void *arg;
8026219Swpaul{
8126219Swpaul	sthread_t tid;
8226219Swpaul	int r;
8337300Sbde	time_t now, end;
8426219Swpaul
8526219Swpaul	SM_ASSERT(Monitor);
8626219Swpaul	SM_ASSERT(Mon_exec_time > 0);
8726219Swpaul	tid = (sthread_t) sthread_get_id();
8826219Swpaul	if (pthread_detach(tid) != 0)
8926219Swpaul	{
9026219Swpaul		/* log an error */
9126219Swpaul		return (void *)1;
9265220Sache	}
9365220Sache
9465220Sache/*
9526219Swpaul**  NOTE: this is "flow through" code,
9626219Swpaul**  do NOT use do { } while ("break" is used here!)
9765220Sache*/
9865220Sache
9926219Swpaul#define MON_CHK_STOP							\
10026219Swpaul	now = time(NULL);						\
10126219Swpaul	end = Mon_cur_ctx->ctx_start + Mon_exec_time;			\
10265220Sache	if (now > end)							\
10326219Swpaul	{								\
10426219Swpaul		smi_log(SMI_LOG_ERR,					\
10565220Sache			"WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
10626219Swpaul			(long) now, (long) end,				\
10726219Swpaul			(long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
10826219Swpaul		mi_stop_milters(MILTER_STOP);				\
10926219Swpaul		break;							\
11026219Swpaul	}
11126219Swpaul
11226219Swpaul	(void) smutex_lock(&Mon_mutex);
11326219Swpaul	while (mi_stop() == MILTER_CONT)
11426219Swpaul	{
11526219Swpaul		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
11626219Swpaul		{
11726219Swpaul			struct timespec abstime;
11826219Swpaul
11926219Swpaul			MON_CHK_STOP;
12026219Swpaul			abstime.tv_sec = end;
12126219Swpaul			abstime.tv_nsec = 0;
12226219Swpaul			r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
12326219Swpaul					&abstime);
12426219Swpaul		}
12526219Swpaul		else
12626219Swpaul			r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
12726219Swpaul		if (mi_stop() != MILTER_CONT)
12826219Swpaul			break;
12990271Salfred		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
13026219Swpaul		{
13126219Swpaul			MON_CHK_STOP;
13226219Swpaul		}
13326219Swpaul	}
13426219Swpaul	(void) smutex_unlock(&Mon_mutex);
13526219Swpaul
13637300Sbde	return NULL;
13726219Swpaul}
13837300Sbde
13937300Sbde/*
14026219Swpaul**  MI_MONITOR_INIT -- initialize monitoring thread
14126219Swpaul**
14226219Swpaul**	Parameters: none
14326219Swpaul**
14426219Swpaul**	Returns:
14526219Swpaul**		MI_SUCCESS/MI_FAILURE
14626219Swpaul*/
14726219Swpaul
14826219Swpaulint
14926219Swpaulmi_monitor_init()
15026219Swpaul{
15126219Swpaul	int r;
15226219Swpaul	sthread_t tid;
15326219Swpaul
15426219Swpaul	SM_ASSERT(!Monitor);
15526219Swpaul	if (Mon_exec_time <= 0)
15626219Swpaul		return MI_SUCCESS;
15726219Swpaul	Monitor = true;
15826219Swpaul	if (!smutex_init(&Mon_mutex))
15926219Swpaul		return MI_FAILURE;
16092889Sobrien	if (scond_init(&Mon_cv) != 0)
16192889Sobrien		return MI_FAILURE;
16292889Sobrien	SM_TAILQ_INIT(&Mon_ctx_head);
16326219Swpaul
16426219Swpaul	r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
16526219Swpaul	if (r != 0)
16626219Swpaul		return r;
16726219Swpaul	return MI_SUCCESS;
16826219Swpaul}
16926219Swpaul
17026219Swpaul/*
17126219Swpaul**  MI_MONITOR_WORK_BEGIN -- record start of thread execution
17226219Swpaul**
17326219Swpaul**	Parameters:
17426219Swpaul**		ctx -- session context
17526219Swpaul**		cmd -- milter command char
17626219Swpaul**
17726219Swpaul**	Returns:
17826219Swpaul**		0
17926219Swpaul*/
18026219Swpaul
18126219Swpaulint
18226219Swpaulmi_monitor_work_begin(ctx, cmd)
18326219Swpaul	SMFICTX_PTR ctx;
18426219Swpaul	int cmd;
18526219Swpaul{
18626219Swpaul	(void) smutex_lock(&Mon_mutex);
18726219Swpaul	if (NULL == Mon_cur_ctx)
18826219Swpaul	{
18926219Swpaul		Mon_cur_ctx = ctx;
19026219Swpaul		(void) scond_signal(&Mon_cv);
19126219Swpaul	}
19226219Swpaul	ctx->ctx_start = time(NULL);
19326219Swpaul	SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
19426219Swpaul	(void) smutex_unlock(&Mon_mutex);
19526219Swpaul	return 0;
19626219Swpaul}
19726219Swpaul
19826219Swpaul/*
19926219Swpaul**  MI_MONITOR_WORK_END -- record end of thread execution
20026219Swpaul**
20126219Swpaul**	Parameters:
20226219Swpaul**		ctx -- session context
20326219Swpaul**		cmd -- milter command char
20426219Swpaul**
20526219Swpaul**	Returns:
20626219Swpaul**		0
20726219Swpaul*/
20826219Swpaul
20926219Swpaulint
21026219Swpaulmi_monitor_work_end(ctx, cmd)
21126219Swpaul	SMFICTX_PTR ctx;
21226219Swpaul	int cmd;
21326219Swpaul{
21426219Swpaul	(void) smutex_lock(&Mon_mutex);
21526219Swpaul	ctx->ctx_start = 0;
21626219Swpaul	SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
21726219Swpaul	if (Mon_cur_ctx == ctx)
21826219Swpaul	{
21926219Swpaul		if (SM_TAILQ_EMPTY(&Mon_ctx_head))
22026219Swpaul			Mon_cur_ctx = NULL;
22126219Swpaul		else
22226219Swpaul			Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
22326219Swpaul	}
22426219Swpaul	(void) smutex_unlock(&Mon_mutex);
22526219Swpaul	return 0;
22626219Swpaul}
22726219Swpaul#endif /* _FFR_THREAD_MONITOR */
22826219Swpaul