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 moduleintegrationtest.signalfd.main;
22 23 importcore.stdc.errno;
24 importcore.sys.posix.semaphore;
25 importcore.sys.posix.sys.mman;
26 27 importocean.sys.SignalFD;
28 importocean.sys.Epoll;
29 importocean.sys.SignalMask;
30 31 importocean.meta.types.Qualifiers;
32 importocean.core.Array : contains;
33 importocean.core.Enforce;
34 importocean.core.Test;
35 36 importcore.sys.posix.signal : kill, pid_t, sigaction, sigaction_t,
37 SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS;
38 importcore.sys.posix.stdlib : exit;
39 importcore.sys.posix.unistd : fork;
40 importcore.sys.posix.sys.wait : waitpid;
41 42 43 /*******************************************************************************
44 45 Class to perform a single test on SignalFD.
46 47 *******************************************************************************/48 49 privateclassSignalFDTest50 {
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 privatestaticsem_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 privatestaticimmutablemax_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 privateSignalFDsignal_fd;
81 82 83 /***************************************************************************
84 85 List of signals to be tested but which the SignalFD does *not* handle.
86 87 ***************************************************************************/88 89 privateint[] non_handled_signals;
90 91 92 /***************************************************************************
93 94 Child process' id. Set when the process is forked, in run().
95 96 ***************************************************************************/97 98 privatepid_tpid;
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 publicthis ( SignalFDsignal_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 privateint[] handled_signals ( )
135 {
136 returnthis.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 privatestaticclassSigHandler148 {
149 /***********************************************************************
150 151 Signal to be handled.
152 153 ***********************************************************************/154 155 privateintsignal;
156 157 158 /***********************************************************************
159 160 Previous handler for this signal. Can be restored by the restore()
161 method.
162 163 ***********************************************************************/164 165 privatesigaction_told_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 publicthis ( intsignal )
180 {
181 this.signal = signal;
182 sigaction_thandler;
183 handler.sa_handler = &typeof(this).handler;
184 autosigaction_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 at188 // the beginning of each test189 foreach ( reffired; 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 publicvoidrestore ( )
203 {
204 autosigaction_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 privatestaticbool[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 ) privatestaticvoidhandler ( intsignal )
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 publicstaticboolfired ( intsignal )
256 in257 {
258 assert(signal < max_signal);
259 }
260 do261 {
262 returntypeof(this).fired_signals[signal];
263 }
264 }
265 266 /***************************************************************************
267 268 Static constructor, initializes semaphore.
269 270 ***************************************************************************/271 272 staticthis ()
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_semaphoreisnull)
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 staticvoiddestroy ()
296 {
297 autoret = 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 privatevoidrun ( )
314 {
315 SigHandler[] handlers;
316 autosigset = 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 ~= newSigHandler(signal);
333 }
334 335 // Prevent signal handler from handling SIGHUP.336 // (This is necessary because the parent process sends this signal to337 // the child immediately after forking. The child has, however, not338 // immediately set up its signalfd and epoll instances, meaning that the339 // signal will be handled by the default signal handler, breaking the340 // unittest. Masking the signal handler first means that the signal will341 // fire immediately when the child process' signalfd is set up and342 // registered with epoll.)343 // The signals are unmasked again when the test is over (see344 // 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 fail349 test!(">=")(this.pid, 0); // fork() error350 351 if ( this.pid == 0 )
352 {
353 this.child();
354 }
355 else356 {
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 privatevoidparent ( )
372 {
373 // Send specified signals to child.374 foreach ( signal;
375 this.handled_signals ~ this.non_handled_signals )
376 {
377 autoret = kill(this.pid, signal);
378 enforce(ret == 0);
379 }
380 381 autores = 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 intchild_exit_status;
387 intwait_pid_res;
388 389 do390 {
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 pid396 test!("!=")(wait_pid_res, -1); // waitpid() error397 test!("==")(wait_pid_res, this.pid); // waitpid() returned wrong pid398 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 privatevoidchild ( )
415 {
416 // Register this.signal_fd with epoll417 Epollepoll;
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 event424 // loop, in order to be notified when the signals have fired425 intret;
426 427 do428 {
429 ret = sem_wait(signals_sent_semaphore);
430 }
431 while (ret == -1 && errno == EINTR);
432 enforce (ret == 0);
433 434 staticimmutableinttimeout_ms = 100; // just in case435 epoll_event_t[1] fired_events;
436 autoepoll_res = epoll.wait(fired_events, timeout_ms);
437 438 // FLAKY: call to epoll_wait() may fail or return wrong number of events439 test!("!=")(epoll_res, -1); // epoll_wait() error440 test!("==")(epoll_res, 1); // one event fired441 assert(fired_events[0].data.objissignal_fd, "unexpected event data");
442 443 // Allow this.signal_fd to handle the signals which have fired444 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 handled452 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 been459 // handled, and that all signals which the fd was not expected to handle460 // 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 have470 // been handled by the normal signal handler, and that all signals which471 // the fd was expected to handle have not been handled by the normal472 // 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) {} else494 voidmain ( )
495 {
496 // Test a single signal handled by a signalfd497 newSignalFDTest(newSignalFD([SIGHUP]), [SIGHUP]);
498 499 // Test multiple signals handled by a signalfd500 newSignalFDTest(newSignalFD([SIGHUP, SIGINT, SIGQUIT]),
501 [SIGHUP, SIGINT, SIGQUIT]);
502 503 // Test a single signal handled by a signalfd and a single signal which is504 // not handled505 newSignalFDTest(newSignalFD([SIGHUP]), [SIGHUP, SIGINT]);
506 507 // Test multiple signals handled by a signalfd and multiple signals which508 // are not handled509 newSignalFDTest(newSignalFD([SIGHUP, SIGINT, SIGQUIT]),
510 [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS]);
511 512 // unittests where the set of signals being handled by the SignalFD is set513 // after construction.514 515 // Test a single signal handled by a signalfd516 {
517 autosignalfd = newSignalFD([]);
518 signalfd.register(SIGHUP);
519 newSignalFDTest(signalfd, [SIGHUP]);
520 }
521 522 // Test multiple signals handled by a signalfd523 {
524 autosignalfd = newSignalFD([]);
525 signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
526 newSignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT]);
527 }
528 529 // Test a single signal handled by a signalfd and a single signal which is530 // not handled531 {
532 autosignalfd = newSignalFD([]);
533 signalfd.register(SIGHUP);
534 newSignalFDTest(signalfd, [SIGHUP, SIGINT]);
535 }
536 537 // Test multiple signals handled by a signalfd and multiple signals which538 // are not handled539 {
540 autosignalfd = newSignalFD([]);
541 signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
542 newSignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
543 SIGBUS]);
544 }
545 546 // Test extending the set of handled signals after an initial test547 {
548 autosignalfd = newSignalFD([]);
549 signalfd.register(SIGHUP).register(SIGINT).register(SIGQUIT);
550 newSignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT]);
551 552 signalfd.register(SIGILL).register(SIGABRT).register(SIGBUS);
553 newSignalFDTest(signalfd, [SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT,
554 SIGBUS]);
555 }
556 557 // Destroy all global resources558 SignalFDTest.destroy();
559 }