summaryrefslogtreecommitdiffstats
path: root/src/master/master_sig.c
blob: db5e39d2f2516504219331be9f4351a6816ee27c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*++
/* NAME
/*	master_sig 3
/* SUMMARY
/*	Postfix master - signal processing
/* SYNOPSIS
/*	#include "master.h"
/*
/*	int	master_gotsighup;
/*	int	master_gotsigchld;
/*
/*	int	master_sigsetup()
/* DESCRIPTION
/*	This module implements the master process signal handling interface.
/*
/*	master_gotsighup (master_gotsigchld) is set to SIGHUP (SIGCHLD)
/*	when the process receives a hangup (child death) signal.
/*
/*	master_sigsetup() enables processing of hangup and child death signals.
/*	Receipt of SIGINT, SIGQUIT, SIGSEGV, SIGILL, or SIGTERM
/*	is interpreted as a request for termination.  Child processes are
/*	notified of the master\'s demise by sending them a SIGTERM signal.
/* DIAGNOSTICS
/* BUGS
/*	Need a way to register cleanup actions.
/* SEE ALSO
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System libraries. */

#include <sys_defs.h>
#include <signal.h>
#include <unistd.h>

/* Utility library. */

#include <msg.h>
#include <posix_signals.h>
#include <killme_after.h>

/* Application-specific. */

#include "master.h"

#ifdef USE_SIG_RETURN
#include <sys/syscall.h>
#undef USE_SIG_PIPE
#else
#define USE_SIG_PIPE
#endif

/* Local stuff. */

#ifdef USE_SIG_PIPE
#include <errno.h>
#include <fcntl.h>
#include <iostuff.h>
#include <events.h>

int     master_sig_pipe[2];

#define SIG_PIPE_WRITE_FD master_sig_pipe[1]
#define SIG_PIPE_READ_FD master_sig_pipe[0]
#endif

int     master_gotsigchld;
int     master_gotsighup;

#ifdef USE_SIG_RETURN

/* master_sighup - register arrival of hangup signal */

static void master_sighup(int sig)
{

    /*
     * WARNING WARNING WARNING.
     * 
     * This code runs at unpredictable moments, as a signal handler. Don't put
     * any code here other than for setting a global flag.
     */
    master_gotsighup = sig;
}

/* master_sigchld - register arrival of child death signal */

static void master_sigchld(int sig, int code, struct sigcontext * scp)
{

    /*
     * WARNING WARNING WARNING.
     * 
     * This code runs at unpredictable moments, as a signal handler. Don't put
     * any code here other than for setting a global flag, or code that is
     * intended to be run within a signal handler.
     */
    master_gotsigchld = sig;
    if (scp != NULL && scp->sc_syscall == SYS_select) {
	scp->sc_syscall_action = SIG_RETURN;
#ifndef SA_RESTART
    } else if (scp != NULL) {
	scp->sc_syscall_action = SIG_RESTART;
#endif
    }
}

#else

/* master_sighup - register arrival of hangup signal */

static void master_sighup(int sig)
{
    int     saved_errno = errno;

    /*
     * WARNING WARNING WARNING.
     * 
     * This code runs at unpredictable moments, as a signal handler. Don't put
     * any code here other than for setting a global flag, or code that is
     * intended to be run within a signal handler. Restore errno in case we
     * are interrupting the epilog of a failed system call.
     */
    master_gotsighup = sig;
    if (write(SIG_PIPE_WRITE_FD, "", 1) != 1)
	msg_warn("write to SIG_PIPE_WRITE_FD failed: %m");
    errno = saved_errno;
}

/* master_sigchld - force wakeup from select() */

static void master_sigchld(int unused_sig)
{
    int     saved_errno = errno;

    /*
     * WARNING WARNING WARNING.
     * 
     * This code runs at unpredictable moments, as a signal handler. Don't put
     * any code here other than for setting a global flag, or code that is
     * intended to be run within a signal handler. Restore errno in case we
     * are interrupting the epilog of a failed system call.
     */
    master_gotsigchld = 1;
    if (write(SIG_PIPE_WRITE_FD, "", 1) != 1)
	msg_warn("write to SIG_PIPE_WRITE_FD failed: %m");
    errno = saved_errno;
}

