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