1 /*
2  *  Copyright (c) 1999-2004 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.42 2004/08/20 21:10:30 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;
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 		sig = 0;
109 #if defined(SOLARIS) || defined(__svr5__)
110 		if ((sig = sigwait(&set)) < 0)
111 #else /* defined(SOLARIS) || defined(__svr5__) */
112 		if (sigwait(&set, &sig) != 0)
113 #endif /* defined(SOLARIS) || defined(__svr5__) */
114 		{
115 			/* this can happen on OSF/1 (at least) */
116 			if (errno == EINTR)
117 				continue;
118 			smi_log(SMI_LOG_ERR,
119 				"%s: sigwait returned error: %d",
120 				(char *)name, errno);
121 			if (++errs > MAX_FAILS_T)
122 			{
123 				mi_stop_milters(MILTER_ABRT);
124 				return NULL;
125 			}
126 			continue;
127 		}
128 		errs = 0;
129 
130 		switch (sig)
131 		{
132 		  case SIGHUP:
133 		  case SIGTERM:
134 			mi_stop_milters(MILTER_STOP);
135 			return NULL;
136 		  case SIGINT:
137 			mi_stop_milters(MILTER_ABRT);
138 			return NULL;
139 		  default:
140 			smi_log(SMI_LOG_ERR,
141 				"%s: sigwait returned unmasked signal: %d",
142 				(char *)name, sig);
143 			break;
144 		}
145 	}
146 	/* NOTREACHED */
147 }
148 /*
149 **  MI_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
150 **
151 **	Parameters:
152 **		name -- name of milter
153 **
154 **	Returns:
155 **		MI_SUCCESS/MI_FAILURE
156 */
157 
158 static int
159 mi_spawn_signal_thread(name)
160 	char *name;
161 {
162 	sthread_t tid;
163 	int r;
164 	sigset_t set;
165 
166 	/* Mask HUP and KILL signals */
167 	(void) sigemptyset(&set);
168 	(void) sigaddset(&set, SIGHUP);
169 	(void) sigaddset(&set, SIGTERM);
170 	(void) sigaddset(&set, SIGINT);
171 
172 	if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
173 	{
174 		smi_log(SMI_LOG_ERR,
175 			"%s: Couldn't mask HUP and KILL signals", name);
176 		return MI_FAILURE;
177 	}
178 	r = thread_create(&tid, mi_signal_thread, (void *)name);
179 	if (r != 0)
180 	{
181 		smi_log(SMI_LOG_ERR,
182 			"%s: Couldn't start signal thread: %d",
183 			name, r);
184 		return MI_FAILURE;
185 	}
186 	return MI_SUCCESS;
187 }
188 /*
189 **  MI_CONTROL_STARTUP -- startup for thread to handle signals
190 **
191 **	Parameters:
192 **		name -- name of milter
193 **
194 **	Returns:
195 **		MI_SUCCESS/MI_FAILURE
196 */
197 
198 int
199 mi_control_startup(name)
200 	char *name;
201 {
202 
203 	if (!smutex_init(&M_Mutex))
204 	{
205 		smi_log(SMI_LOG_ERR,
206 			"%s: Couldn't initialize control pipe mutex", name);
207 		return MI_FAILURE;
208 	}
209 
210 	/*
211 	**  spawn_signal_thread must happen before other threads are spawned
212 	**  off so that it can mask the right signals and other threads
213 	**  will inherit that mask.
214 	*/
215 	if (mi_spawn_signal_thread(name) == MI_FAILURE)
216 	{
217 		smi_log(SMI_LOG_ERR,
218 			"%s: Couldn't spawn signal thread", name);
219 		(void) smutex_destroy(&M_Mutex);
220 		return MI_FAILURE;
221 	}
222 	return MI_SUCCESS;
223 }
224