/* master_sig_event - called upon return from select() */

static void master_sig_event(int unused_event, void *unused_context)
{
    char    c[1];

    while (read(SIG_PIPE_READ_FD, c, 1) > 0)
	 /* void */ ;
}

#endif

/* master_sigdeath - die, women and children first */

static void master_sigdeath(int sig)
{
    const char *myname = "master_sigdeath";
    struct sigaction action;
    pid_t   pid = getpid();

    /*
     * Set alarm clock here for suicide after 5s.
     */
    killme_after(5);

    /*
     * Terminate all processes in our process group, except ourselves.
     */
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = SIG_IGN;
    if (sigaction(SIGTERM, &action, (struct sigaction *) 0) < 0)
	msg_fatal("%s: sigaction: %m", myname);
    if (kill(-pid, SIGTERM) < 0)
	msg_fatal("%s: kill process group: %m", myname);

    /*
     * XXX We're running from a signal handler, and should not call complex
     * routines at all, but it would be even worse to silently terminate
     * without informing the sysadmin. For this reason, msg(3) was made safe
     * for usage by signal handlers that terminate the process.
     */
    msg_info("terminating on signal %d", sig);

    /*
     * Undocumented: when a process runs with PID 1, Linux won't deliver a
     * signal unless the process specifies a handler (i.e. SIG_DFL is treated
     * as SIG_IGN).
     */
    if (init_mode)
	/* Don't call exit() from a signal handler. */
	_exit(0);

    /*
     * Deliver the signal to ourselves and clean up. XXX We're running as a
     * signal handler and really should not be doing complicated things...
     */
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = SIG_DFL;
    if (sigaction(sig, &action, (struct sigaction *) 0) < 0)
	msg_fatal("%s: sigaction: %m", myname);
    if (kill(pid, sig) < 0)
	msg_fatal("%s: kill myself: %m", myname);
}

/* master_sigsetup - set up signal handlers */

void    master_sigsetup(void)
{
    const char *myname = "master_sigsetup";
    struct sigaction action;
    static int sigs[] = {
	SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGSEGV, SIGTERM,
    };
    unsigned i;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    /*
     * Prepare to kill our children when we receive any of the above signals.
     */
    action.sa_handler = master_sigdeath;
    for (i = 0; i < sizeof(sigs) / sizeof(sigs[0]); i++)
	if (sigaction(sigs[i], &action, (struct sigaction *) 0) < 0)
	    msg_fatal("%s: sigaction(%d): %m", myname, sigs[i]);

#ifdef USE_SIG_PIPE
    if (pipe(master_sig_pipe))
	msg_fatal("pipe: %m");
    non_blocking(SIG_PIPE_WRITE_FD, NON_BLOCKING);
    non_blocking(SIG_PIPE_READ_FD, NON_BLOCKING);
    close_on_exec(SIG_PIPE_WRITE_FD, CLOSE_ON_EXEC);
    close_on_exec(SIG_PIPE_READ_FD, CLOSE_ON_EXEC);
    event_enable_read(SIG_PIPE_READ_FD, master_sig_event, (void *) 0);
#endif

    /*
     * Intercept SIGHUP (re-read config file) and SIGCHLD (child exit).
     */
#ifdef SA_RESTART
    action.sa_flags |= SA_RESTART;
#endif
    action.sa_handler = master_sighup;
    if (sigaction(SIGHUP, &action, (struct sigaction *) 0) < 0)
	msg_fatal("%s: sigaction(%d): %m", myname, SIGHUP);

    action.sa_flags |= SA_NOCLDSTOP;
    action.sa_handler = master_sigchld;
    if (sigaction(SIGCHLD, &action, (struct sigaction *) 0) < 0)
	msg_fatal("%s: sigaction(%d): %m", myname, SIGCHLD);
}