1 /*
2  *  Copyright (c) 1999-2004, 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
12 
13 #include <sm/gen.h>
14 SM_RCSID("@(#)$Id: signal.c,v 8.44 2006/03/03 03:42:04 ca Exp $")
15 
16 #include "libmilter.h"
17 
18 /*
19 **  thread to handle signals
20 */
21 
22 static smutex_t M_Mutex;
23 
24 static int MilterStop = MILTER_CONT;
25 
26 static void	*mi_signal_thread __P((void *));
27 static int	 mi_spawn_signal_thread __P((char *));
28 
29 /*
30 **  MI_STOP -- return value of MilterStop
31 **
32 **	Parameters:
33 **		none.
34 **
35 **	Returns:
36 **		value of MilterStop
37 */
38 
39 int
40 mi_stop()
41 {
42 	return MilterStop;
43 }
44 /*
45 **  MI_STOP_MILTERS -- set value of MilterStop
46 **
47 **	Parameters:
48 **		v -- new value for MilterStop.
49 **
50 **	Returns:
51 **		none.
52 */
53 
54 void
55 mi_stop_milters(v)
56 	int v;
57 {
58 	(void) smutex_lock(&M_Mutex);
59 	if (MilterStop < v)
60 		MilterStop = v;
61 
62 	/* close listen socket */
63 	mi_closener();
64 	(void) smutex_unlock(&M_Mutex);
65 }
66 /*
67 **  MI_CLEAN_SIGNALS -- clean up signal handler thread
68 **
69 **	Parameters:
70 **		none.
71 **
72 **	Returns:
73 **		none.
74 */
75 
76 void
77 mi_clean_signals()
78 {
79 	(void) smutex_destroy(&M_Mutex);
80 }
81 /*
82 **  MI_SIGNAL_THREAD -- thread to deal with signals
83 **
84 **	Parameters:
85 **		name -- name of milter
86 **
87 **	Returns:
88 **		NULL
89 */
90 
91 static void *
92 mi_signal_thread(name)
93 	void *name;
94 {
95 	int sig, errs, sigerr;
96 	sigset_t set;
97 
98 	(void) sigemptyset(&set);
99 	(void) sigaddset(&set, SIGHUP);
100 	(void) sigaddset(&set, SIGTERM);
101 
102 	/* Handle Ctrl-C gracefully for debugging */
103 	(void) sigaddset(&set, SIGINT);
104 	errs = 0;
105 
106 	for (;;)
107 	{
108 		sigerr = sig = 0;
109 #if defined(SOLARIS) || defined(__svr5__)
110 		if ((sig = sigwait(&set)) < 0)
111 #else /* defined(SOLARIS) || defined(__svr5__) */
112 		if ((sigerr = sigwait(&set, &sig)) != 0)
113 #endif /* defined(SOLARIS) || defined(__svr5__) */
114 		{
115 			/* some OS return -1 and set errno: copy it */
116 			if (sigerr <= 0)
117 				sigerr = errno;
118 
119 			/* this can happen on OSF/1 (at least) */
120 			if (sigerr == EINTR)
121 				continue;
122 			smi_log(SMI_LOG_ERR,
123 				"%s: sigwait returned error: %d",
124 				(char *)name, sigerr);
125 			if (++errs > MAX_FAILS_T)
126 			{
127 				mi_stop_milters(MILTER_ABRT);
128 				return NULL;
129 			}
130 			continue;
131 		}
132 		errs = 0;
133 
134 		switch (sig)
135 		{
136 		  case SIGHUP:
137 		  case SIGTERM:
138 			mi_stop_milters(MILTER_STOP);
139 			return NULL;
140 		  case SIGINT:
141 			mi_stop_milters(MILTER_ABRT);
142 			return NULL;
143 		  default:
144 			smi_log(SMI_LOG_ERR,
145 				"%s: sigwait returned unmasked signal: %d",
146 				(char *)name, sig);
147 			break;
148 		}
149 	}
150 	/* NOTREACHED */
151 }
152 /*
153 **  MI_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
154 **
155 **	Parameters:
156 **		name -- name of milter
157 **
158 **	Returns:
159 **		MI_SUCCESS/MI_FAILURE
160 */
161 
162 static int
163 mi_spawn_signal_thread(name)
164 	char *name;
165 {
166 	sthread_t tid;
167 	int r;
168 	sigset_t set;
169 
170 	/* Mask HUP and KILL signals */
171 	(void) sigemptyset(&set);
172 	(void) sigaddset(&set, SIGHUP);
173 	(void) sigaddset(&set, SIGTERM);
174 	(void) sigaddset(&set, SIGINT);
175 
176 	if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
177 	{
178 		smi_log(SMI_LOG_ERR,
179 			"%s: Couldn't mask HUP and KILL signals", name);
180 		return MI_FAILURE;
181 	}
182 	r = thread_create(&tid, mi_signal_thread, (void *)name);
183 	if (r != 0)
184 	{
185 		smi_log(SMI_LOG_ERR,
186 			"%s: Couldn't start signal thread: %d",
187 			name, r);
188 		return MI_FAILURE;
189 	}
190 	return MI_SUCCESS;
191 }
192 /*
193 **  MI_CONTROL_STARTUP -- startup for thread to handle signals
194 **
195 **	Parameters:
196 **		name -- name of milter
197 **
198 **	Returns:
199 **		MI_SUCCESS/MI_FAILURE
200 */
201 
202 int
203 mi_control_startup(name)
204 	char *name;
205 {
206 
207 	if (!smutex_init(&M_Mutex))
208 	{
209 		smi_log(SMI_LOG_ERR,
210 			"%s: Couldn't initialize control pipe mutex", name);
211 		return MI_FAILURE;
212 	}
213 
214 	/*
215 	**  spawn_signal_thread must happen before other threads are spawned
216 	**  off so that it can mask the right signals and other threads
217 	**  will inherit that mask.
218 	*/
219 	if (mi_spawn_signal_thread(name) == MI_FAILURE)
220 	{
221 		smi_log(SMI_LOG_ERR,
222 			"%s: Couldn't spawn signal thread", name);
223 		(void) smutex_destroy(&M_Mutex);
224 		return MI_FAILURE;
225 	}
226 	return MI_SUCCESS;
227 }
228