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

Respond to termination signals and delete PID file

This commit is contained in:
J. King 2021-06-06 18:54:24 -04:00
parent 410310282f
commit 2c7b16ed27
2 changed files with 49 additions and 5 deletions

View file

@ -94,10 +94,14 @@ USAGE_TEXT;
return 0; return 0;
case "daemon": case "daemon":
if ($args['--fork'] !== null) { if ($args['--fork'] !== null) {
$this->fork($args['--fork']); $pidfile = $this->resolvePID($args['--fork']);
$this->fork($pidfile);
} }
$this->loadConf(); $this->loadConf();
Arsse::$obj->get(Service::class)->watch(true); Arsse::$obj->get(Service::class)->watch(true);
if (isset($pidfile)) {
unlink($pidfile);
}
return 0; return 0;
case "feed refresh": case "feed refresh":
return (int) !Arsse::$db->feedUpdate((int) $args['<n>'], true); return (int) !Arsse::$db->feedUpdate((int) $args['<n>'], true);
@ -282,6 +286,7 @@ USAGE_TEXT;
default: default:
fclose($pipe[1]); fclose($pipe[1]);
fread($pipe[0], 100); fread($pipe[0], 100);
fclose($pipe[0]);
# Call exit() in the original process. The process that invoked the daemon must be able to rely on that this exit() happens after initialization is complete and all external communication channels are established and accessible. # Call exit() in the original process. The process that invoked the daemon must be able to rely on that this exit() happens after initialization is complete and all external communication channels are established and accessible.
exit; exit;
} }
@ -320,4 +325,23 @@ USAGE_TEXT;
} }
} }
} }
/** Resolves the PID file path and ensures the file or parent directory is writable */
protected function resolvePID(string $pidfile): string {
$dir = dirname($pidfile);
$file = basename($pidfile);
if ($base = @realpath($dir)) {
$out = "$base/$file";
if (file_exists($out)) {
if (!is_writable($out)) {
throw new \Exception("PID file is not writable");
}
} elseif (!is_writable($base)) {
throw new \Exception("Cannot create PID file");
}
} else {
throw new \Exception("Parent directory of PID file does not exist");
}
return $out;
}
} }

View file

@ -16,6 +16,7 @@ class Service {
/** @var Service\Driver */ /** @var Service\Driver */
protected $drv; protected $drv;
protected $loop = false;
public function __construct() { public function __construct() {
$driver = Arsse::$conf->serviceDriver; $driver = Arsse::$conf->serviceDriver;
@ -23,6 +24,8 @@ class Service {
} }
public function watch(bool $loop = true): \DateTimeInterface { public function watch(bool $loop = true): \DateTimeInterface {
$this->loop = $loop;
$this->signalInit();
$t = new \DateTime(); $t = new \DateTime();
do { do {
$this->checkIn(); $this->checkIn();
@ -37,13 +40,14 @@ class Service {
static::cleanupPost(); static::cleanupPost();
$t->add(Arsse::$conf->serviceFrequency); $t->add(Arsse::$conf->serviceFrequency);
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
if ($loop) { if ($this->loop) {
do { do {
@time_sleep_until($t->getTimestamp()); sleep((int) max(0, $t->getTimestamp() - time()));
} while ($t->getTimestamp() > time()); pcntl_signal_dispatch();
} while ($this->loop && $t->getTimestamp() > time());
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} while ($loop); } while ($this->loop);
return $t; return $t;
} }
@ -88,4 +92,20 @@ class Service {
} }
return true; return true;
} }
protected function signalInit(): void {
if (function_exists("pcntl_async_signals") && function_exists("pcntl_signal")) {
// receive asynchronous signals if supported
pcntl_async_signals(true);
foreach ([\SIGABRT, \SIGINT, \SIGTERM] as $sig) {
pcntl_signal($sig, [$this, "sigTerm"]);
}
}
}
protected function sigTerm(int $signo): void {
$this->loop = false;
}
} }