ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SignalHandlerTest.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the Monolog package.
5  *
6  * (c) Jordi Boggiano <j.boggiano@seld.be>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11 
12 namespace Monolog;
13 
17 
23 {
24 
26  private $blockedSignals;
27  private $signalHandlers;
28 
29  protected function setUp()
30  {
31  $this->signalHandlers = array();
32  if (extension_loaded('pcntl')) {
33  if (function_exists('pcntl_async_signals')) {
34  $this->asyncSignalHandling = pcntl_async_signals();
35  }
36  if (function_exists('pcntl_sigprocmask')) {
37  pcntl_sigprocmask(SIG_BLOCK, array(), $this->blockedSignals);
38  }
39  }
40  }
41 
42  protected function tearDown()
43  {
44  if ($this->asyncSignalHandling !== null) {
45  pcntl_async_signals($this->asyncSignalHandling);
46  }
47  if ($this->blockedSignals !== null) {
48  pcntl_sigprocmask(SIG_SETMASK, $this->blockedSignals);
49  }
50  if ($this->signalHandlers) {
51  pcntl_signal_dispatch();
52  foreach ($this->signalHandlers as $signo => $handler) {
53  pcntl_signal($signo, $handler);
54  }
55  }
56  }
57 
58  private function setSignalHandler($signo, $handler = SIG_DFL) {
59  if (function_exists('pcntl_signal_get_handler')) {
60  $this->signalHandlers[$signo] = pcntl_signal_get_handler($signo);
61  } else {
62  $this->signalHandlers[$signo] = SIG_DFL;
63  }
64  $this->assertTrue(pcntl_signal($signo, $handler));
65  }
66 
67  public function testHandleSignal()
68  {
69  $logger = new Logger('test', array($handler = new TestHandler));
70  $errHandler = new SignalHandler($logger);
71  $signo = 2; // SIGINT.
72  $siginfo = array('signo' => $signo, 'errno' => 0, 'code' => 0);
73  $errHandler->handleSignal($signo, $siginfo);
74  $this->assertCount(1, $handler->getRecords());
75  $this->assertTrue($handler->hasCriticalRecords());
76  $records = $handler->getRecords();
77  $this->assertSame($siginfo, $records[0]['context']);
78  }
79 
89  public function testRegisterSignalHandler()
90  {
91  // SIGCONT and SIGURG should be ignored by default.
92  if (!defined('SIGCONT') || !defined('SIGURG')) {
93  $this->markTestSkipped('This test requires the SIGCONT and SIGURG pcntl constants.');
94  }
95 
96  $this->setSignalHandler(SIGCONT, SIG_IGN);
97  $this->setSignalHandler(SIGURG, SIG_IGN);
98 
99  $logger = new Logger('test', array($handler = new TestHandler));
100  $errHandler = new SignalHandler($logger);
101  $pid = posix_getpid();
102 
103  $this->assertTrue(posix_kill($pid, SIGURG));
104  $this->assertTrue(pcntl_signal_dispatch());
105  $this->assertCount(0, $handler->getRecords());
106 
107  $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, false);
108 
109  $this->assertTrue(posix_kill($pid, SIGCONT));
110  $this->assertTrue(pcntl_signal_dispatch());
111  $this->assertCount(0, $handler->getRecords());
112 
113  $this->assertTrue(posix_kill($pid, SIGURG));
114  $this->assertTrue(pcntl_signal_dispatch());
115  $this->assertCount(1, $handler->getRecords());
116  $this->assertTrue($handler->hasInfoThatContains('SIGURG'));
117  }
118 
126  public function testRegisterDefaultPreviousSignalHandler($signo, $callPrevious, $expected)
127  {
128  $this->setSignalHandler($signo, SIG_DFL);
129 
130  $path = tempnam(sys_get_temp_dir(), 'monolog-');
131  $this->assertNotFalse($path);
132 
133  $pid = pcntl_fork();
134  if ($pid === 0) { // Child.
135  $streamHandler = new StreamHandler($path);
136  $streamHandler->setFormatter($this->getIdentityFormatter());
137  $logger = new Logger('test', array($streamHandler));
138  $errHandler = new SignalHandler($logger);
139  $errHandler->registerSignalHandler($signo, LogLevel::INFO, $callPrevious, false, false);
140  pcntl_sigprocmask(SIG_SETMASK, array(SIGCONT));
141  posix_kill(posix_getpid(), $signo);
142  pcntl_signal_dispatch();
143  // If $callPrevious is true, SIGINT should terminate by this line.
144  pcntl_sigprocmask(SIG_BLOCK, array(), $oldset);
145  file_put_contents($path, implode(' ', $oldset), FILE_APPEND);
146  posix_kill(posix_getpid(), $signo);
147  pcntl_signal_dispatch();
148  exit();
149  }
150 
151  $this->assertNotSame(-1, $pid);
152  $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
153  $this->assertNotSame(-1, $status);
154  $this->assertSame($expected, file_get_contents($path));
155  }
156 
157  public function defaultPreviousProvider()
158  {
159  if (!defined('SIGCONT') || !defined('SIGINT') || !defined('SIGURG')) {
160  return array();
161  }
162 
163  return array(
164  array(SIGINT, false, 'Program received signal SIGINT'.SIGCONT.'Program received signal SIGINT'),
165  array(SIGINT, true, 'Program received signal SIGINT'),
166  array(SIGURG, false, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
167  array(SIGURG, true, 'Program received signal SIGURG'.SIGCONT.'Program received signal SIGURG'),
168  );
169  }
170 
176  public function testRegisterCallablePreviousSignalHandler($callPrevious)
177  {
178  $this->setSignalHandler(SIGURG, SIG_IGN);
179 
180  $logger = new Logger('test', array($handler = new TestHandler));
181  $errHandler = new SignalHandler($logger);
182  $previousCalled = 0;
183  pcntl_signal(SIGURG, function ($signo, array $siginfo = null) use (&$previousCalled) {
184  ++$previousCalled;
185  });
186  $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, $callPrevious, false, false);
187  $this->assertTrue(posix_kill(posix_getpid(), SIGURG));
188  $this->assertTrue(pcntl_signal_dispatch());
189  $this->assertCount(1, $handler->getRecords());
190  $this->assertTrue($handler->hasInfoThatContains('SIGURG'));
191  $this->assertSame($callPrevious ? 1 : 0, $previousCalled);
192  }
193 
194  public function callablePreviousProvider()
195  {
196  return array(
197  array(false),
198  array(true),
199  );
200  }
201 
208  public function testRegisterSyscallRestartingSignalHandler($restartSyscalls)
209  {
210  $this->setSignalHandler(SIGURG, SIG_IGN);
211 
212  $parentPid = posix_getpid();
213  $microtime = microtime(true);
214 
215  $pid = pcntl_fork();
216  if ($pid === 0) { // Child.
217  usleep(100000);
218  posix_kill($parentPid, SIGURG);
219  usleep(100000);
220  exit();
221  }
222 
223  $this->assertNotSame(-1, $pid);
224  $logger = new Logger('test', array($handler = new TestHandler));
225  $errHandler = new SignalHandler($logger);
226  $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, $restartSyscalls, false);
227  if ($restartSyscalls) {
228  // pcntl_wait is expected to be restarted after the signal handler.
229  $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
230  } else {
231  // pcntl_wait is expected to be interrupted when the signal handler is invoked.
232  $this->assertSame(-1, pcntl_waitpid($pid, $status));
233  }
234  $this->assertSame($restartSyscalls, microtime(true) - $microtime > 0.15);
235  $this->assertTrue(pcntl_signal_dispatch());
236  $this->assertCount(1, $handler->getRecords());
237  if ($restartSyscalls) {
238  // The child has already exited.
239  $this->assertSame(-1, pcntl_waitpid($pid, $status));
240  } else {
241  // The child has not exited yet.
242  $this->assertNotSame(-1, pcntl_waitpid($pid, $status));
243  }
244  }
245 
246  public function restartSyscallsProvider()
247  {
248  return array(
249  array(false),
250  array(true),
251  array(false),
252  array(true),
253  );
254  }
255 
261  public function testRegisterAsyncSignalHandler($initialAsync, $desiredAsync, $expectedBefore, $expectedAfter)
262  {
263  $this->setSignalHandler(SIGURG, SIG_IGN);
264  pcntl_async_signals($initialAsync);
265 
266  $logger = new Logger('test', array($handler = new TestHandler));
267  $errHandler = new SignalHandler($logger);
268  $errHandler->registerSignalHandler(SIGURG, LogLevel::INFO, false, false, $desiredAsync);
269  $this->assertTrue(posix_kill(posix_getpid(), SIGURG));
270  $this->assertCount($expectedBefore, $handler->getRecords());
271  $this->assertTrue(pcntl_signal_dispatch());
272  $this->assertCount($expectedAfter, $handler->getRecords());
273  }
274 
275  public function asyncProvider()
276  {
277  return array(
278  array(false, false, 0, 1),
279  array(false, null, 0, 1),
280  array(false, true, 1, 1),
281  array(true, false, 0, 1),
282  array(true, null, 1, 1),
283  array(true, true, 1, 1),
284  );
285  }
286 
287 }
$path
Definition: aliased.php:25
Monolog log channel.
Definition: Logger.php:28
testRegisterCallablePreviousSignalHandler($callPrevious)
callablePreviousProvider testRegisterSignalHandler function pcntl_signal_get_handler ...
testRegisterAsyncSignalHandler($initialAsync, $desiredAsync, $expectedBefore, $expectedAfter)
asyncProvider testRegisterDefaultPreviousSignalHandler function pcntl_async_signals ...
testRegisterSignalHandler()
testHandleSignal extension pcntl extension posix function pcntl_signal function pcntl_signal_disp...
$records
Definition: simple_test.php:22
setSignalHandler($signo, $handler=SIG_DFL)
testRegisterDefaultPreviousSignalHandler($signo, $callPrevious, $expected)
defaultPreviousProvider testRegisterSignalHandler function pcntl_fork function pcntl_sigprocmask ...
testRegisterSyscallRestartingSignalHandler($restartSyscalls)
restartSyscallsProvider testRegisterDefaultPreviousSignalHandler function pcntl_fork function pcnt...
exit
Definition: backend.php:16
Stores to any stream resource.
Used for testing purposes.
Definition: TestHandler.php:66
Monolog POSIX signal handler.
$handler