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 }