1d0cef73dSGregory Neil Shapiro /*
25dd76dd0SGregory 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>
124313cc83SGregory 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.
42*5b0945b5SGregory 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