monitor.c revision 168515
1168515Sgshapiro/*
2168515Sgshapiro *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3168515Sgshapiro *	All rights reserved.
4168515Sgshapiro *
5168515Sgshapiro * By using this file, you agree to the terms and conditions set
6168515Sgshapiro * forth in the LICENSE file which can be found at the top level of
7168515Sgshapiro * the sendmail distribution.
8168515Sgshapiro *
9168515Sgshapiro */
10168515Sgshapiro
11168515Sgshapiro#include "libmilter.h"
12168515Sgshapiro
13168515Sgshapiro#if _FFR_THREAD_MONITOR
14168515Sgshapiro
15168515Sgshapiro/*
16168515Sgshapiro**  Thread Monitoring
17168515Sgshapiro**  Todo: more error checking (return code from function calls)
18168515Sgshapiro**  add comments.
19168515Sgshapiro*/
20168515Sgshapiro
21168515Sgshapirobool Monitor = false; /* use monitoring? */
22168515Sgshapirostatic unsigned int Mon_exec_time = 0;
23168515Sgshapiro
24168515Sgshapiro/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
25168515Sgshapirostatic smutex_t Mon_mutex;
26168515Sgshapirostatic scond_t Mon_cv;
27168515Sgshapiro
28168515Sgshapiro/*
29168515Sgshapiro**  Current ctx to monitor.
30168515Sgshapiro**  Invariant:
31168515Sgshapiro**  Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
32168515Sgshapiro**	time ago.
33168515Sgshapiro**
34168515Sgshapiro**  Basically the entries in the list are ordered by time because new
35168515Sgshapiro**	entries are appended at the end. However, due to the concurrent
36168515Sgshapiro**	execution (multi-threaded) and no guaranteed order of wakeups
37168515Sgshapiro**	after a mutex_lock() attempt, the order might not be strict,
38168515Sgshapiro**	i.e., if the list contains e1 and e2 (in that order) then
39168515Sgshapiro**	the the start time of e2 can be (slightly) smaller than that of e1.
40168515Sgshapiro**	However, this slight inaccurracy should not matter for the proper
41168515Sgshapiro**	working of this algorithm.
42168515Sgshapiro*/
43168515Sgshapiro
44168515Sgshapirostatic SMFICTX_PTR Mon_cur_ctx = NULL;
45168515Sgshapirostatic smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
46168515Sgshapiro
47168515Sgshapiro/*
48168515Sgshapiro**  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
49168515Sgshapiro**
50168515Sgshapiro**	Parameters:
51168515Sgshapiro**		tm -- maximum execution time for a thread
52168515Sgshapiro**
53168515Sgshapiro**	Returns:
54168515Sgshapiro**		MI_SUCCESS
55168515Sgshapiro*/
56168515Sgshapiro
57168515Sgshapiroint
58168515Sgshapirosmfi_set_max_exec_time(tm)
59168515Sgshapiro	unsigned int tm;
60168515Sgshapiro{
61168515Sgshapiro	Mon_exec_time = tm;
62168515Sgshapiro	return MI_SUCCESS;
63168515Sgshapiro}
64168515Sgshapiro
65168515Sgshapiro/*
66168515Sgshapiro**  MI_MONITOR_THREAD -- monitoring thread
67168515Sgshapiro**
68168515Sgshapiro**	Parameters:
69168515Sgshapiro**		arg -- ignored (required by pthread_create())
70168515Sgshapiro**
71168515Sgshapiro**	Returns:
72168515Sgshapiro**		NULL on termination.
73168515Sgshapiro*/
74168515Sgshapiro
75168515Sgshapirostatic void *
76168515Sgshapiromi_monitor_thread(arg)
77168515Sgshapiro	void *arg;
78168515Sgshapiro{
79168515Sgshapiro	sthread_t tid;
80168515Sgshapiro	int r;
81168515Sgshapiro	time_t now, end;
82168515Sgshapiro
83168515Sgshapiro	SM_ASSERT(Monitor);
84168515Sgshapiro	SM_ASSERT(Mon_exec_time > 0);
85168515Sgshapiro	tid = (sthread_t) sthread_get_id();
86168515Sgshapiro	if (pthread_detach(tid) != 0)
87168515Sgshapiro	{
88168515Sgshapiro		/* log an error */
89168515Sgshapiro		return (void *)1;
90168515Sgshapiro	}
91168515Sgshapiro
92168515Sgshapiro/*
93168515Sgshapiro**  NOTE: this is "flow through" code,
94168515Sgshapiro**  do NOT use do { } while ("break" is used here!)
95168515Sgshapiro*/
96168515Sgshapiro
97168515Sgshapiro#define MON_CHK_STOP							\
98168515Sgshapiro	now = time(NULL);						\
99168515Sgshapiro	end = Mon_cur_ctx->ctx_start + Mon_exec_time;			\
100168515Sgshapiro	if (now > end)							\
101168515Sgshapiro	{								\
102168515Sgshapiro		smi_log(SMI_LOG_ERR,					\
103168515Sgshapiro			"WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
104168515Sgshapiro			(long) now, (long) end,				\
105168515Sgshapiro			(long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
106168515Sgshapiro		mi_stop_milters(MILTER_STOP);				\
107168515Sgshapiro		break;							\
108168515Sgshapiro	}
109168515Sgshapiro
110168515Sgshapiro	(void) smutex_lock(&Mon_mutex);
111168515Sgshapiro	while (mi_stop() == MILTER_CONT)
112168515Sgshapiro	{
113168515Sgshapiro		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
114168515Sgshapiro		{
115168515Sgshapiro			struct timespec abstime;
116168515Sgshapiro
117168515Sgshapiro			MON_CHK_STOP;
118168515Sgshapiro			abstime.tv_sec = end;
119168515Sgshapiro			abstime.tv_nsec = 0;
120168515Sgshapiro			r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
121168515Sgshapiro					&abstime);
122168515Sgshapiro		}
123168515Sgshapiro		else
124168515Sgshapiro			r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
125168515Sgshapiro		if (mi_stop() != MILTER_CONT)
126168515Sgshapiro			break;
127168515Sgshapiro		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
128168515Sgshapiro		{
129168515Sgshapiro			MON_CHK_STOP;
130168515Sgshapiro		}
131168515Sgshapiro	}
132168515Sgshapiro	(void) smutex_unlock(&Mon_mutex);
133168515Sgshapiro
134168515Sgshapiro	return NULL;
135168515Sgshapiro}
136168515Sgshapiro
137168515Sgshapiro/*
138168515Sgshapiro**  MI_MONITOR_INIT -- initialize monitoring thread
139168515Sgshapiro**
140168515Sgshapiro**	Parameters: none
141168515Sgshapiro**
142168515Sgshapiro**	Returns:
143168515Sgshapiro**		MI_SUCCESS/MI_FAILURE
144168515Sgshapiro*/
145168515Sgshapiro
146168515Sgshapiroint
147168515Sgshapiromi_monitor_init()
148168515Sgshapiro{
149168515Sgshapiro	int r;
150168515Sgshapiro	sthread_t tid;
151168515Sgshapiro
152168515Sgshapiro	SM_ASSERT(!Monitor);
153168515Sgshapiro	if (Mon_exec_time <= 0)
154168515Sgshapiro		return MI_SUCCESS;
155168515Sgshapiro	Monitor = true;
156168515Sgshapiro	if (!smutex_init(&Mon_mutex))
157168515Sgshapiro		return MI_FAILURE;
158168515Sgshapiro	if (scond_init(&Mon_cv) != 0)
159168515Sgshapiro		return MI_FAILURE;
160168515Sgshapiro	SM_TAILQ_INIT(&Mon_ctx_head);
161168515Sgshapiro
162168515Sgshapiro	r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
163168515Sgshapiro	if (r != 0)
164168515Sgshapiro		return r;
165168515Sgshapiro	return MI_SUCCESS;
166168515Sgshapiro}
167168515Sgshapiro
168168515Sgshapiro/*
169168515Sgshapiro**  MI_MONITOR_WORK_BEGIN -- record start of thread execution
170168515Sgshapiro**
171168515Sgshapiro**	Parameters:
172168515Sgshapiro**		ctx -- session context
173168515Sgshapiro**		cmd -- milter command char
174168515Sgshapiro**
175168515Sgshapiro**	Returns:
176168515Sgshapiro**		0
177168515Sgshapiro*/
178168515Sgshapiro
179168515Sgshapiroint
180168515Sgshapiromi_monitor_work_begin(ctx, cmd)
181168515Sgshapiro	SMFICTX_PTR ctx;
182168515Sgshapiro	int cmd;
183168515Sgshapiro{
184168515Sgshapiro	(void) smutex_lock(&Mon_mutex);
185168515Sgshapiro	if (NULL == Mon_cur_ctx)
186168515Sgshapiro	{
187168515Sgshapiro		Mon_cur_ctx = ctx;
188168515Sgshapiro		(void) scond_signal(&Mon_cv);
189168515Sgshapiro	}
190168515Sgshapiro	ctx->ctx_start = time(NULL);
191168515Sgshapiro	SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
192168515Sgshapiro	(void) smutex_unlock(&Mon_mutex);
193168515Sgshapiro	return 0;
194168515Sgshapiro}
195168515Sgshapiro
196168515Sgshapiro/*
197168515Sgshapiro**  MI_MONITOR_WORK_END -- record end of thread execution
198168515Sgshapiro**
199168515Sgshapiro**	Parameters:
200168515Sgshapiro**		ctx -- session context
201168515Sgshapiro**		cmd -- milter command char
202168515Sgshapiro**
203168515Sgshapiro**	Returns:
204168515Sgshapiro**		0
205168515Sgshapiro*/
206168515Sgshapiro
207168515Sgshapiroint
208168515Sgshapiromi_monitor_work_end(ctx, cmd)
209168515Sgshapiro	SMFICTX_PTR ctx;
210168515Sgshapiro	int cmd;
211168515Sgshapiro{
212168515Sgshapiro	(void) smutex_lock(&Mon_mutex);
213168515Sgshapiro	ctx->ctx_start = 0;
214168515Sgshapiro	SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
215168515Sgshapiro	if (Mon_cur_ctx == ctx)
216168515Sgshapiro	{
217168515Sgshapiro		if (SM_TAILQ_EMPTY(&Mon_ctx_head))
218168515Sgshapiro			Mon_cur_ctx = NULL;
219168515Sgshapiro		else
220168515Sgshapiro			Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
221168515Sgshapiro	}
222168515Sgshapiro	(void) smutex_unlock(&Mon_mutex);
223168515Sgshapiro	return 0;
224168515Sgshapiro}
225168515Sgshapiro#endif /* _FFR_THREAD_MONITOR */
226