1 /*******************************************************************************
2 
3     Application extension which handles signals to the process and calls the
4     onSignal() method of all registered extensions (see ISignalExtExtension).
5     The extension can handle any number of different signals -- depending solely
6     on which signals are specified in the constructor.
7 
8     Note: not only must the extension be registered with the application but its
9     internal ISelectClient (returned by the selectClient() method) must also be
10     registered with an epoll instance! Until the event is registered with epoll
11     and the event loop started, the signal handlers will not be called in
12     response to signals which have occurred.
13 
14     Usage example:
15 
16     ---
17 
18         import ocean.util.app.Application;
19         import ocean.util.app.ext.SignalExt;
20         import ocean.io.select.EpollSelectDispatcher;
21         import core.sys.posix.signal : SIGINT, SIGTERM;
22 
23         // Example application class which does two things:
24         // 1. Owns an instance of SignalExtension and registers some signals
25         //    with it.
26         // 2. Implements ISignalExtExtension to be notified when registered
27         //    signals occur.
28 
29         // It's important to understand that these two things are not connected.
30         // It's perfectly possible for an application class to own a SignalExt
31         // but for another class (indeed other classes) elsewhere to implement
32         // ISignalExtExtension to receive the notification of signals occurring.
33         class MyApp : Application, ISignalExtExtension
34         {
35             private SignalExt signal_ext;
36 
37             this ( )
38             {
39                 super("name", "desc");
40 
41                 // Construct a signal extension instance and tell it which
42                 // signals we're interested in. The list of signals can be
43                 // extended after construction via the register() method.
44                 auto signals = [SIGINT, SIGTERM];
45                 this.signal_ext = new SignalExt(signals);
46 
47                 // Register the signal extension with the application class
48                 // (this).
49                 this.registerExtension(this.signal_ext);
50 
51                 // Register this class with the signal extension so that it will
52                 // be notified (via its onSignal() method, below) when one of
53                 // the registered signals occurs.
54                 this.signal_ext.registerExtension(this);
55             }
56 
57             // Signal handler callback required by ISignalExtExtension. Called
58             // when a signal which has been registered with the signal extension
59             // occurs.
60             override void onSignal ( int signum )
61             {
62             }
63 
64             // Application main method required by Application.
65             override int run ( char[][] args )
66             {
67                 // Important: onSignal() will not be called until the signal
68                 // extension's event has been registered with epoll!
69                 auto epoll = new EpollSelectDispatcher;
70                 epoll.register(this.signal_ext.selectClient());
71                 epoll.eventLoop();
72 
73                 return 0;
74             }
75         }
76 
77     ---
78 
79     Copyright:
80         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
81         All rights reserved.
82 
83     License:
84         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
85         Alternatively, this file may be distributed under the terms of the Tango
86         3-Clause BSD License (see LICENSE_BSD.txt for details).
87 
88 *******************************************************************************/
89 
90 module ocean.util.app.ext.SignalExt;
91 
92 
93 
94 import ocean.meta.types.Qualifiers;
95 import ocean.core.Verify;
96 
97 import ocean.util.app.model.IApplicationExtension;
98 import ocean.util.app.model.ExtensibleClassMixin;
99 
100 import ocean.util.app.Application;
101 
102 import ocean.util.app.ext.model.ISignalExtExtension;
103 
104 import ocean.sys.Pipe;
105 
106 public class SignalExt : IApplicationExtension
107 {
108     import core.sys.posix.signal;
109 
110     import ocean.application.components.Signals;
111 
112     // For SignalErrnoException
113     import ocean.sys.SignalFD;
114 
115     import ocean.io.select.protocol.SelectReader;
116     import ocean.io.device.IODevice;
117     import ocean.io.device.Device;
118     import ocean.io.device.Conduit: ISelectable;
119     import ocean.io.select.client.model.ISelectClient;
120 
121     /***************************************************************************
122 
123         Adds a list of extensions (this.extensions) and methods to handle them.
124         See ExtensibleClassMixin documentation for details.
125 
126     ***************************************************************************/
127 
128     mixin ExtensibleClassMixin!(ISignalExtExtension);
129 
130     /***************************************************************************
131 
132         Signal handlers wrapper.
133 
134     ***************************************************************************/
135 
136     private Signals signals;
137 
138     /***************************************************************************
139 
140         Constructor. Creates the internal signal event, handling the specified
141         signals. The event (accessible via the event() method) must be
142         registered with epoll.
143 
144         The list of signals handled may be extended after construction by
145         calling the register() method.
146 
147         Note that the signals will be handled with a delay of up to single
148         epoll cycle. This is because the signal extension is synced with the
149         EpollSelectDispatcher. This makes it unsuitable to handle critical
150         signals (like `SIGABRT` or `SIGSEGV`) where the application shouldn't
151         be allowed to proceed in the general case; for these cases setup an
152         asynchronous signal handler using `sigaction` instead.
153 
154         Params:
155             signals = list of signals to handle
156             ignore_signals = list of signals to ignore
157 
158         Throws:
159             SignalErrnoException if setting up the signal handling fails
160 
161     ***************************************************************************/
162 
163     public this ( int[] signals, int[] ignore_signals = null )
164     {
165         this.signals = new Signals(&this.handleSignals);
166         this.signals.handle(signals);
167         this.signals.ignore(ignore_signals);
168     }
169 
170     /***************************************************************************
171 
172         Ignores the signals.
173 
174         Params:
175             signals = list of signals to ignore
176 
177         Throws:
178             SignalErrnoException if setting up the signal fails
179 
180     ***************************************************************************/
181 
182     public void ignore ( in int[] signals )
183     {
184         this.signals.ignore(signals);
185     }
186 
187     /***************************************************************************
188 
189         Adds the specified signal to the set of signals handled by this
190         extension.
191 
192         Params:
193             signal = signal to handle
194 
195         Returns:
196             this instance for chaining
197 
198         Throws:
199             SignalErrnoException if the updating the signal handling fails
200 
201     ***************************************************************************/
202 
203     public typeof(this) register ( int signal )
204     {
205         this.signals.handle(cast(int[])(&signal)[0..1]);
206 
207         return this;
208     }
209 
210 
211     /***************************************************************************
212 
213         ISelectClient getter, for registering with epoll.
214 
215         Returns:
216             ISelectClient interface to register with epoll
217 
218     ***************************************************************************/
219 
220     public ISelectClient selectClient ( )
221     {
222         return this.signals.selectClient();
223     }
224 
225 
226     /***************************************************************************
227 
228         Extension order. This extension uses -2_000 because it should be called
229         before the LogExt and StatsExt.
230 
231         Returns:
232             the extension order
233 
234     ***************************************************************************/
235 
236     public override int order ( )
237     {
238         return -2_000;
239     }
240 
241 
242     /***************************************************************************
243 
244         Signal handler delegate, called from epoll when a signal has fired. In
245         turn notifies all registered extensions about the signal.
246 
247         Params:
248             signals = info about signals which have fired
249 
250     ***************************************************************************/
251 
252     private void handleSignals ( int[] signals )
253     {
254         foreach ( ext; this.extensions )
255         {
256             foreach (signal; signals)
257             {
258                 ext.onSignal(signal);
259             }
260         }
261     }
262 
263     /***************************************************************************
264 
265         atExit IApplicationExtension method.
266 
267         Should restore the original signal handlers.
268 
269     ***************************************************************************/
270 
271     public override void atExit ( IApplication app, istring[] args, int status,
272             ExitException exception )
273     {
274         this.signals.clear();
275     }
276 
277     /***************************************************************************
278 
279         Unused IApplicationExtension methods.
280 
281         We just need to provide an "empty" implementation to satisfy the
282         interface.
283 
284     ***************************************************************************/
285 
286     public override void preRun ( IApplication app, istring[] args )
287     {
288     }
289 
290     /// ditto
291     public override void postRun ( IApplication app, istring[] args, int status )
292     {
293     }
294 
295     /// ditto
296     public override ExitException onExitException ( IApplication app, istring[] args,
297             ExitException exception )
298     {
299         return exception;
300     }
301 }