1 /*******************************************************************************
2 
3     Unittests for SignalFD. The tests involve signals (obviously) and forking
4     processes, so are placed in this slowtest module.
5 
6     FLAKY: the unittests in this module are very flaky, as they rely on making
7     various system calls (fork(), waitpid(), epoll_wait(), epoll_ctl(), etc)
8     which could, under certain environmental conditions, fail.
9 
10     Copyright:
11         Copyright (c) 2014-2017 dunnhumby Germany GmbH.
12         All rights reserved.
13 
14     License:
15         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
16         Alternatively, this file may be distributed under the terms of the Tango
17         3-Clause BSD License (see LICENSE_BSD.txt for details).
18 
19 *******************************************************************************/
20 
21 module integrationtest.signalfd.main;
22 
23 import core.stdc.errno;
24 import core.sys.posix.semaphore;
25 import core.sys.posix.sys.mman;
26 
27 import ocean.sys.SignalFD;
28 import ocean.sys.Epoll;
29 import ocean.sys.SignalMask;
30 
31 import ocean.meta.types.Qualifiers;
32 import ocean.core.Array : contains;
33 import ocean.core.Enforce;
34 import ocean.core.Test;
35 
36 import core.sys.posix.signal : kill, pid_t, sigaction, sigaction_t,
37     SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS;
38 import core.sys.posix.stdlib : exit;
39 import core.sys.posix.unistd : fork;
40 import core.sys.posix.sys.wait : waitpid;
41 
42 
43 /*******************************************************************************
44 
45     Class to perform a single test on SignalFD.
46 
47 *******************************************************************************/
48 
49 private class SignalFDTest
50 {
51     /***************************************************************************
52 
53         Semaphore used to synchronize parent and child process. Because child
54         may handle only some of the signals that parent has sent (when handle
55         returns in the middle of the parent sending signals), we need to make
56         sure that we will handle signals in child only after parent sent all
57         signals to the child.
58 
59     ***************************************************************************/
60 
61     private static sem_t* signals_sent_semaphore;
62 
63     /***************************************************************************
64 
65         Maximum signal which can be handled during tests. This is due to the
66         need to use a fixed-length array (see SigHandler.handled()).
67 
68     ***************************************************************************/
69 
70     private static immutable max_signal = 32;
71 
72 
73     /***************************************************************************
74 
75         SignalFD instance being tested. The test does not modify it, only uses
76         it.
77 
78     ***************************************************************************/
79 
80     private SignalFD signal_fd;
81 
82 
83     /***************************************************************************
84 
85         List of signals to be tested but which the SignalFD does *not* handle.
86 
87     ***************************************************************************/
88 
89     private int[] non_handled_signals;
90 
91 
92     /***************************************************************************
93 
94         Child process' id. Set when the process is forked, in run().
95 
96     ***************************************************************************/
97 
98     private pid_t pid;
99 
100 
101     /***************************************************************************
102 
103         Constructor. Sets this.non_handled_signals and runs the test.
104 
105         Params:
106             signal_fd = SignalFD instance to test
107             signals = list of signals to send during test
108 
109     ***************************************************************************/
110 
111     public this ( SignalFD signal_fd, int[] signals )
112     {
113         this.signal_fd = signal_fd;
114 
115         foreach ( signal; signals )
116         {
117             if ( !signal_fd.isRegistered(signal) )
118             {
119                 this.non_handled_signals ~= signal;
120             }
121         }
122 
123         this.run();
124     }
125 
126 
127     /***************************************************************************
128 
129         Returns:
130             the list of signals handled by this.signal_fd
131 
132     ***************************************************************************/
133 
134     private int[] handled_signals ( )
135     {
136         return this.signal_fd.registered_signals;
137     }
138 
139 
140     /***************************************************************************
141 
142         Non-fd signal handler. Required in order to confirm that the SignalFD is
143         not handling signals which it is not supposed to handle.
144 
145     ***************************************************************************/
146 
147     private static class SigHandler
148     {
149         /***********************************************************************
150 
151             Signal to be handled.
152 
153         ***********************************************************************/
154 
155         private int signal;
156 
157 
158         /***********************************************************************
159 
160             Previous handler for this signal. Can be restored by the restore()
161             method.
162 
163         ***********************************************************************/
164 
165         private sigaction_t old_handler;
166 
167 
168         /***********************************************************************
169 
170             Constructor. Registers a new signal handler for the specified
171             signal. The handler, when it fires, simply registers the fact that
172             the signal has been fired, in the static fired_signals array.
173 
174             Params:
175                 signal = signal to handle
176 
177         ***********************************************************************/
178 
179         public this ( int signal )
180         {
181             this.signal = signal;
182             sigaction_t handler;
183             handler.sa_handler = &typeof(this).handler;
184             auto sigaction_res = sigaction(this.signal, &handler, &this.old_handler);
185             enforce(sigaction_res == 0);
186 
187             // Reset the static list of fired signals, so that it is clear at
188             // the beginning of each test
189             foreach ( ref fired; typeof(this).fired_signals )
190             {
191                 fired = false;
192             }
193         }
194 
195 
196         /***********************************************************************
197 
198             Restores the previous handler for this signal.
199 
200         ***********************************************************************/
201 
202         public void restore ( )
203         {
204             auto sigaction_res = sigaction(this.signal, &this.old_handler, null);
205             enforce(sigaction_res == 0);
206         }
207 
208 
209         /***********************************************************************
210 
211             List of flags, set to true when the signal corresponding to the
212             array index has fired and been handled by this class (see
213             handler(), below).
214 
215             Note that the array is static (and fixed-length) because it is
216             accessed from an interrupt handler, thus could be called in the
217             middle of GC activity.
218 
219         ***********************************************************************/
220 
221         private static bool[max_signal] fired_signals;
222 
223 
224         /***********************************************************************
225 
226             Signal handler. Adds the specified signal to the static list of
227             signals which have fired.
228 
229             Params:
230                 signal = signal which fired
231 
232         ***********************************************************************/
233 
234         extern ( C ) private static void handler ( int signal )
235         {
236             if ( signal < max_signal )
237             {
238                 typeof(this).fired_signals[signal] = true;
239             }
240         }
241 
242 
243         /***********************************************************************
244 
245             Tells whether the specified signal has fired.
246 
247             Params:
248                 signal = signal to check
249 
250             Returns:
251                 true if the signal has fired
252 
253         ***********************************************************************/
254 
255         public static bool fired ( int signal )
256         in
257         {
258             assert(signal < max_signal);
259         }
260         do
261         {
262             return typeof(this).fired_signals[signal];
263         }
264     }
265 
266     /***************************************************************************
267 
268         Static constructor, initializes semaphore.
269 
270     ***************************************************************************/
271 
272     static this ()
273     {
274         signals_sent_semaphore = cast(sem_t*)mmap(null,
275                 sem_t.sizeof, PROT_READ | PROT_WRITE,
276                 MAP_SHARED | MAP_ANON, -1, 0);
277 
278         if (signals_sent_semaphore is null)
279         {
280             exit(1);
281         }
282 
283         if (sem_init(signals_sent_semaphore, 1, 0) == -1)
284         {
285             exit(1);
286         }
287     }
288 
289     /***************************************************************************
290 
291         Destroys the unnamed semaphore and deallocates shared memory mapping.
292 
293     ***************************************************************************/
294 
295     static void destroy ()
296     {
297         auto ret = sem_destroy(signals_sent_semaphore);
298         enforce(ret == 0);
299 
300         ret = munmap(signals_sent_semaphore, sem_t.sizeof);
301         enforce(ret == 0);
302     }
303 
304     /***************************************************************************
305 
306         Runs the test, setting up normal (i.e. non-fd) signal handlers for all
307         signals which are to be tested but which are not handled by
308         this.signal_fd, and then forking the process in order to be able to send
309         signals to the child process.
310 
311     ***************************************************************************/
312 
313     private void run ( )
314     {
315         SigHandler[] handlers;
316         auto sigset = SignalSet.getCurrent();
317 
318         scope ( exit )
319         {
320             foreach ( handler; handlers )
321             {
322                 handler.restore();
323             }
324 
325             sigset.remove(this.handled_signals);
326             sigset.mask();
327         }
328 
329         // Set up normal (i.e. non-fd) handler for non-handled signals.
330         foreach ( signal; this.non_handled_signals )
331         {
332             handlers ~= new SigHandler(signal);
333         }
334 
335         // Prevent signal handler from handling SIGHUP.
336         // (This is necessary because the parent process sends this signal to
337         // the child immediately after forking. The child has, however, not
338         // immediately set up its signalfd and epoll instances, meaning that the
339         // signal will be handled by the default signal handler, breaking the
340         // unittest. Masking the signal handler first means that the signal will
341         // fire immediately when the child process' signalfd is set up and
342         // registered with epoll.)
343         // The signals are unmasked again when the test is over (see
344         // scope(exit), above), in order to not affect subsequent tests.
345         sigset.add(this.handled_signals);
346         sigset.block();
347         this.pid = fork();
348         // FLAKY: call to fork() may fail
349         test!(">=")(this.pid, 0); // fork() error
350 
351         if ( this.pid == 0 )
352         {
353             this.child();
354         }
355         else
356         {
357             this.parent();
358         }
359     }
360 
361 
362     /***************************************************************************
363 
364         Parent process' behaviour. Sends all specified signals to the child then
365         waits for the child process to exit and checks its exit status. A
366         non-zero exit status indicates that the child process exited with an
367         exception, meaning that the test failed.
368 
369     ***************************************************************************/
370 
371     private void parent ( )
372     {
373         // Send specified signals to child.
374         foreach ( signal;
375             this.handled_signals ~ this.non_handled_signals )
376         {
377             auto ret = kill(this.pid, signal);
378             enforce(ret == 0);
379         }
380 
381         auto res = sem_post(signals_sent_semaphore);
382         enforce(res == 0);
383 
384         // Wait for the child process to exit. The exit status should be 0,
385         // otherwise an exception has been thrown in the child process.
386         int child_exit_status;
387         int wait_pid_res;
388 
389         do
390         {
391             wait_pid_res = waitpid(this.pid, &child_exit_status, 0);
392         }
393         while (wait_pid_res == -1 && errno == EINTR);
394 
395         // FLAKY: call to waitpid() may fail or return an invalid pid
396         test!("!=")(wait_pid_res, -1); // waitpid() error
397         test!("==")(wait_pid_res, this.pid); // waitpid() returned wrong pid
398         test!("==")(child_exit_status, 0);
399     }
400 
401 
402     /***************************************************************************
403 
404         Child process' behaviour. Sets up an epoll instance and registers the
405         SignalFD instance with it, ready to receive notifications of signals
406         which have fired. When the fd fires, tests are performed to check that
407         the correct set of signals have been handled in the expected way. If all
408         tests succeed, the child process is exited with status code 0.
409         Otherwise, an exception is thrown, which will cause the child process to
410         exit with a non-zero status.
411 
412     ***************************************************************************/
413 
414     private void child ( )
415     {
416         // Register this.signal_fd with epoll
417         Epoll epoll;
418         epoll.create();
419         epoll.ctl(Epoll.CtlOp.EPOLL_CTL_ADD,
420             this.signal_fd.fileHandle(),
421             Epoll.Event.EPOLLIN, this.signal_fd);
422 
423         // Wait for the parent to send signals, then start the epoll event
424         // loop, in order to be notified when the signals have fired
425         int ret;
426 
427         do
428         {
429             ret = sem_wait(signals_sent_semaphore);
430         }
431         while (ret == -1 && errno == EINTR);
432         enforce (ret == 0);
433 
434         static immutable int timeout_ms = 100; // just in case
435         epoll_event_t[1] fired_events;
436         auto epoll_res = epoll.wait(fired_events, timeout_ms);
437 
438         // FLAKY: call to epoll_wait() may fail or return wrong number of events
439         test!("!=")(epoll_res, -1); // epoll_wait() error
440         test!("==")(epoll_res, 1); // one event fired
441         assert(fired_events[0].data.obj is signal_fd, "unexpected event data");
442 
443         // Allow this.signal_fd to handle the signals which have fired
444         SignalFD.SignalInfo[] siginfos;
445         this.signal_fd.handle(siginfos);
446         enforce(
447             siginfos.length == this.handled_signals.length,
448             "handled signals count wrong"
449         );
450 
451         // Create a list of the signals which were handled
452         int[] fired_signals;
453         foreach ( siginfo; siginfos )
454         {
455             fired_signals ~= siginfo.ssi_signo;
456         }
457 
458         // Check that all signals which the fd was expected to handle have been
459         // handled, and that all signals which the fd was not expected to handle
460         // have been handled by the normal signal handler.
461         foreach ( signal; this.handled_signals )
462         {
463             test(fired_signals.contains(signal),
464                 "fd-handled signal not caught by fd");
465             test(!SigHandler.fired(signal),
466                 "fd-handled signal caught by normal handler function");
467         }
468 
469         // Check that all signals which the fd was not expected to handle have
470         // been handled by the normal signal handler, and that all signals which
471         // the fd was expected to handle have not been handled by the normal
472         // signal handler.
473         foreach ( signal; this.non_handled_signals )
474         {
475             test(!fired_signals.contains(signal), "signal caught by fd");
476             test(SigHandler.fired(signal),
477                 "signal not caught by normal handler function");
478         }
479 
480         exit(0);
481     }
482 }
483 
484 
485 
486 /*******************************************************************************
487 
488     Tests where the set of signals being handled by the SignalFD is set in the
489     ctor.
490 
491 *******************************************************************************/
492 
493 version (unittest) {} else
494 void main ( )
495 {
496     // Test a single signal handled by a signalfd
497     new SignalFDTest(new SignalFD([SIGHUP]), [SIGHUP]);
498 
499     // Test multiple signals handled by a signalfd
500     new SignalFDTest(new SignalFD([SIGHUP, SIGINT, SIGQUIT]),
501         [SIGHUP, SIGINT, SIGQUIT]);
502 
503     // Test a single signal handled by a signalfd and a single signal which is
504     // not handled
505     new SignalFDTest(new SignalFD([SIGHUP]), [SIGHUP, SIGINT]);
506 
507     // Test multiple signals handled by a signalfd and multiple signals which
508     // are not handled
509     new SignalFDTest(new SignalFD([SIGHUP, SIGINT, SIGQUIT]),
510         [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS]);
511 
512     // unittests where the set of signals being handled by the SignalFD is set
513     // after construction.
514 
515     // Test a single signal handled by a signalfd
516     {
517         auto signalfd = new SignalFD([]);
518         signalfd.register(SIGHUP);
519         new SignalFDTest(signalfd, [SIGHUP]);
520     }
521 
522     // Test multiple signals handled by a signalfd
523     {
524         auto signalfd = new SignalFD([]);
525         signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
526         new SignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT]);
527     }
528 
529     // Test a single signal handled by a signalfd and a single signal which is
530     // not handled
531     {
532         auto signalfd = new SignalFD([]);
533         signalfd.register(SIGHUP);
534         new SignalFDTest(signalfd, [SIGHUP, SIGINT]);
535     }
536 
537     // Test multiple signals handled by a signalfd and multiple signals which
538     // are not handled
539     {
540         auto signalfd = new SignalFD([]);
541         signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
542         new SignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
543             SIGBUS]);
544     }
545 
546     // Test extending the set of handled signals after an initial test
547     {
548         auto signalfd = new SignalFD([]);
549         signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
550         new SignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT]);
551 
552         signalfd.register(SIGILL).register(SIGABRT).register(SIGBUS);
553         new SignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
554             SIGBUS]);
555     }
556 
557     // Destroy all global resources
558     SignalFDTest.destroy();
559 }