1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 13:12:41 +00:00

Test PID writing

This commit is contained in:
J. King 2021-06-24 18:32:20 -04:00
parent 2767ab755e
commit 0bb5e916d2
2 changed files with 73 additions and 14 deletions

View file

@ -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]);
} }

View file

@ -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],
]; ];