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