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