Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / sendmail-8.14 / libmilter / monitor.c
1 /*
2  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10
11 #include "libmilter.h"
12
13 #if _FFR_THREAD_MONITOR
14
15 /*
16 **  Thread Monitoring
17 **  Todo: more error checking (return code from function calls)
18 **  add comments.
19 */
20
21 bool Monitor = false; /* use monitoring? */
22 static unsigned int Mon_exec_time = 0;
23
24 /* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
25 static smutex_t Mon_mutex;
26 static scond_t Mon_cv;
27
28 /*
29 **  Current ctx to monitor.
30 **  Invariant:
31 **  Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
32 **      time ago.
33 **
34 **  Basically the entries in the list are ordered by time because new
35 **      entries are appended at the end. However, due to the concurrent
36 **      execution (multi-threaded) and no guaranteed order of wakeups
37 **      after a mutex_lock() attempt, the order might not be strict,
38 **      i.e., if the list contains e1 and e2 (in that order) then
39 **      the the start time of e2 can be (slightly) smaller than that of e1.
40 **      However, this slight inaccurracy should not matter for the proper
41 **      working of this algorithm.
42 */
43
44 static SMFICTX_PTR Mon_cur_ctx = NULL;
45 static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
46
47 /*
48 **  SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
49 **
50 **      Parameters:
51 **              tm -- maximum execution time for a thread
52 **
53 **      Returns:
54 **              MI_SUCCESS
55 */
56
57 int
58 smfi_set_max_exec_time(tm)
59         unsigned int tm;
60 {
61         Mon_exec_time = tm;
62         return MI_SUCCESS;
63 }
64
65 /*
66 **  MI_MONITOR_THREAD -- monitoring thread
67 **
68 **      Parameters:
69 **              arg -- ignored (required by pthread_create())
70 **
71 **      Returns:
72 **              NULL on termination.
73 */
74
75 static void *
76 mi_monitor_thread(arg)
77         void *arg;
78 {
79         sthread_t tid;
80         int r;
81         time_t now, end;
82
83         SM_ASSERT(Monitor);
84         SM_ASSERT(Mon_exec_time > 0);
85         tid = (sthread_t) sthread_get_id();
86         if (pthread_detach(tid) != 0)
87         {
88                 /* log an error */
89                 return (void *)1;
90         }
91
92 /*
93 **  NOTE: this is "flow through" code,
94 **  do NOT use do { } while ("break" is used here!)
95 */
96
97 #define MON_CHK_STOP                                                    \
98         now = time(NULL);                                               \
99         end = Mon_cur_ctx->ctx_start + Mon_exec_time;                   \
100         if (now > end)                                                  \
101         {                                                               \
102                 smi_log(SMI_LOG_ERR,                                    \
103                         "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
104                         (long) now, (long) end,                         \
105                         (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
106                 mi_stop_milters(MILTER_STOP);                           \
107                 break;                                                  \
108         }
109
110         (void) smutex_lock(&Mon_mutex);
111         while (mi_stop() == MILTER_CONT)
112         {
113                 if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
114                 {
115                         struct timespec abstime;
116
117                         MON_CHK_STOP;
118                         abstime.tv_sec = end;
119                         abstime.tv_nsec = 0;
120                         r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
121                                         &abstime);
122                 }
123                 else
124                         r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
125                 if (mi_stop() != MILTER_CONT)
126                         break;
127                 if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
128                 {
129                         MON_CHK_STOP;
130                 }
131         }
132         (void) smutex_unlock(&Mon_mutex);
133
134         return NULL;
135 }
136
137 /*
138 **  MI_MONITOR_INIT -- initialize monitoring thread
139 **
140 **      Parameters: none
141 **
142 **      Returns:
143 **              MI_SUCCESS/MI_FAILURE
144 */
145
146 int
147 mi_monitor_init()
148 {
149         int r;
150         sthread_t tid;
151
152         SM_ASSERT(!Monitor);
153         if (Mon_exec_time <= 0)
154                 return MI_SUCCESS;
155         Monitor = true;
156         if (!smutex_init(&Mon_mutex))
157                 return MI_FAILURE;
158         if (scond_init(&Mon_cv) != 0)
159                 return MI_FAILURE;
160         SM_TAILQ_INIT(&Mon_ctx_head);
161
162         r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
163         if (r != 0)
164                 return r;
165         return MI_SUCCESS;
166 }
167
168 /*
169 **  MI_MONITOR_WORK_BEGIN -- record start of thread execution
170 **
171 **      Parameters:
172 **              ctx -- session context
173 **              cmd -- milter command char
174 **
175 **      Returns:
176 **              0
177 */
178
179 int
180 mi_monitor_work_begin(ctx, cmd)
181         SMFICTX_PTR ctx;
182         int cmd;
183 {
184         (void) smutex_lock(&Mon_mutex);
185         if (NULL == Mon_cur_ctx)
186         {
187                 Mon_cur_ctx = ctx;
188                 (void) scond_signal(&Mon_cv);
189         }
190         ctx->ctx_start = time(NULL);
191         SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
192         (void) smutex_unlock(&Mon_mutex);
193         return 0;
194 }
195
196 /*
197 **  MI_MONITOR_WORK_END -- record end of thread execution
198 **
199 **      Parameters:
200 **              ctx -- session context
201 **              cmd -- milter command char
202 **
203 **      Returns:
204 **              0
205 */
206
207 int
208 mi_monitor_work_end(ctx, cmd)
209         SMFICTX_PTR ctx;
210         int cmd;
211 {
212         (void) smutex_lock(&Mon_mutex);
213         ctx->ctx_start = 0;
214         SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
215         if (Mon_cur_ctx == ctx)
216         {
217                 if (SM_TAILQ_EMPTY(&Mon_ctx_head))
218                         Mon_cur_ctx = NULL;
219                 else
220                         Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
221         }
222         (void) smutex_unlock(&Mon_mutex);
223         return 0;
224 }
225 #endif /* _FFR_THREAD_MONITOR */