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

Finish testing PID file path checking

This commit is contained in:
J. King 2021-06-16 12:48:09 -04:00
parent 59cf27089a
commit f1c29c99c7
7 changed files with 128 additions and 76 deletions

View file

@ -103,11 +103,12 @@ abstract class AbstractException extends \Exception {
"ImportExport/Exception.invalidTagName" => 10615, "ImportExport/Exception.invalidTagName" => 10615,
"Rule/Exception.invalidPattern" => 10701, "Rule/Exception.invalidPattern" => 10701,
"Service/Exception.pidNotFile" => 10801, "Service/Exception.pidNotFile" => 10801,
"Service/Exception.pidDirNotFound" => 10802, "Service/Exception.pidDirMissing" => 10802,
"Service/Exception.pidUnusable" => 10803, "Service/Exception.pidDirUnresolvable" => 10803,
"Service/Exception.pidUnreadable" => 10804, "Service/Exception.pidUnusable" => 10804,
"Service/Exception.pidUnwritable" => 10805, "Service/Exception.pidUnreadable" => 10805,
"Service/Exception.pidUncreatable" => 10806, "Service/Exception.pidUnwritable" => 10806,
"Service/Exception.pidUncreatable" => 10807,
]; ];
protected $symbol; protected $symbol;

View file

@ -183,7 +183,7 @@ USAGE_TEXT;
// create a Daemon object which contains various helper functions // create a Daemon object which contains various helper functions
$daemon = Arsse::$obj->get(Daemon::class); $daemon = Arsse::$obj->get(Daemon::class);
// resolve the PID file to its absolute path; this also checks its readability and writability // resolve the PID file to its absolute path; this also checks its readability and writability
$pidfile = $daemon->resolvePID($pidfile); $pidfile = $daemon->checkPIDFilePath($pidfile);
// daemonize // daemonize
$daemon->fork($pidfile); $daemon->fork($pidfile);
// start the fetching service as normal // start the fetching service as normal

View file

@ -109,33 +109,74 @@ class Daemon {
} }
/** Resolves the PID file path and ensures the file or parent directory is writable */ /** Resolves the PID file path and ensures the file or parent directory is writable */
public function resolvePID(string $pidfile): string { public function checkPIDFilePath(string $pidfile): string {
$dir = dirname($pidfile); $dir = dirname($pidfile);
$file = basename($pidfile); $file = basename($pidfile);
$base = $this->resolveRelativePath($dir);
if (!strlen($file)) { if (!strlen($file)) {
throw new Exception("pidNotFile", ['pidfile' => $dir]); throw new Exception("pidNotFile", ['pidfile' => $dir]);
} elseif ($base = @$this->realpath($dir)) { } elseif ($base) {
$out = "$base/$file"; $out = "$base/$file";
if (file_exists($out)) { if (file_exists($out)) {
if (!is_readable($out) && !is_writable($out)) { if (!is_file($out)) {
throw new Exception("pidNotFile", ['pidfile' => $out]);
} elseif (!is_readable($out) && !is_writable($out)) {
throw new Exception("pidUnusable", ['pidfile' => $out]); throw new Exception("pidUnusable", ['pidfile' => $out]);
} elseif (!is_readable($out)) { } elseif (!is_readable($out)) {
throw new Exception("pidunreadable", ['pidfile' => $out]); throw new Exception("pidUnreadable", ['pidfile' => $out]);
} elseif (!is_writable($out)) { } elseif (!is_writable($out)) {
throw new Exception("pidUnwritable", ['pidfile' => $out]); throw new Exception("pidUnwritable", ['pidfile' => $out]);
} elseif (!is_file($out)) {
throw new Exception("pidNotFile", ['pidfile' => $out]);
} }
} elseif (!is_dir($base)) {
throw new Exception("pidDirMissing", ['piddir' => $dir]);
} elseif (!is_writable($base)) { } elseif (!is_writable($base)) {
throw new Exception("pidUncreatable", ['pidfile' => $out]); throw new Exception("pidUncreatable", ['pidfile' => $out]);
} }
} else { } else {
throw new Exception("pidDirNotFound", ['piddir' => $dir]); throw new Exception("pidDirUnresolvable", ['piddir' => $dir]);
} }
return $out; return $out;
} }
protected function realpath(string $path) { /** Resolves paths with relative components
return @realpath($path); *
* This method has fewer filesystem access requirements than the native
* realpath() function. The current working directory most be resolvable
* for a relative path, but for absolute paths with relativelu components
* the filesystem is not involved at all.
*
* Consequently symbolic links are not resolved.
*
* @return string|false
*/
public function resolveRelativePath(string $path) {
if ($path[0] !== "/") {
$cwd = $this->cwd();
if ($cwd === false) {
return false;
}
$path = substr($cwd, 1)."/".$path;
}
$path = explode("/", substr($path, 1));
$out = [];
foreach ($path as $p) {
if ($p === "..") {
array_pop($out);
} elseif ($p === ".") {
continue;
} else {
$out[] = $p;
}
}
return "/".implode("/", $out);
}
/** Wrapper around posix_getcwd to facilitate testing
*
* @return string|false
* @codeCoverageIgnore
*/
protected function cwd() {
return posix_getcwd();
} }
} }

