1 /******************************************************************************* 2 3 Epoll-based signal handlers. 4 5 Note that the signals will be handled with a delay of up to single epoll 6 cycle. This is because the signal extension is synced with the 7 EpollSelectDispatcher. This makes it unsuitable to handle critical signals 8 (like `SIGABRT` or `SIGSEGV`) where the application shouldn't be allowed to 9 proceed in the general case; for these cases setup an asynchronous signal 10 handler using `sigaction` instead. 11 12 Copyright: 13 Copyright (c) 2018 dunnhumby Germany GmbH. 14 All rights reserved. 15 16 License: 17 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 18 Alternatively, this file may be distributed under the terms of the Tango 19 3-Clause BSD License (see LICENSE_BSD.txt for details). 20 21 *******************************************************************************/ 22 23 module ocean.application.components.Signals; 24 25 import ocean.meta.types.Qualifiers; 26 27 /// ditto 28 public class Signals 29 { 30 import ocean.core.Verify; 31 import ocean.io.device.Conduit: ISelectable; 32 import ocean.io.device.Device; 33 import ocean.io.device.IODevice; 34 import ocean.io.select.client.model.ISelectClient; 35 import ocean.io.select.protocol.SelectReader; 36 import ocean.sys.Pipe; 37 import ocean.sys.SignalFD; 38 39 import core.sys.posix.signal; 40 41 /*************************************************************************** 42 43 SignalErrnoException. 44 45 ***************************************************************************/ 46 47 alias SignalFD.SignalErrnoException SignalErrnoException; 48 49 /*************************************************************************** 50 51 SelectReader instance used to read the data from pipe written by 52 signal handler. 53 54 ***************************************************************************/ 55 56 private SelectReader reader; 57 58 /*************************************************************************** 59 60 Associative array of old signal handlers, addressed by the signal 61 number. 62 63 ***************************************************************************/ 64 65 private sigaction_t[int] old_signals; 66 67 /*************************************************************************** 68 69 Helper class wrapping Device to InputDevice. Used to read from Pipe 70 via SelectReader. 71 72 ***************************************************************************/ 73 74 private static class InputDeviceWrapper: InputDevice, ISelectable 75 { 76 /*********************************************************************** 77 78 Device instance to read from. 79 80 ***********************************************************************/ 81 82 private Device device; 83 84 /*********************************************************************** 85 86 Constructor. 87 88 Params: 89 device = device instance to read from. 90 91 ***********************************************************************/ 92 93 public this (Device device) 94 { 95 this.device = device; 96 } 97 98 /*********************************************************************** 99 100 Returns: 101 file handle of the underlying device. 102 103 ***********************************************************************/ 104 105 override Handle fileHandle() 106 { 107 return this.device.fileHandle(); 108 } 109 } 110 111 /*************************************************************************** 112 113 InputDevice reading data from the file. 114 115 ***************************************************************************/ 116 117 private InputDeviceWrapper pipe_source; 118 119 /*************************************************************************** 120 121 Pipe used to tranfser the data from the signal handler back to the 122 application. Static as used from the static signal handler 123 124 ***************************************************************************/ 125 126 private static Pipe signal_pipe; 127 128 /// Signal handler delegate type. 129 private alias void delegate ( int[] ) HandlerDg; 130 131 /// Delegate to call when the signal handler fires in epoll. 132 private HandlerDg handler_dg; 133 134 /*************************************************************************** 135 136 Signal handler. Needs to be static method as it is registered as 137 C callback. 138 139 Params: 140 signum = signal being handled 141 142 ***************************************************************************/ 143 144 private static extern(C) void signalHandler (int signum) 145 { 146 typeof(this).signal_pipe.sink.write(cast(ubyte[])(&signum)[0..1]); 147 } 148 149 /*************************************************************************** 150 151 Static constructor. Initialises signal_pipe static member. 152 153 ***************************************************************************/ 154 155 static this ( ) 156 { 157 // Setup a pipe for transferring the signal info. Unbuffered, 158 // as we want these to be available as soon as possible. 159 typeof(this).signal_pipe = new Pipe(0); 160 } 161 162 /*************************************************************************** 163 164 Constructor. Creates the internal signal event. The event (accessible 165 via the selectClient() method) must be registered with epoll. 166 167 Params: 168 handler_dg = delegate to call when the signal handler fires in epoll 169 170 ***************************************************************************/ 171 172 public this ( scope HandlerDg handler_dg ) 173 { 174 verify(handler_dg !is null); 175 this.handler_dg = handler_dg; 176 177 typeof(this).signal_pipe.source.setNonBlock(); 178 this.pipe_source = 179 new InputDeviceWrapper(typeof(this).signal_pipe.source); 180 this.reader = new SelectReader(this.pipe_source, int.sizeof); 181 182 // Make the intention to read 4 bytes (the signal number). This will 183 // read it from the pipe in one of the epoll cycles, after the data 184 // is written into the pipe from signal handler. 185 this.reader.read(&this.handleSignals); 186 } 187 188 /*************************************************************************** 189 190 Ignores the specified signals. 191 192 Params: 193 signals = list of signals to ignore 194 195 Throws: 196 SignalErrnoException if setting up the signal fails 197 198 ***************************************************************************/ 199 200 public void ignore ( in int[] signals ) 201 { 202 this.installSignalHandlers(signals, SIG_IGN); 203 } 204 205 /*************************************************************************** 206 207 Handles the specified signals. 208 209 Params: 210 signals = list of signals to handle 211 212 Throws: 213 SignalErrnoException if setting up the signal fails 214 215 ***************************************************************************/ 216 217 public void handle ( in int[] signals ) 218 { 219 this.installSignalHandlers(signals); 220 } 221 222 /*************************************************************************** 223 224 Restores the original signal handlers. 225 226 Throws: 227 SignalErrnoException if resetting up the signal fails 228 229 ***************************************************************************/ 230 231 public void clear ( ) 232 { 233 foreach (signal, sa; this.old_signals) 234 { 235 if (sigaction(signal, &sa, null) == -1) 236 { 237 throw (new SignalErrnoException).useGlobalErrno("sigaction"); 238 } 239 } 240 } 241 242 /*************************************************************************** 243 244 Returns: 245 ISelectClient interface to register with epoll 246 247 ***************************************************************************/ 248 249 public ISelectClient selectClient ( ) 250 { 251 return this.reader; 252 } 253 254 /*************************************************************************** 255 256 Signal handler delegate, called from epoll when a signal has fired. In 257 turn notifies all registered extensions about the signal. 258 259 Params: 260 signals = info about signals which have fired 261 262 ***************************************************************************/ 263 264 private void handleSignals ( void[] signals_read ) 265 { 266 auto signals = cast(int[])signals_read; 267 this.handler_dg(signals); 268 } 269 270 /*************************************************************************** 271 272 Installs the signal handlers. 273 274 Params: 275 signals = list of signals to handle 276 signal_handler = signal handler to install 277 278 Throws: 279 SignalErrnoException if setting up the signal fails 280 281 ***************************************************************************/ 282 283 private void installSignalHandlers ( in int[] signals, 284 in typeof(sigaction_t.sa_handler) signal_handler = &this.signalHandler) 285 { 286 sigaction_t sa; 287 288 sa.sa_handler = signal_handler; 289 290 if (sigemptyset(&sa.sa_mask) == -1) 291 { 292 throw (new SignalErrnoException).useGlobalErrno("sigemptyset"); 293 } 294 295 foreach (signal; signals) 296 { 297 this.old_signals[signal] = sigaction_t.init; 298 sigaction_t* old_handler = signal in this.old_signals; 299 verify(old_handler !is null); 300 301 if (sigaction(signal, &sa, old_handler) == -1) 302 { 303 throw (new SignalErrnoException).useGlobalErrno("sigaction"); 304 } 305 } 306 } 307 }