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