View file

@ -209,7 +209,8 @@ return [
'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidTagName' => 'Input data contains an invalid tag name', 'Exception.JKingWeb/Arsse/ImportExport/Exception.invalidTagName' => 'Input data contains an invalid tag name',
'Exception.JKingWeb/Arsse/Rule/Exception.invalidPattern' => 'Specified rule pattern is invalid', 'Exception.JKingWeb/Arsse/Rule/Exception.invalidPattern' => 'Specified rule pattern is invalid',
'Exception.JKingWeb/Arsse/Service/Exception.pidNotFile' => 'Specified PID file location "{pidfile}" must be a regular file', 'Exception.JKingWeb/Arsse/Service/Exception.pidNotFile' => 'Specified PID file location "{pidfile}" must be a regular file',
'Exception.JKingWeb/Arsse/Service/Exception.pidDirNotFound' => 'Parent directory "{piddir}" of PID file does not exist', 'Exception.JKingWeb/Arsse/Service/Exception.pidDirMissing' => 'Parent directory "{piddir}" of PID file does not exist',
'Exception.JKingWeb/Arsse/Service/Exception.pidDirUnresolvable' => 'Parent directory "{piddir}" of PID file could not be resolved to its absolute path',
'Exception.JKingWeb/Arsse/Service/Exception.pidUnreadable' => 'Insufficient permissions to open PID file "{pidfile}" for reading', 'Exception.JKingWeb/Arsse/Service/Exception.pidUnreadable' => 'Insufficient permissions to open PID file "{pidfile}" for reading',
'Exception.JKingWeb/Arsse/Service/Exception.pidUnwritable' => 'Insufficient permissions to open PID file "{pidfile}" for writing', 'Exception.JKingWeb/Arsse/Service/Exception.pidUnwritable' => 'Insufficient permissions to open PID file "{pidfile}" for writing',
'Exception.JKingWeb/Arsse/Service/Exception.pidUnusable' => 'Insufficient permissions to open PID file "{pidfile}" for reading or writing', 'Exception.JKingWeb/Arsse/Service/Exception.pidUnusable' => 'Insufficient permissions to open PID file "{pidfile}" for reading or writing',

View file

@ -0,0 +1,68 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\Service;
use JKingWeb\Arsse\Service\Daemon;
use JKingWeb\Arsse\Service\Exception;
use org\bovigo\vfs\vfsStream;
/** @covers \JKingWeb\Arsse\Service\Daemon */
class TestDaemon extends \JKingWeb\Arsse\Test\AbstractTest {
protected $pidfiles = [
'errors' => [
'create' => [],
'read' => "cannot be read",
'write' => "cannot be written to",
'readwrite' => "can neither be read nor written to",
],
'ok' => [
'dir' => [],
'file' => "this file can be fully accessed",
],
];
public function setUp(): void {
parent::setUp();
$this->daemon = $this->partialMock(Daemon::class);
}
/** @dataProvider providePidChecks */
public function testCheckPidFiles(string $file, bool $accessible, $exp): void {
$vfs = vfsStream::setup("pidtest", 0777, $this->pidfiles);
$path = $vfs->url()."/";
// set up access blocks
chmod($path."errors/create", 0555);
chmod($path."errors/read", 0333);
chmod($path."errors/write", 0555);
chmod($path."errors/readwrite", 0111);
// set up mock daemon class
$this->daemon->resolveRelativePath->returns($accessible ? dirname($path.$file) : false);
$daemon = $this->daemon->get();
// perform the test
if ($exp instanceof \Exception) {
$this->assertException($exp);
$daemon->checkPIDFilePath($file);
} else {
$this->assertSame($path.$exp, $daemon->checkPIDFilePath($file));
}
}
public function providePidChecks(): iterable {
return [
["ok/file", false, new Exception("pidDirUnresolvable")],
["not/found", true, new Exception("pidDirMissing")],
["errors/create/pid", true, new Exception("pidUncreatable")],
["errors/read", true, new Exception("pidUnreadable")],
["errors/write", true, new Exception("pidUnwritable")],
["errors/readwrite", true, new Exception("pidUnusable")],
["", true, new Exception("pidNotFile")],
["ok/dir", true, new Exception("pidNotFile")],
["ok/file", true, "ok/file"],
["ok/dir/file", true, "ok/dir/file"],
];
}
}

View file

@ -1,59 +0,0 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\Service;
use JKingWeb\Arsse\Service\Daemon;
use JKingWeb\Arsse\Service\Exception;
use org\bovigo\vfs\vfsStream;
/** @covers \JKingWeb\Arsse\Service */
class TestPID extends \JKingWeb\Arsse\Test\AbstractTest {
protected $pidfiles = [
'errors' => [
'create' => [],
'read' => "",
'write' => "",
'readwrite' => "",
],
'ok' => [
'dir' => [],
'file' => "",
],
];
public function setUp(): void {
parent::setUp();
$this->daemon = $this->partialMock(Daemon::class);
}
/** @dataProvider providePidResolutions */
public function testResolvePidFiles(string $file, bool $realpath, $exp): void {
$vfs = vfsStream::setup("pidtest", 0777, $this->pidfiles);
$path = $vfs->url()."/";
// set up access blocks
chmod($path."errors/create", 0555);
chmod($path."errors/read", 0333);
chmod($path."errors/write", 0555);
chmod($path."errors/readwrite", 0111);
// set up mock daemon class
$this->daemon->realPath->returns($realpath ? $path.$file : false);
$daemon = $this->daemon->get();
// perform the test
if ($exp instanceof \Exception) {
$this->assertException($exp);
$daemon->resolvePID($file);
} else {
$this->assertSame($exp, $daemon->resolvePID($file));
}
}
public function providePidResolutions(): iterable {
return [
["errors/create", true, new Exception("pidUncreatable")],
];
}
}

View file

@ -142,7 +142,7 @@
<file>cases/Service/TestService.php</file> <file>cases/Service/TestService.php</file>
<file>cases/Service/TestSerial.php</file> <file>cases/Service/TestSerial.php</file>
<file>cases/Service/TestSubprocess.php</file> <file>cases/Service/TestSubprocess.php</file>
<file>cases/Service/TestPID.php</file> <file>cases/Service/TestDaemon.php</file>
<file>cases/CLI/TestCLI.php</file> <file>cases/CLI/TestCLI.php</file>
<file>cases/TestArsse.php</file> <file>cases/TestArsse.php</file>
</testsuite> </testsuite>