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 
87 import ocean.core.Exception;
88 import ocean.sys.ErrnoException;
89 
90 import ocean.sys.SignalMask;
91 
92 import ocean.meta.codegen.Identifier;
93 
94 import ocean.io.model.IConduit;
95 
96 import core.sys.posix.signal;
97 
98 import core.sys.posix.unistd : read, close;
99 
100 import core.stdc.errno : EAGAIN, EWOULDBLOCK, errno;
101 
102 import ocean.core.Array : contains;
103 
104 import ocean.transition;
105 
106 debug import ocean.io.Stdout;
107 
108 /*******************************************************************************
109 
110     Signal fd class
111 
112 *******************************************************************************/
113 
114 public class SignalFD : ISelectable
115 {
116     import core.sys.linux.sys.signalfd;
117     import ocean.sys.CloseOnExec;
118 
119     /***************************************************************************
120 
121         errno exception type for signal events.
122 
123     ***************************************************************************/
124 
125     public static class SignalErrnoException : ErrnoException { }
126 
127 
128     /***************************************************************************
129 
130         Exception type for signal events.
131 
132     ***************************************************************************/
133 
134     private static class SignalException : Exception
135     {
136         mixin ReusableExceptionImplementation;
137     }
138 
139 
140     /***************************************************************************
141 
142         More convenient alias for signalfd_siginfo.
143 
144     ***************************************************************************/
145 
146     public alias signalfd_siginfo SignalInfo;
147 
148 
149     /***************************************************************************
150 
151         Re-usable exception instances.
152 
153     ***************************************************************************/
154 
155     private SignalErrnoException errno_exception;
156 
157     private SignalException exception;
158 
159 
160     /***************************************************************************
161 
162         Integer file descriptor provided by the operating system and used to
163         manage the signal event. The default value (-1), when passed to the
164         signalfd() function (see register()) causes a new fd to be created.
165 
166     ***************************************************************************/
167 
168     private int fd = -1;
169 
170 
171     /***************************************************************************
172 
173         List of signals being handled by the fd. (Declared as package so that
174         the module slowtest can access it.)
175 
176     ***************************************************************************/
177 
178     private int[] signals;
179 
180 
181     /***************************************************************************
182 
183         Constructor. Creates a signal event file descriptor which will be
184         written to when one of the specified signals fires. The normal signal
185         handling for the specified signals is optionally masked.
186 
187         The list of signals handled may be extended after construction by
188         calling the register() method.
189 
190         Params:
191             signals = list of signals to register
192             mask = if true, default signal handling of the specified signals
193                 will be masked
194 
195         Throws:
196             SignalErrnoException if the creation of the signalfd fails
197 
198     ***************************************************************************/
199 
200     public this ( int[] signals, bool mask = true )
201     {
202         foreach ( signal; signals )
203         {
204             this.register(signal, mask);
205         }
206 
207         this.exception = new SignalException;
208         this.errno_exception = new SignalErrnoException;
209     }
210 
211 
212     /***************************************************************************
213 
214         Destructor. Destroys the signal file descriptor and unmasks all masked
215         signals.
216 
217     ***************************************************************************/
218 
219     ~this ( )
220     {
221         this.unmaskHandledSignals();
222         .close(this.fd);
223     }
224 
225 
226     /***************************************************************************
227 
228         Adds the specified signal to the set of signals handled by this fd. The
229         normal signal handling for the specified signal is optionally masked.
230 
231         Params:
232             signal = signal to register
233             mask = if true, default signal handling of the specified signal will
234                 be masked
235 
236         Returns:
237             this instance for chaining
238 
239         Throws:
240             SignalErrnoException if the creation of the signalfd fails
241 
242     ***************************************************************************/
243 
244     public typeof(this) register ( int signal, bool mask = true )
245     {
246         if ( !this.isRegistered(signal) )
247         {
248             this.signals ~= signal;
249         }
250 
251         SignalSet sigset;
252         sigset.clear;
253         sigset.add(this.signals);
254         auto c_sigset = cast(sigset_t) sigset;
255 
256         this.fd = signalfd(
257             this.fd, &c_sigset, setCloExec(SFD_NONBLOCK, SFD_CLOEXEC)
258         );
259         if ( this.fd == -1 )
260         {
261             scope ( exit ) .errno = 0;
262             auto errnum = .errno;
263             throw this.errno_exception.set(errnum, identifier!(signalfd));
264         }
265 
266         if ( mask )
267         {
268             this.maskHandledSignals();
269         }
270 
271         return this;
272     }
273 
274 
275     /***************************************************************************
276 
277         Unmasks all signals registered with this fd, meaning that the default
278         signal (interrupt) handler will deal with them from now.
279 
280         Warning: this will simply unmask all specified signals. This could be
281         problematic if some completely different module has separately requested
282         that these signals be masked. If this situation ever arises we'll need
283         to come up with a clever solution involving some kind of reference
284         counting or something.
285 
286     ***************************************************************************/
287 
288     public void unmaskHandledSignals ( )
289     {
290         auto sigset = SignalSet.getCurrent();
291         sigset.remove(this.signals);
292         sigset.mask();
293     }
294 
295 
296     /***************************************************************************
297 
298         Masks all signals registered with this fd, meaning that the default
299         signal (interrupt) handler will not deal with them from now.
300 
301     ***************************************************************************/
302 
303     public void maskHandledSignals ( )
304     {
305         SignalSet sigset;
306         sigset.clear();
307         sigset.add(this.signals);
308         sigset.block();
309     }
310 
311 
312     /***************************************************************************
313 
314         Required by ISelectable interface.
315 
316         Returns:
317             file descriptor used to manage signal event
318 
319     ***************************************************************************/
320 
321     public Handle fileHandle ( )
322     {
323         return cast(Handle)this.fd;
324     }
325 
326 
327     /***************************************************************************
328 
329         Checks whether the specified signal is registered to be handled by this
330         fd.
331 
332         Params:
333             signal = code of signal to check
334 
335         Returns:
336             true if the specified signal is handled by this fd
337 
338     ***************************************************************************/
339 
340     public bool isRegistered ( int signal )
341     {
342         return !!this.signals.contains(signal);
343     }
344 
345 
346     /***************************************************************************
347 
348         Getter for the list of registered signals. Changing the contents of the
349         returned array may lead to undefined behaviour.
350 
351         Returns:
352             list of signals which are registered to be handled by this fd
353 
354     ***************************************************************************/
355 
356     public int[] registered_signals ( )
357     {
358         return this.signals;
359     }
360 
361 
362     /***************************************************************************
363 
364         Should be called when the signal event has fired. Fills in the provided
365         array with structs containing information about which signals fired.
366 
367         Params:
368             siginfos = output array of structs containing information about
369                 signals which fired
370 
371         Throws:
372             if an error occurs while reading from the signalfd
373 
374     ***************************************************************************/
375 
376     public void handle ( ref SignalInfo[] siginfos )
377     {
378         siginfos.length = 0;
379         enableStomping(siginfos);
380 
381         SignalInfo siginfo;
382 
383         ssize_t bytes;
384         do
385         {
386             /*
387              * Reading from a signal FD has a special meaning and satisfies
388              * certain assumptions: A read() call for siginfo.sizeof bytes
389              * attempts to consume one of the pending signals. If it succeeds
390              * then it is guaranteed to have read all bytes. If there are no
391              * signals pending -- usually because we consumed all pending
392              * signals by having called read(siginfo.sizeof) as often as there
393              * were signals pending, but also if this method was called without
394              * the signal FD having fired -- then read() fails with
395              * EAGAIN/EWOULDBLOCK.
396              */
397             bytes = .read(this.fd, &siginfo, siginfo.sizeof);
398             if ( bytes == siginfo.sizeof )
399             {
400                 siginfos ~= siginfo;
401             }
402             else if ( bytes < 0 )
403             {
404                 // Check for errnum == EAGAIN/EWOUDLBLOCK which indicates the
405                 // end of the list of signals (that is, we either read the whole
406                 // list or no signal was actually pending in the first place).
407 
408                 scope ( exit ) .errno = 0;
409                 auto errnum = .errno;
410 
411                 switch ( errnum )
412                 {
413                     case EAGAIN:
414                     break; // The loop will exit because bytes < 0.
415 
416                     static if ( EAGAIN != EWOULDBLOCK )
417                     {
418                         case EWOUDLBLOCK:
419                         break; // The loop will exit because bytes < 0.
420                     }
421 
422                     default:
423                         throw this.errno_exception.set(errnum, identifier!(.read));
424                 }
425             }
426             else
427             {
428                 // This should not happen: read(siginfo.sizeof) from a signal FD
429                 // returns either siginfo.sizeof for success or -1 for failure.
430 
431                 throw this.exception.set("read invalid bytes from signalfd");
432             }
433         }
434         while ( bytes > 0 );
435     }
436 }