mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Test PID writing
This commit is contained in:
parent
2767ab755e
commit
0bb5e916d2
2 changed files with 73 additions and 14 deletions
|
@ -7,7 +7,7 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\Arsse\Service;
|
namespace JKingWeb\Arsse\Service;
|
||||||
|
|
||||||
class Daemon {
|
class Daemon {
|
||||||
protected const PID_PATTERN = '/^([1-9]\d{0,77})*$/D'; // no more than 78 digits (256-bit unsigned integer), starting with a digit other than zero
|
protected const PID_PATTERN = '/^([1-9]\d{0,77})?$/D'; // no more than 78 digits (256-bit unsigned integer), starting with a digit other than zero
|
||||||
|
|
||||||
/** Daemonizes the process via the traditional sysvinit double-fork procedure
|
/** Daemonizes the process via the traditional sysvinit double-fork procedure
|
||||||
*
|
*
|
||||||
|
@ -96,20 +96,25 @@ class Daemon {
|
||||||
public function writePID(string $pidfile): void {
|
public function writePID(string $pidfile): void {
|
||||||
if ($f = @fopen($pidfile, "c+")) {
|
if ($f = @fopen($pidfile, "c+")) {
|
||||||
if (@flock($f, \LOCK_EX | \LOCK_NB)) {
|
if (@flock($f, \LOCK_EX | \LOCK_NB)) {
|
||||||
// confirm that some other process didn't get in before us
|
try {
|
||||||
$pid = fread($f, 80);
|
// confirm that some other process didn't get in before us
|
||||||
if (preg_match(static::PID_PATTERN, (string) $pid)) {
|
$pid = fread($f, 80);
|
||||||
if ($this->processExists((int) $pid)) {
|
if (preg_match(static::PID_PATTERN, (string) $pid)) {
|
||||||
throw new Exception("pidDuplicate", ['pid' => $pid]);
|
if (strlen($pid) && $this->processExists((int) $pid)) {
|
||||||
|
throw new Exception("pidDuplicate", ['pid' => $pid]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("pidCorrupt", ['pidfile' => $pidfile]);
|
||||||
}
|
}
|
||||||
} else {
|
// write the PID to the pidfile
|
||||||
throw new Exception("pidCorrupt", ['pidfile' => $pidfile]);
|
rewind($f);
|
||||||
|
if (!ftruncate($f, 0) || !fwrite($f, (string) posix_getpid())) {
|
||||||
|
throw new Exception("pidInaccessible", ['pidfile' => $pidfile]);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
flock($f, \LOCK_UN);
|
||||||
|
fclose($f);
|
||||||
}
|
}
|
||||||
// write the PID to the pidfile
|
|
||||||
rewind($f);
|
|
||||||
ftruncate($f, 0);
|
|
||||||
fwrite($f, (string) posix_getpid());
|
|
||||||
fclose($f);
|
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("pidLocked", ['pidfile' => $pidfile]);
|
throw new Exception("pidLocked", ['pidfile' => $pidfile]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,8 +146,62 @@ class TestDaemon extends \JKingWeb\Arsse\Test\AbstractTest {
|
||||||
["overlong", new Exception("pidCorrupt")],
|
["overlong", new Exception("pidCorrupt")],
|
||||||
["unreadable", new Exception("pidInaccessible")],
|
["unreadable", new Exception("pidInaccessible")],
|
||||||
["unwritable", null],
|
["unwritable", null],
|
||||||
["missing", null],
|
|
||||||
["locked", null],
|
["locked", null],
|
||||||
|
["missing", null],
|
||||||
|
["stale", null],
|
||||||
|
["empty", null],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providePidWriteChecks
|
||||||
|
* @requires extension posix
|
||||||
|
*/
|
||||||
|
public function testCheckPidWrites(string $file, $exp) {
|
||||||
|
$pid = (string) posix_getpid();
|
||||||
|
$vfs = vfsStream::setup("pidtest", 0777, $this->pidfiles);
|
||||||
|
$path = $vfs->url()."/pid/";
|
||||||
|
// set up access blocks
|
||||||
|
$f = fopen($path."locked", "r+");
|
||||||
|
flock($f, \LOCK_EX | \LOCK_NB);
|
||||||
|
chmod($path."unreadable", 0333);
|
||||||
|
chmod($path."unwritable", 0555);
|
||||||
|
// set up mock daemon class
|
||||||
|
$this->daemon->processExists->with(2112)->returns(true);
|
||||||
|
$this->daemon->processExists->with(42)->returns(false);
|
||||||
|
$daemon = $this->daemon->get();
|
||||||
|
// perform the test
|
||||||
|
try {
|
||||||
|
if ($exp instanceof \Exception) {
|
||||||
|
$this->assertException($exp);
|
||||||
|
$exp = $this->pidfiles['pid'][$file] ?? false;
|
||||||
|
$daemon->writePID($path.$file);
|
||||||
|
} else {
|
||||||
|
$this->assertSame($exp, $daemon->writePID($path.$file));
|
||||||
|
$exp = $pid;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
flock($f, \LOCK_UN);
|
||||||
|
fclose($f);
|
||||||
|
chmod($path."unreadable", 0777);
|
||||||
|
$this->assertSame($exp, @file_get_contents($path.$file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providePidWriteChecks(): iterable {
|
||||||
|
return [
|
||||||
|
["current", new Exception("pidDuplicate")],
|
||||||
|
["malformed1", new Exception("pidCorrupt")],
|
||||||
|
["malformed2", new Exception("pidCorrupt")],
|
||||||
|
["malformed3", new Exception("pidCorrupt")],
|
||||||
|
["bogus1", new Exception("pidCorrupt")],
|
||||||
|
["bogus2", new Exception("pidCorrupt")],
|
||||||
|
["bogus3", new Exception("pidCorrupt")],
|
||||||
|
["overlong", new Exception("pidCorrupt")],
|
||||||
|
["unreadable", new Exception("pidInaccessible")],
|
||||||
|
["unwritable", new Exception("pidInaccessible")],
|
||||||
|
["locked", new Exception("pidLocked")],
|
||||||
|
["missing", null],
|
||||||
["stale", null],
|
["stale", null],
|
||||||
["empty", null],
|
["empty", null],
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue