1 /*******************************************************************************
2 
3     Linux signal file descriptor event.
4 
5     signalfd man page follows:
6 
7     signalfd() creates a file descriptor that can be used to accept signals
8     targeted at the caller.  This provides an alternative to the use of a signal
9     handler or sigwaitinfo(2), and has the advantage that the file descriptor may
10     be monitored by select(2), poll(2), and epoll(7).
11 
12     The mask argument specifies the set of signals that the caller wishes to
13     accept via the file descriptor.  This argument is a signal set whose contents
14     can be initialized using the macros described in sigsetops(3).  Normally, the
15     set of signals to be received via the file descriptor should be blocked using
16     sigprocmask(2), to prevent the signals being handled according to their
17     default dispositions.  It is not possible to receive SIGKILL or SIGSTOP
18     signals via a signalfd file descriptor; these signals are silently ignored if
19     specified in mask.
20 
21     If the fd argument is -1, then the call creates a new file descriptor and
22     associates the signal set specified in mask with that descriptor.  If fd is
23     not -1, then it must specify a valid existing signalfd file descriptor, and
24     mask is used to replace the signal set associated with that descriptor.
25 
26     Starting with Linux 2.6.27, the following values may be bitwise ORed in flags
27     to change the behaviour of signalfd():
28 
29     SFD_NONBLOCK  Set the O_NONBLOCK file status flag on the new open file
30                   description.  Using this flag saves extra calls to fcntl(2) to
31                   achieve the same result.
32 
33     SFD_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the new file
34                   descriptor.  See the description of the O_CLOEXEC flag in
35                   open(2) for reasons why this may be useful.
36 
37     In Linux up to version 2.6.26, the flags argument is unused, and must be
38     specified as zero.
39 
40     signalfd() returns a file descriptor that supports the following operations:
41 
42     read(2)
43            If one or more of the signals specified in mask is pending for the
44            process, then the buffer supplied to read(2) is used to return one or
45            more signalfd_siginfo structures (see below) that describe the signals.
46            The read(2) returns information for as many signals as are pending and
47            will fit in the supplied buffer.  The buffer must be at least
48            sizeof(struct signalfd_siginfo) bytes.  The return value of the read(2)
49            is the total number of bytes read.
50 
51            As a consequence of the read(2), the signals are consumed, so that they
52            are no longer pending for the process (i.e., will not be caught by
53            signal handlers, and cannot be accepted using sigwaitinfo(2)).
54 
55            If none of the signals in mask is pending for the process, then the
56            read(2) either blocks until one of the signals in mask is generated for
57            the process, or fails with the error EAGAIN if the file descriptor has
58            been made nonblocking.
59 
60     poll(2), select(2) (and similar)
61            The file descriptor is readable (the select(2) readfds argument; the
62            poll(2) POLLIN flag) if one or more of the signals in mask is pending
63            for the process.
64 
65            The signalfd file descriptor also supports the other file-descriptor
66            multiplexing APIs: pselect(2), ppoll(2), and epoll(7).
67 
68     close(2)
69            When the file descriptor is no longer required it should be closed.
70            When all file descriptors associated with the same signalfd object have
71            been closed, the resources for object are freed by the kernel.
72 
73     Copyright:
74         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
75         All rights reserved.
76 
77     License:
78         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
79         Alternatively, this file may be distributed under the terms of the Tango
80         3-Clause BSD License (see LICENSE_BSD.txt for details).
81 
82 *******************************************************************************/
83 
84 module ocean.sys.SignalFD;
85 
86 import ocean.core.Array : contains;
87 import ocean.core.Exception;
88 import ocean.meta.codegen.Identifier;
89 import ocean.meta.types.Qualifiers;
90 import ocean.sys.ErrnoException;
91 import ocean.sys.SignalMask;
92 import ocean.io.model.IConduit;
93 debug import ocean.io.Stdout;
94 
95 import core.stdc.errno : EAGAIN, EWOULDBLOCK, errno;
96 import core.sys.posix.signal;
97 import core.sys.posix.unistd : read, close;
98 
99 
100 /// Ditto
101 public class SignalFD : ISelectable
102 {
103     import core.sys.linux.sys.signalfd;
104     import ocean.sys.CloseOnExec;
105 
106     /***************************************************************************
107 
108         errno exception type for signal events.
109 
110     ***************************************************************************/
111 
112     public static class SignalErrnoException : ErrnoException { }
113 
114 
115     /***************************************************************************
116 
117         Exception type for signal events.
118 
119     ***************************************************************************/
120 
121     private static class SignalException : Exception
122     {
123         mixin ReusableExceptionImplementation;
124     }
125 
126 
127     /***************************************************************************
128 
129         More convenient alias for signalfd_siginfo.
130 
131     ***************************************************************************/
132 
133     public alias signalfd_siginfo SignalInfo;
134 
135 
136     /***************************************************************************
137 
138         Re-usable exception instances.
139 
140     ***************************************************************************/
141 
142     private SignalErrnoException errno_exception;
143 
144     private SignalException exception;
145 
146 
147     /***************************************************************************
148 
149         Integer file descriptor provided by the operating system and used to
150         manage the signal event. The default value (-1), when passed to the
151         signalfd() function (see register()) causes a new fd to be created.
152 
153     ***************************************************************************/
154 
155     private int fd = -1;
156 
157 
158     /***************************************************************************
159 
160         List of signals being handled by the fd. (Declared as package so that
161         the module slowtest can access it.)
162 
163     ***************************************************************************/
164 
165     private int[] signals;
166 
167 
168     /***************************************************************************
169 
170         Constructor. Creates a signal event file descriptor which will be
171         written to when one of the specified signals fires. The normal signal
172         handling for the specified signals is optionally masked.
173 
174         The list of signals handled may be extended after construction by
175         calling the register() method.
176 
177         Params:
178             signals = list of signals to register
179             mask = if true, default signal handling of the specified signals
180                 will be masked
181 
182         Throws:
183             SignalErrnoException if the creation of the signalfd fails
184 
185     ***************************************************************************/
186 
187     public this ( int[] signals, bool mask = true )
188     {
189         foreach ( signal; signals )
190         {
191             this.register(signal, mask);
192         }
193 
194         this.exception = new SignalException;
195         this.errno_exception = new SignalErrnoException;
196     }
197 
198 
199     /***************************************************************************
200 
201         Destructor. Destroys the signal file descriptor and unmasks all masked
202         signals.
203 
204     ***************************************************************************/
205 
206     ~this ( )
207     {
208         this.unmaskHandledSignals();
209         .close(this.fd);
210     }
211 
212 
213     /***************************************************************************
214 
215         Adds the specified signal to the set of signals handled by this fd. The
216         normal signal handling for the specified signal is optionally masked.
217 
218         Params:
219             signal = signal to register
220             mask = if true, default signal handling of the specified signal will
221                 be masked
222 
223         Returns:
224             this instance for chaining
225 
226         Throws:
227             SignalErrnoException if the creation of the signalfd fails
228 
229     ***************************************************************************/
230 
231     public typeof(this) register ( int signal, bool mask = true )
232     {
233         if ( !this.isRegistered(signal) )
234         {
235             this.signals ~= signal;
236         }
237 
238         SignalSet sigset;
239         sigset.clear;
240         sigset.add(this.signals);
241         auto c_sigset = cast(sigset_t) sigset;
242 
243         this.fd = signalfd(
244             this.fd, &c_sigset, setCloExec(SFD_NONBLOCK, SFD_CLOEXEC)
245         );
246         if ( this.fd == -1 )
247         {
248             scope ( exit ) .errno = 0;
249             auto errnum = .errno;
250             throw this.errno_exception.set(errnum, identifier!(signalfd));
251         }
252 
253         if ( mask )
254         {
255             this.maskHandledSignals();
256         }
257 
258         return this;
259     }
260 
261 
262     /***************************************************************************
263 
264         Unmasks all signals registered with this fd, meaning that the default
265         signal (interrupt) handler will deal with them from now.
266 
267         Warning: this will simply unmask all specified signals. This could be
268         problematic if some completely different module has separately requested
269         that these signals be masked. If this situation ever arises we'll need
270         to come up with a clever solution involving some kind of reference
271         counting or something.
272 
273     ***************************************************************************/
274 
275     public void unmaskHandledSignals ( )
276     {
277         auto sigset = SignalSet.getCurrent();
278         sigset.remove(this.signals);
279         sigset.mask();
280     }
281 
282 
283     /***************************************************************************
284 
285         Masks all signals registered with this fd, meaning that the default
286         signal (interrupt) handler will not deal with them from now.
287 
288     ***************************************************************************/
289 
290     public void maskHandledSignals ( )
291     {
292         SignalSet sigset;
293         sigset.clear();
294         sigset.add(this.signals);
295         sigset.block();
296     }
297 
298 
299     /***************************************************************************
300 
301         Required by ISelectable interface.
302 
303         Returns:
304             file descriptor used to manage signal event
305 
306     ***************************************************************************/
307 
308     public Handle fileHandle ( )
309     {
310         return cast(Handle)this.fd;
311     }
312 
313 
314     /***************************************************************************
315 
316         Checks whether the specified signal is registered to be handled by this
317         fd.
318 
319         Params:
320             signal = code of signal to check
321 
322         Returns:
323             true if the specified signal is handled by this fd
324 
325     ***************************************************************************/
326 
327     public bool isRegistered ( int signal )
328     {
329         return !!this.signals.contains(signal);
330     }
331 
332 
333     /***************************************************************************
334 
335         Getter for the list of registered signals. Changing the contents of the
336         returned array may lead to undefined behaviour.
337 
338         Returns:
339             list of signals which are registered to be handled by this fd
340 
341     ***************************************************************************/
342 
343     public int[] registered_signals ( )
344     {
345         return this.signals;
346     }
347 
348 
349     /***************************************************************************
350 
351         Should be called when the signal event has fired. Fills in the provided
352         array with structs containing information about which signals fired.
353 
354         Params:
355             siginfos = output array of structs containing information about
356                 signals which fired
357 
358         Throws:
359             if an error occurs while reading from the signalfd
360 
361     ***************************************************************************/
362 
363     public void handle ( ref SignalInfo[] siginfos )
364     {
365         siginfos.length = 0;
366         assumeSafeAppend(siginfos);
367 
368         SignalInfo siginfo;
369 
370         ssize_t bytes;
371         do
372         {
373             /*
374              * Reading from a signal FD has a special meaning and satisfies
375              * certain assumptions: A read() call for siginfo.sizeof bytes
376              * attempts to consume one of the pending signals. If it succeeds
377              * then it is guaranteed to have read all bytes. If there are no
378              * signals pending -- usually because we consumed all pending
379              * signals by having called read(siginfo.sizeof) as often as there
380              * were signals pending, but also if this method was called without
381              * the signal FD having fired -- then read() fails with
382              * EAGAIN/EWOULDBLOCK.
383              */
384             bytes = .read(this.fd, &siginfo, siginfo.sizeof);
385             if ( bytes == siginfo.sizeof )
386             {
387                 siginfos ~= siginfo;
388             }
389             else if ( bytes < 0 )
390             {
391                 // Check for errnum == EAGAIN/EWOUDLBLOCK which indicates the
392                 // end of the list of signals (that is, we either read the whole
393                 // list or no signal was actually pending in the first place).
394 
395                 scope ( exit ) .errno = 0;
396                 auto errnum = .errno;
397 
398                 switch ( errnum )
399                 {
400                     case EAGAIN:
401                     break; // The loop will exit because bytes < 0.
402 
403                     static if ( EAGAIN != EWOULDBLOCK )
404                     {
405                         case EWOUDLBLOCK:
406                         break; // The loop will exit because bytes < 0.
407                     }
408 
409                     default:
410                         throw this.errno_exception.set(errnum, identifier!(.read));
411                 }
412             }
413             else
414             {
415                 // This should not happen: read(siginfo.sizeof) from a signal FD
416                 // returns either siginfo.sizeof for success or -1 for failure.
417 
418                 throw this.exception.set("read invalid bytes from signalfd");
419             }
420         }
421         while ( bytes > 0 );
422     }
423 }