fixed ssl.c bug when ssl backend returns IO_BLOCKED but IO engine doesn't get informe...
[ircu2.10.12-pk.git] / ircd / ircd_signal.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_signal.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 /** @file
21  * @brief Signal handlers for ircu.
22  * @version $Id: ircd_signal.c 1633 2006-03-25 03:46:56Z entrope $
23  */
24 #include "config.h"
25
26 #include "ircd.h"
27 #include "ircd_alloc.h"
28 #include "ircd_events.h"
29 #include "ircd_log.h"
30 #include "ircd_signal.h"
31 #include "s_conf.h"
32
33 /* #include <assert.h> -- Now using assert in ircd_log.h */
34 #include <signal.h>
35 #include <string.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38
39 /** Records a function to be called when a child process terminates. */
40 struct ChildRecord {
41   struct ChildRecord *next;
42   SigChldCallBack call;
43   void *datum;
44   pid_t cpid;
45 };
46
47 /** Counts various types of signals that we receive. */
48 static struct tag_SignalCounter {
49   unsigned int alrm; /**< Received SIGALRM count. */
50   unsigned int hup;  /**< Received SIGHUP count. */
51   unsigned int chld; /**< Received SIGCHLD count. */
52 } SignalCounter;
53
54 /** Event generator for SIGHUP. */
55 static struct Signal sig_hup;
56 /** Event generator for SIGINT. */
57 static struct Signal sig_int;
58 /** Event generator for SIGTERM. */
59 static struct Signal sig_term;
60 /** Event generator for SIGCHLD. */
61 static struct Signal sig_chld;
62 /** List of active child process callback requests. */
63 static struct ChildRecord *children;
64 /** List of inactive (free) child records. */
65 static struct ChildRecord *crec_freelist;
66
67 /* Make sure we have a definition for SIGCHLD. */
68 #if !defined(SIGCHLD)
69 # define SIGCHLD SIGCLD
70 #endif
71
72 /** Signal handler for SIGALRM.
73  * @param[in] sig Signal number (ignored).
74  */
75 static void sigalrm_handler(int sig)
76 {
77   ++SignalCounter.alrm;
78 }
79
80 /** Signal callback for SIGTERM.
81  * @param[in] ev Signal event descriptor.
82  */
83 static void sigterm_callback(struct Event* ev)
84 {
85   assert(0 != ev_signal(ev));
86   assert(ET_SIGNAL == ev_type(ev));
87   assert(SIGTERM == sig_signal(ev_signal(ev)));
88   assert(SIGTERM == ev_data(ev));
89
90   server_die("received signal SIGTERM");
91 }
92
93 /** Signal callback for SIGHUP.
94  * @param[in] ev Signal event descriptor.
95  */
96 static void sighup_callback(struct Event* ev)
97 {
98   assert(0 != ev_signal(ev));
99   assert(ET_SIGNAL == ev_type(ev));
100   assert(SIGHUP == sig_signal(ev_signal(ev)));
101   assert(SIGHUP == ev_data(ev));
102
103   ++SignalCounter.hup;
104   rehash(&me, 1);
105 }
106
107 /** Signal callback for SIGINT.
108  * @param[in] ev Signal event descriptor.
109  */
110 static void sigint_callback(struct Event* ev)
111 {
112   assert(0 != ev_signal(ev));
113   assert(ET_SIGNAL == ev_type(ev));
114   assert(SIGINT == sig_signal(ev_signal(ev)));
115   assert(SIGINT == ev_data(ev));
116
117   server_restart("caught signal: SIGINT");
118 }
119
120 /** Allocate a child callback record.
121  * @return Newly allocated callback record.
122  */
123 static struct ChildRecord *alloc_crec(void)
124 {
125   struct ChildRecord *crec;
126
127   if (crec_freelist)
128   {
129     crec = crec_freelist;
130     crec_freelist = crec->next;
131   }
132   else
133   {
134     crec = MyCalloc(1, sizeof(*crec));
135   }
136
137   memset(crec, 0, sizeof(*crec));
138   crec->next = NULL;
139   return crec;
140 }
141
142 /** Release \a crec, which is after \a prev.
143  * @param[in] crec Child process callback record to release.
144  */
145 static void release_crec(struct ChildRecord *crec)
146 {
147   memset(crec, 0, sizeof(*crec));
148   crec->next = crec_freelist;
149   crec_freelist = crec;
150 }
151
152 /** Register a function to be called when a child process terminates.
153  * @param[in] child Child process ID.
154  * @param[in] call Function to call when process \a child terminates.
155  * @param[in] datum Additional data parameter to pass to \a call.
156  */
157 void register_child(pid_t child, SigChldCallBack call, void *datum)
158 {
159   struct ChildRecord *crec;
160
161   crec = alloc_crec();
162   /* Link into #children list. */
163   crec->next = children;
164   children = crec;
165   /* Fill in user fields. */
166   crec->call = call;
167   crec->datum = datum;
168   crec->cpid = child;
169 }
170
171 /** Unregister all callbacks for a child process, optionally calling
172  * them first.
173  * @param[in] child Child process ID to unregister.
174  * @param[in] do_call If non-zero, make the callbacks.
175  * @param[in] status If \a do_call is non-zero, the child's exit status.
176  */
177 static void do_unregister_child(pid_t child, int do_call, int status)
178 {
179   struct ChildRecord *crec = children;
180   struct ChildRecord *prev = NULL;
181
182   while (crec != NULL)
183   {
184     if (crec->cpid == child)
185     {
186       if (do_call)
187         crec->call(child, crec->datum, status);
188
189       if (prev)
190         prev->next = crec->next;
191       else
192         children = crec->next;
193
194       release_crec(crec);
195     }
196     else
197       prev = crec;
198     crec = prev ? prev->next : children;
199   }
200 }
201
202 /** Unregister all callbacks for a child process.
203  * @param[in] child Child process ID to unregister.
204  */
205 void unregister_child(pid_t child)
206 {
207   do_unregister_child(child, 0, 0);
208 }
209
210 /** Signal handler for SIGCHLD.
211  * @param[in] ev Signal event descriptor.
212  */
213 static void sigchld_callback(struct Event *ev)
214 {
215   pid_t cpid;
216   int status;
217
218   ++SignalCounter.chld;
219   do {
220     cpid = waitpid(-1, &status, WNOHANG);
221     if (cpid > 0)
222       do_unregister_child(cpid, 1, status);
223   } while (cpid > 0);
224 }
225
226 /** Register all necessary signal handlers. */
227 void setup_signals(void)
228 {
229   struct sigaction act;
230
231   act.sa_handler = SIG_IGN;
232   act.sa_flags = 0;
233   sigemptyset(&act.sa_mask);
234   sigaddset(&act.sa_mask, SIGPIPE);
235   sigaddset(&act.sa_mask, SIGALRM);
236 #ifdef  SIGWINCH
237   sigaddset(&act.sa_mask, SIGWINCH);
238   sigaction(SIGWINCH, &act, 0);
239 #endif
240   sigaction(SIGPIPE, &act, 0);
241
242   act.sa_handler = sigalrm_handler;
243   sigaction(SIGALRM, &act, 0);
244
245   signal_add(&sig_hup, sighup_callback, 0, SIGHUP);
246   signal_add(&sig_int, sigint_callback, 0, SIGINT);
247   signal_add(&sig_term, sigterm_callback, 0, SIGTERM);
248   signal_add(&sig_chld, sigchld_callback, 0, SIGCHLD);
249
250   /*
251    * At least on Apollo sr10.1 it seems continuing system calls
252    * after signal is the default. The following 'siginterrupt'
253    * should change that default to interrupting calls.
254    */
255   siginterrupt(SIGALRM, 1);
256 }
257
258 /** Kill and clean up all child processes. */
259 void reap_children(void)
260 {
261   /* Send SIGTERM to all children in process group.  Sleep for a
262    * second to let them exit before we try to clean them up.
263    */
264   kill(0, SIGTERM);
265   sleep(1);
266   sigchld_callback(NULL);
267 }