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