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 import ocean.core.Verify;
93 import ocean.meta.types.Qualifiers;
94 import ocean.util.app.Application;
95 import ocean.util.app.model.ExtensibleClassMixin;
96 import ocean.util.app.model.IApplicationExtension;
97 import ocean.util.app.ext.model.ISignalExtExtension;
98 
99 public class SignalExt : IApplicationExtension
100 {
101     import core.sys.posix.signal;
102 
103     import ocean.application.components.Signals;
104     import ocean.io.select.protocol.SelectReader;
105     import ocean.io.device.IODevice;
106     import ocean.io.device.Device;
107     import ocean.io.device.Conduit: ISelectable;
108     import ocean.io.select.client.model.ISelectClient;
109 
110     /***************************************************************************
111 
112         Adds a list of extensions (this.extensions) and methods to handle them.
113         See ExtensibleClassMixin documentation for details.
114 
115     ***************************************************************************/
116 
117     mixin ExtensibleClassMixin!(ISignalExtExtension);
118 
119     /***************************************************************************
120 
121         Signal handlers wrapper.
122 
123     ***************************************************************************/
124 
125     private Signals signals;
126 
127     /***************************************************************************
128 
129         Constructor. Creates the internal signal event, handling the specified
130         signals. The event (accessible via the event() method) must be
131         registered with epoll.
132 
133         The list of signals handled may be extended after construction by
134         calling the register() method.
135 
136         Note that the signals will be handled with a delay of up to single
137         epoll cycle. This is because the signal extension is synced with the
138         EpollSelectDispatcher. This makes it unsuitable to handle critical
139         signals (like `SIGABRT` or `SIGSEGV`) where the application shouldn't
140         be allowed to proceed in the general case; for these cases setup an
141         asynchronous signal handler using `sigaction` instead.
142 
143         Params:
144             signals = list of signals to handle
145             ignore_signals = list of signals to ignore
146 
147         Throws:
148             SignalErrnoException if setting up the signal handling fails
149 
150     ***************************************************************************/
151 
152     public this ( int[] signals, int[] ignore_signals = null )
153     {
154         this.signals = new Signals(&this.handleSignals);
155         this.signals.handle(signals);
156         this.signals.ignore(ignore_signals);
157     }
158 
159     /***************************************************************************
160 
161         Ignores the signals.
162 
163         Params:
164             signals = list of signals to ignore
165 
166         Throws:
167             SignalErrnoException if setting up the signal fails
168 
169     ***************************************************************************/
170 
171     public void ignore ( in int[] signals )
172     {
173         this.signals.ignore(signals);
174     }
175 
176     /***************************************************************************
177 
178         Adds the specified signal to the set of signals handled by this
179         extension.
180 
181         Params:
182             signal = signal to handle
183 
184         Returns:
185             this instance for chaining
186 
187         Throws:
188             SignalErrnoException if the updating the signal handling fails
189 
190     ***************************************************************************/
191 
192     public typeof(this) register ( int signal )
193     {
194         this.signals.handle(cast(int[])(&signal)[0..1]);
195 
196         return this;
197     }
198 
199 
200     /***************************************************************************
201 
202         ISelectClient getter, for registering with epoll.
203 
204         Returns:
205             ISelectClient interface to register with epoll
206 
207     ***************************************************************************/
208 
209     public ISelectClient selectClient ( )
210     {
211         return this.signals.selectClient();
212     }
213 
214 
215     /***************************************************************************
216 
217         Extension order. This extension uses -2_000 because it should be called
218         before the LogExt and StatsExt.
219 
220         Returns:
221             the extension order
222 
223     ***************************************************************************/
224 
225     public override int order ( )
226     {
227         return -2_000;
228     }
229 
230 
231     /***************************************************************************
232 
233         Signal handler delegate, called from epoll when a signal has fired. In
234         turn notifies all registered extensions about the signal.
235 
236         Params:
237             signals = info about signals which have fired
238 
239     ***************************************************************************/
240 
241     private void handleSignals ( int[] signals )
242     {
243         foreach ( ext; this.extensions )
244         {
245             foreach (signal; signals)
246             {
247                 ext.onSignal(signal);
248             }
249         }
250     }
251 
252     /***************************************************************************
253 
254         atExit IApplicationExtension method.
255 
256         Should restore the original signal handlers.
257 
258     ***************************************************************************/
259 
260     public override void atExit ( IApplication app, string[] args, int status,
261             ExitException exception )
262     {
263         this.signals.clear();
264     }
265 
266     /***************************************************************************
267 
268         Unused IApplicationExtension methods.
269 
270         We just need to provide an "empty" implementation to satisfy the
271         interface.
272 
273     ***************************************************************************/
274 
275     public override void preRun ( IApplication app, string[] args )
276     {
277     }
278 
279     /// ditto
280     public override void postRun ( IApplication app, string[] args, int status )
281     {
282     }
283 
284     /// ditto
285     public override ExitException onExitException ( IApplication app, string[] args,
286             ExitException exception )
287     {
288         return exception;
289     }
290 }