xref: /titanic_41/usr/src/cmd/sendmail/libmilter/monitor.c (revision 7800901e60d340b6af88e94a2149805dcfcaaf56)
1058561cbSjbeck /*
2058561cbSjbeck  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3058561cbSjbeck  *	All rights reserved.
4058561cbSjbeck  *
5058561cbSjbeck  * By using this file, you agree to the terms and conditions set
6058561cbSjbeck  * forth in the LICENSE file which can be found at the top level of
7058561cbSjbeck  * the sendmail distribution.
8058561cbSjbeck  *
9058561cbSjbeck  */
10058561cbSjbeck 
11058561cbSjbeck #pragma ident	"%Z%%M%	%I%	%E% SMI"
12058561cbSjbeck 
13*7800901eSjbeck #include <sm/gen.h>
14*7800901eSjbeck SM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $")
15058561cbSjbeck #include "libmilter.h"
16058561cbSjbeck 
17058561cbSjbeck #if _FFR_THREAD_MONITOR
18058561cbSjbeck 
19058561cbSjbeck /*
20058561cbSjbeck **  Thread Monitoring
21058561cbSjbeck **  Todo: more error checking (return code from function calls)
22058561cbSjbeck **  add comments.
23058561cbSjbeck */
24058561cbSjbeck 
25058561cbSjbeck bool Monitor = false; /* use monitoring? */
26058561cbSjbeck static unsigned int Mon_exec_time = 0;
27058561cbSjbeck 
28058561cbSjbeck /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
29058561cbSjbeck static smutex_t Mon_mutex;
30058561cbSjbeck static scond_t Mon_cv;
31058561cbSjbeck 
32058561cbSjbeck /*
33058561cbSjbeck **  Current ctx to monitor.
34058561cbSjbeck **  Invariant:
35058561cbSjbeck **  Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
36058561cbSjbeck **	time ago.
37058561cbSjbeck **
38058561cbSjbeck **  Basically the entries in the list are ordered by time because new
39058561cbSjbeck **	entries are appended at the end. However, due to the concurrent
40058561cbSjbeck **	execution (multi-threaded) and no guaranteed order of wakeups
41058561cbSjbeck **	after a mutex_lock() attempt, the order might not be strict,
42058561cbSjbeck **	i.e., if the list contains e1 and e2 (in that order) then
43058561cbSjbeck **	the the start time of e2 can be (slightly) smaller than that of e1.
44058561cbSjbeck **	However, this slight inaccurracy should not matter for the proper
45058561cbSjbeck **	working of this algorithm.
46058561cbSjbeck */
47058561cbSjbeck 
48058561cbSjbeck static SMFICTX_PTR Mon_cur_ctx = NULL;
49058561cbSjbeck static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
50058561cbSjbeck 
51058561cbSjbeck /*
52058561cbSjbeck **  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
53058561cbSjbeck **
54058561cbSjbeck **	Parameters:
55058561cbSjbeck **		tm -- maximum execution time for a thread
56058561cbSjbeck **
57058561cbSjbeck **	Returns:
58058561cbSjbeck **		MI_SUCCESS
59058561cbSjbeck */
60058561cbSjbeck 
61058561cbSjbeck int
smfi_set_max_exec_time(tm)62058561cbSjbeck smfi_set_max_exec_time(tm)
63058561cbSjbeck 	unsigned int tm;
64058561cbSjbeck {
65058561cbSjbeck 	Mon_exec_time = tm;
66058561cbSjbeck 	return MI_SUCCESS;
67058561cbSjbeck }
68058561cbSjbeck 
69058561cbSjbeck /*
70058561cbSjbeck **  MI_MONITOR_THREAD -- monitoring thread
71058561cbSjbeck **
72058561cbSjbeck **	Parameters:
73058561cbSjbeck **		arg -- ignored (required by pthread_create())
74058561cbSjbeck **
75058561cbSjbeck **	Returns:
76058561cbSjbeck **		NULL on termination.
77058561cbSjbeck */
78058561cbSjbeck 
79058561cbSjbeck static void *
mi_monitor_thread(arg)80058561cbSjbeck mi_monitor_thread(arg)
81058561cbSjbeck 	void *arg;
82058561cbSjbeck {
83058561cbSjbeck 	sthread_t tid;
84058561cbSjbeck 	int r;
85058561cbSjbeck 	time_t now, end;
86058561cbSjbeck 
87058561cbSjbeck 	SM_ASSERT(Monitor);
88058561cbSjbeck 	SM_ASSERT(Mon_exec_time > 0);
89058561cbSjbeck 	tid = (sthread_t) sthread_get_id();
90058561cbSjbeck 	if (pthread_detach(tid) != 0)
91058561cbSjbeck 	{
92058561cbSjbeck 		/* log an error */
93058561cbSjbeck 		return (void *)1;
94058561cbSjbeck 	}
95058561cbSjbeck 
96058561cbSjbeck /*
97058561cbSjbeck **  NOTE: this is "flow through" code,
98058561cbSjbeck **  do NOT use do { } while ("break" is used here!)
99058561cbSjbeck */
100058561cbSjbeck 
101058561cbSjbeck #define MON_CHK_STOP							\
102058561cbSjbeck 	now = time(NULL);						\
103058561cbSjbeck 	end = Mon_cur_ctx->ctx_start + Mon_exec_time;			\
104058561cbSjbeck 	if (now > end)							\
105058561cbSjbeck 	{								\
106058561cbSjbeck 		smi_log(SMI_LOG_ERR,					\
107058561cbSjbeck 			"WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
108058561cbSjbeck 			(long) now, (long) end,				\
109058561cbSjbeck 			(long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
110058561cbSjbeck 		mi_stop_milters(MILTER_STOP);				\
111058561cbSjbeck 		break;							\
112058561cbSjbeck 	}
113058561cbSjbeck 
114058561cbSjbeck 	(void) smutex_lock(&Mon_mutex);
115058561cbSjbeck 	while (mi_stop() == MILTER_CONT)
116058561cbSjbeck 	{
117058561cbSjbeck 		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
118058561cbSjbeck 		{
119058561cbSjbeck 			struct timespec abstime;
120058561cbSjbeck 
121058561cbSjbeck 			MON_CHK_STOP;
122058561cbSjbeck 			abstime.tv_sec = end;
123058561cbSjbeck 			abstime.tv_nsec = 0;
124058561cbSjbeck 			r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
125058561cbSjbeck 					&abstime);
126058561cbSjbeck 		}
127058561cbSjbeck 		else
128058561cbSjbeck 			r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
129058561cbSjbeck 		if (mi_stop() != MILTER_CONT)
130058561cbSjbeck 			break;
131058561cbSjbeck 		if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
132058561cbSjbeck 		{
133058561cbSjbeck 			MON_CHK_STOP;
134058561cbSjbeck 		}
135058561cbSjbeck 	}
136058561cbSjbeck 	(void) smutex_unlock(&Mon_mutex);
137058561cbSjbeck 
138058561cbSjbeck 	return NULL;
139058561cbSjbeck }
140058561cbSjbeck 
141058561cbSjbeck /*
142058561cbSjbeck **  MI_MONITOR_INIT -- initialize monitoring thread
143058561cbSjbeck **
144058561cbSjbeck **	Parameters: none
145058561cbSjbeck **
146058561cbSjbeck **	Returns:
147058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
148058561cbSjbeck */
149058561cbSjbeck 
150058561cbSjbeck int
mi_monitor_init()151058561cbSjbeck mi_monitor_init()
152058561cbSjbeck {
153058561cbSjbeck 	int r;
154058561cbSjbeck 	sthread_t tid;
155058561cbSjbeck 
156058561cbSjbeck 	SM_ASSERT(!Monitor);
157058561cbSjbeck 	if (Mon_exec_time <= 0)
158058561cbSjbeck 		return MI_SUCCESS;
159058561cbSjbeck 	Monitor = true;
160058561cbSjbeck 	if (!smutex_init(&Mon_mutex))
161058561cbSjbeck 		return MI_FAILURE;
162058561cbSjbeck 	if (scond_init(&Mon_cv) != 0)
163058561cbSjbeck 		return MI_FAILURE;
164058561cbSjbeck 	SM_TAILQ_INIT(&Mon_ctx_head);
165058561cbSjbeck 
166058561cbSjbeck 	r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
167058561cbSjbeck 	if (r != 0)
168058561cbSjbeck 		return r;
169058561cbSjbeck 	return MI_SUCCESS;
170058561cbSjbeck }
171058561cbSjbeck 
172058561cbSjbeck /*
173058561cbSjbeck **  MI_MONITOR_WORK_BEGIN -- record start of thread execution
174058561cbSjbeck **
175058561cbSjbeck **	Parameters:
176058561cbSjbeck **		ctx -- session context
177058561cbSjbeck **		cmd -- milter command char
178058561cbSjbeck **
179058561cbSjbeck **	Returns:
180058561cbSjbeck **		0
181058561cbSjbeck */
182058561cbSjbeck 
183058561cbSjbeck int
mi_monitor_work_begin(ctx,cmd)184058561cbSjbeck mi_monitor_work_begin(ctx, cmd)
185058561cbSjbeck 	SMFICTX_PTR ctx;
186058561cbSjbeck 	int cmd;
187058561cbSjbeck {
188058561cbSjbeck 	(void) smutex_lock(&Mon_mutex);
189058561cbSjbeck 	if (NULL == Mon_cur_ctx)
190058561cbSjbeck 	{
191058561cbSjbeck 		Mon_cur_ctx = ctx;
192058561cbSjbeck 		(void) scond_signal(&Mon_cv);
193058561cbSjbeck 	}
194058561cbSjbeck 	ctx->ctx_start = time(NULL);
195058561cbSjbeck 	SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
196058561cbSjbeck 	(void) smutex_unlock(&Mon_mutex);
197058561cbSjbeck 	return 0;
198058561cbSjbeck }
199058561cbSjbeck 
200058561cbSjbeck /*
201058561cbSjbeck **  MI_MONITOR_WORK_END -- record end of thread execution
202058561cbSjbeck **
203058561cbSjbeck **	Parameters:
204058561cbSjbeck **		ctx -- session context
205058561cbSjbeck **		cmd -- milter command char
206058561cbSjbeck **
207058561cbSjbeck **	Returns:
208058561cbSjbeck **		0
209058561cbSjbeck */
210058561cbSjbeck 
211058561cbSjbeck int
mi_monitor_work_end(ctx,cmd)212058561cbSjbeck mi_monitor_work_end(ctx, cmd)
213058561cbSjbeck 	SMFICTX_PTR ctx;
214058561cbSjbeck 	int cmd;
215058561cbSjbeck {
216058561cbSjbeck 	(void) smutex_lock(&Mon_mutex);
217058561cbSjbeck 	ctx->ctx_start = 0;
218058561cbSjbeck 	SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
219058561cbSjbeck 	if (Mon_cur_ctx == ctx)
220058561cbSjbeck 	{
221058561cbSjbeck 		if (SM_TAILQ_EMPTY(&Mon_ctx_head))
222058561cbSjbeck 			Mon_cur_ctx = NULL;
223058561cbSjbeck 		else
224058561cbSjbeck 			Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
225058561cbSjbeck 	}
226058561cbSjbeck 	(void) smutex_unlock(&Mon_mutex);
227058561cbSjbeck 	return 0;
228058561cbSjbeck }
229058561cbSjbeck #endif /* _FFR_THREAD_MONITOR */
230