1/*
2 *  Copyright (c) 1999-2004, 2006 Proofpoint, 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>
12SM_RCSID("@(#)$Id: signal.c,v 8.45 2013-11-22 20:51:36 ca Exp $")
13
14#include "libmilter.h"
15
16/*
17**  thread to handle signals
18*/
19
20static smutex_t M_Mutex;
21
22static int MilterStop = MILTER_CONT;
23
24static void	*mi_signal_thread __P((void *));
25static 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
37int
38mi_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
52void
53mi_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
74void
75mi_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
89static void *
90mi_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 SIGWAIT_TAKES_1_ARG
108		if ((sig = sigwait(&set)) < 0)
109#else
110		if ((sigerr = sigwait(&set, &sig)) != 0)
111#endif
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
160static int
161mi_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
200int
201mi_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