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