modifs.mineures sans commentaires
This commit is contained in:
parent
b195888602
commit
333ddca4f5
|
@ -193,9 +193,9 @@ abstract class AbstractCmd implements ICmd {
|
|||
return $retcode == 0;
|
||||
}
|
||||
|
||||
function fork_exec(int &$retcode=null): bool {
|
||||
function fork_exec(?int &$retcode=null, bool $wait=true): bool {
|
||||
$cmd = $this->getCmd(null, true);
|
||||
sh::_fork_exec($cmd, $retcode);
|
||||
sh::_fork_exec($cmd, $retcode, $wait);
|
||||
return $retcode == 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,27 +136,34 @@ class sh {
|
|||
return $retcode == 0;
|
||||
}
|
||||
|
||||
static function _waitpid(int $pid, ?int &$retcode=null): bool {
|
||||
pcntl_waitpid($pid, $status);
|
||||
if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status);
|
||||
elseif (pcntl_wifsignaled($status)) $retcode = -pcntl_wtermsig($status);
|
||||
else $retcode = app2::EC_FORK_CHILD;
|
||||
return $retcode == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lancer la commande $cmd dans un processus fils via un shell et attendre la
|
||||
* fin de son exécution.
|
||||
*
|
||||
* $cmd doit déjà être formaté comme il convient
|
||||
*/
|
||||
static final function _fork_exec(string $cmd, int &$retcode=null): bool {
|
||||
static final function _fork_exec(string $cmd, ?int &$retcode=null, bool $wait=true): bool {
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
// parent, impossible de forker
|
||||
throw new ExitError(app2::EC_FORK_PARENT, "unable to fork");
|
||||
} elseif ($pid) {
|
||||
// parent, fork ok
|
||||
pcntl_waitpid($pid, $status);
|
||||
if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status);
|
||||
else $retcode = app2::EC_FORK_CHILD;
|
||||
return $retcode == 0;
|
||||
if ($wait) return self::_waitpid($pid, $retcode);
|
||||
$retcode = null;
|
||||
return true;
|
||||
}
|
||||
// child, fork ok
|
||||
pcntl_exec("/bin/sh", ["-c", $cmd]);
|
||||
return false;
|
||||
throw StateException::unexpected_state();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
<?php
|
||||
namespace nur\sery\tools;
|
||||
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\wip\app\app2;
|
||||
use nur\sery\wip\app\cli\Application;
|
||||
use nur\sery\wip\app\cli\bg_launcher;
|
||||
use nur\sery\wip\app\RunFile;
|
||||
use nur\yaml;
|
||||
|
||||
class BgLauncherApp extends Application {
|
||||
|
@ -31,6 +34,11 @@ class BgLauncherApp extends Application {
|
|||
|
||||
protected ?array $args = null;
|
||||
|
||||
static function show_infos(RunFile $runfile, ?int $level=null): void {
|
||||
msg::print($runfile->getDesc(), $level);
|
||||
msg::print(yaml::with(["data" => $runfile->read()]), -1);
|
||||
}
|
||||
|
||||
function main() {
|
||||
$args = $this->args;
|
||||
|
||||
|
@ -52,18 +60,20 @@ class BgLauncherApp extends Application {
|
|||
switch ($this->action) {
|
||||
case self::ACTION_START:
|
||||
$appClass::_manage_runfile(count($args), $args, $runfile);
|
||||
if ($runfile->warnIfLocked()) return;
|
||||
array_splice($args, 0, 0, [
|
||||
PHP_BINARY,
|
||||
NULIB_APP_app_launcher,
|
||||
path::abspath(NULIB_APP_app_launcher),
|
||||
]);
|
||||
app2::params_putenv();
|
||||
bg_launcher::_start($args, $runfile);
|
||||
break;
|
||||
case self::ACTION_STOP:
|
||||
bg_launcher::_stop($runfile);
|
||||
self::show_infos($runfile);
|
||||
break;
|
||||
case self::ACTION_INFOS:
|
||||
yaml::dump($runfile->read());
|
||||
self::show_infos($runfile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,10 +18,19 @@ class SteamTrainApp extends Application {
|
|||
"description" => <<<EOT
|
||||
Cette application peut être utilisée pour tester le lancement des tâches de fond
|
||||
EOT,
|
||||
|
||||
["-c", "--count", "args" => 1,
|
||||
"help" => "spécifier le nombre d'étapes",
|
||||
],
|
||||
["-s", "--use-signal-handler", "value" => true,
|
||||
"help" => "installer un gestionnaire de signaux",
|
||||
],
|
||||
];
|
||||
|
||||
protected $count = 100;
|
||||
|
||||
function main() {
|
||||
$count = 100;
|
||||
$count = intval($this->count);
|
||||
app2::action("Running train...", $count);
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
msg::print("Tchou-tchou! x $i");
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use nur\sery\A;
|
||||
use nur\sery\cl;
|
||||
use nur\sery\file\SharedFile;
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\php\time\DateTime;
|
||||
use nur\sery\php\time\Elapsed;
|
||||
use nur\sery\str;
|
||||
|
||||
/**
|
||||
|
@ -41,18 +43,12 @@ class RunFile {
|
|||
], $merge);
|
||||
}
|
||||
|
||||
protected function initData(bool $forStart=true): array {
|
||||
if ($forStart) {
|
||||
$pid = posix_getpid();
|
||||
$dateStart = new DateTime();
|
||||
} else {
|
||||
$pid = $dateStart = null;
|
||||
}
|
||||
protected function initData(): array {
|
||||
return [
|
||||
"name" => $this->name,
|
||||
"id" => bin2hex(random_bytes(16)),
|
||||
"pg_pid" => null,
|
||||
"pid" => $pid,
|
||||
"mode" => null,
|
||||
"pgid" => null,
|
||||
"pid" => null,
|
||||
"serial" => 0,
|
||||
# lock
|
||||
"locked" => false,
|
||||
|
@ -60,10 +56,11 @@ class RunFile {
|
|||
"date_release" => null,
|
||||
# run
|
||||
"logfile" => $this->logfile,
|
||||
"date_start" => $dateStart,
|
||||
"date_start" => null,
|
||||
"date_stop" => null,
|
||||
"exitcode" => null,
|
||||
"is_done" => null,
|
||||
"is_reaped" => null,
|
||||
"is_ack_done" => null,
|
||||
# action
|
||||
"action" => null,
|
||||
"action_date_start" => null,
|
||||
|
@ -75,7 +72,7 @@ class RunFile {
|
|||
|
||||
function read(): array {
|
||||
$data = $this->file->unserialize();
|
||||
if (!is_array($data)) $data = $this->initData(false);
|
||||
if (!is_array($data)) $data = $this->initData();
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -84,7 +81,7 @@ class RunFile {
|
|||
$file->lockWrite();
|
||||
$data = $file->unserialize(null, false, true);
|
||||
if (!is_array($data)) {
|
||||
$data = $this->initData(false);
|
||||
$data = $this->initData();
|
||||
$file->ftruncate();
|
||||
$file->serialize($data, false, true);
|
||||
}
|
||||
|
@ -160,14 +157,40 @@ class RunFile {
|
|||
# cycle de vie de l'application
|
||||
|
||||
/**
|
||||
* indiquer que l'application démarre. l'état est entièrement réinitialisé,
|
||||
* sauf le PID du leader qui est laissé en l'état
|
||||
* Préparer le démarrage de l'application. Cette méhode est appelée par un
|
||||
* script externe qui doit préparer le démarrage du script
|
||||
*
|
||||
* - démarrer un groupe de process dont le process courant est le leader
|
||||
*/
|
||||
function wfPrepare(?int &$pgid=null): void {
|
||||
$this->update(function (array $data) use (&$pgid) {
|
||||
posix_setsid();
|
||||
$pgid = posix_getpid();
|
||||
return cl::merge($this->initData(), [
|
||||
"mode" => "session",
|
||||
"pgid" => $pgid,
|
||||
"pid" => null,
|
||||
"date_start" => new DateTime(),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/** indiquer que l'application démarre. */
|
||||
function wfStart(): void {
|
||||
$this->update(function (array $data) {
|
||||
return cl::merge($this->initData(), [
|
||||
"pg_pid" => $data["pg_pid"],
|
||||
]);
|
||||
$pid = posix_getpid();
|
||||
if ($data["mode"] === "session") {
|
||||
A::merge($data, [
|
||||
"pid" => $pid,
|
||||
]);
|
||||
} else {
|
||||
$data = cl::merge($this->initData(), [
|
||||
"mode" => "standalone",
|
||||
"pid" => $pid,
|
||||
"date_start" => new DateTime(),
|
||||
]);
|
||||
}
|
||||
return $data;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -183,13 +206,12 @@ class RunFile {
|
|||
return $data["date_start"] !== null && $data["date_stop"] === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* vérifier si l'application marquée comme démarrée tourne réellement
|
||||
*/
|
||||
function isRunning(?array $data=null): bool {
|
||||
$data ??= $this->read();
|
||||
if ($data["date_start"] === null) return false;
|
||||
if ($data["date_stop"] !== null) return false;
|
||||
function _getCid(array $data=null): int {
|
||||
if ($data["mode"] === "session") return -$data["pgid"];
|
||||
else return $data["pid"];
|
||||
}
|
||||
|
||||
function _isRunning(array $data=null): bool {
|
||||
if (!posix_kill($data["pid"], 0)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case 1: #PCNTL_EPERM:
|
||||
|
@ -208,10 +230,22 @@ class RunFile {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* vérifier si l'application marquée comme démarrée tourne réellement
|
||||
*/
|
||||
function isRunning(?array $data=null): bool {
|
||||
$data ??= $this->read();
|
||||
if ($data["date_start"] === null) return false;
|
||||
if ($data["date_stop"] !== null) return false;
|
||||
return $this->_isRunning($data);
|
||||
}
|
||||
|
||||
/** indiquer que l'application s'arrête */
|
||||
function wfStop(): void {
|
||||
$this->update(function (array $data) {
|
||||
return ["date_stop" => new DateTime()];
|
||||
return [
|
||||
"date_stop" => new DateTime(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -228,16 +262,45 @@ class RunFile {
|
|||
}
|
||||
|
||||
/** après l'arrêt de l'application, mettre à jour le code de retour */
|
||||
function wfStopped(int $exitcode): void {
|
||||
function wfReaped(int $exitcode): void {
|
||||
$this->update(function (array $data) use ($exitcode) {
|
||||
return [
|
||||
"pg_pid" => null,
|
||||
"mode" => null,
|
||||
"pgid" => null,
|
||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
||||
"exitcode" => $exitcode,
|
||||
"is_reaped" => true,
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
||||
* ce n'est pas le cas
|
||||
*/
|
||||
function _isUndead(?int $pid=null): bool {
|
||||
$data = $this->read();
|
||||
if ($data["date_start"] === null) return false;
|
||||
if ($data["date_stop"] !== null) return false;
|
||||
$pid ??= $data["pid"];
|
||||
if (!posix_kill($pid, 0)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case 1: #PCNTL_EPERM:
|
||||
# process auquel on n'a pas accès?! est-ce un autre process qui a
|
||||
# réutilisé le PID?
|
||||
return false;
|
||||
case 3: #PCNTL_ESRCH:
|
||||
# process inexistant
|
||||
return true;
|
||||
case 22: #PCNTL_EINVAL:
|
||||
# ne devrait pas se produire
|
||||
return false;
|
||||
}
|
||||
}
|
||||
# process existant auquel on a accès
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* comme {@link self::isStopped()} mais ne renvoie true qu'une seule fois si
|
||||
* $updateDone==true
|
||||
|
@ -246,16 +309,42 @@ class RunFile {
|
|||
$done = false;
|
||||
$this->update(function (array $ldata) use (&$done, &$data, $updateDone) {
|
||||
$data = $ldata;
|
||||
if ($data["date_start"] === null || $data["date_stop"] === null || $data["is_done"]) {
|
||||
if ($data["date_start"] === null || $data["date_stop"] === null || $data["is_ack_done"]) {
|
||||
return false;
|
||||
}
|
||||
$done = true;
|
||||
if ($updateDone) return ["is_done" => $done];
|
||||
if ($updateDone) return ["is_ack_done" => $done];
|
||||
else return null;
|
||||
});
|
||||
return $done;
|
||||
}
|
||||
|
||||
function getDesc(?array $data=null): ?string {
|
||||
$data ??= $this->read();
|
||||
$desc = $data["name"];
|
||||
$dateStart = $data["date_start"];
|
||||
$dateStop = $data["date_stop"];
|
||||
$exitcode = $data["exitcode"];
|
||||
if ($exitcode !== null) $exitcode = "\nCode de retour $exitcode";
|
||||
if (!$this->wasStarted($data)) {
|
||||
return "$desc: pas encore démarré";
|
||||
} elseif ($this->isRunning($data)) {
|
||||
$sinceStart = Elapsed::format_since($dateStart);
|
||||
$started = "\nDémarré depuis $dateStart ($sinceStart)";
|
||||
return "$desc: EN COURS pid $data[pid]$started";
|
||||
} elseif ($this->isStopped($data)) {
|
||||
$duration = "\nDurée ".Elapsed::format_delay($dateStart, $dateStop);
|
||||
$sinceStop = Elapsed::format_since($dateStop);
|
||||
$stopped = "\nArrêtée $sinceStop le $dateStop";
|
||||
$reaped = $data["is_reaped"]? ", reaped": null;
|
||||
$done = $data["is_ack_done"]? ", ACK done": null;
|
||||
return "$desc: TERMINEE$duration$stopped$exitcode$reaped$done";
|
||||
} else {
|
||||
$stopped = $dateStop? "\nArrêtée le $dateStop": null;
|
||||
return "$desc: CRASHED\nCommencé le $dateStart$stopped$exitcode";
|
||||
}
|
||||
}
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# gestion des actions
|
||||
|
||||
|
@ -286,21 +375,20 @@ class RunFile {
|
|||
function getActionDesc(?array $data=null): ?string {
|
||||
$data ??= $this->read();
|
||||
$action = $data["action"];
|
||||
if ($action === null) {
|
||||
return "pid $data[pid] [$data[date_start]]";
|
||||
if ($action !== null) {
|
||||
$date ??= $data["action_date_step"];
|
||||
$date ??= $data["action_date_start"];
|
||||
if ($date !== null) $action = "$date $action";
|
||||
$action = "Etape en cours: $action";
|
||||
$current = $data["action_current_step"];
|
||||
$max = $data["action_max_step"];
|
||||
if ($current !== null && $max !== null) {
|
||||
$action .= " ($current / $max)";
|
||||
} elseif ($current !== null) {
|
||||
$action .= " ($current)";
|
||||
}
|
||||
}
|
||||
|
||||
$date ??= $data["action_date_step"];
|
||||
$date ??= $data["action_date_start"];
|
||||
if ($date !== null) $action = "[$date] $action";
|
||||
$current = $data["action_current_step"];
|
||||
$max = $data["action_max_step"];
|
||||
if ($current !== null && $max !== null) {
|
||||
$action .= " ($current / $max)";
|
||||
} elseif ($current !== null) {
|
||||
$action .= " ($current)";
|
||||
}
|
||||
return "pid $data[pid] $action";
|
||||
return $action;
|
||||
}
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -313,64 +401,4 @@ class RunFile {
|
|||
$name = str::join("/", [$this->name, $name]);
|
||||
return new LockFile($file, $name, $title);
|
||||
}
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Gestionnaire de tâches (tm_*)
|
||||
|
||||
/** démarrer un groupe de process dont le process courant est le leader */
|
||||
function tm_startPg(): void {
|
||||
$this->update(function (array $data) {
|
||||
posix_setsid();
|
||||
return [
|
||||
"pg_pid" => posix_getpid(),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* vérifier si on est dans le cas où la tâche devrait tourner mais en réalité
|
||||
* ce n'est pas le cas
|
||||
*/
|
||||
function tm_isUndead(?int $pid=null): bool {
|
||||
$data = $this->read();
|
||||
if ($data["date_start"] === null) return false;
|
||||
if ($data["date_stop"] !== null) return false;
|
||||
$pid ??= $data["pid"];
|
||||
if (!posix_kill($pid, 0)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case 1: #PCNTL_EPERM:
|
||||
# process auquel on n'a pas accès?! est-ce un autre process qui a
|
||||
# réutilisé le PID?
|
||||
return false;
|
||||
case 3: #PCNTL_ESRCH:
|
||||
# process inexistant
|
||||
return true;
|
||||
case 22: #PCNTL_EINVAL:
|
||||
# ne devrait pas se produire
|
||||
return false;
|
||||
}
|
||||
}
|
||||
# process existant auquel on a accès
|
||||
return false;
|
||||
}
|
||||
|
||||
function tm_isReapable(): bool {
|
||||
$data = $this->read();
|
||||
return $data["date_stop"] !== null && $data["exitcode"] === null;
|
||||
}
|
||||
|
||||
/** marquer la tâche comme terminée */
|
||||
function tm_reap(?int $pid=null): void {
|
||||
$data = $this->read();
|
||||
$pid ??= $data["pid"];
|
||||
pcntl_waitpid($pid, $status);
|
||||
$exitcode = pcntl_wifexited($status)? pcntl_wexitstatus($status): 127;
|
||||
$this->update(function (array $data) use ($exitcode) {
|
||||
return [
|
||||
"pg_pid" => null,
|
||||
"date_stop" => $data["date_stop"] ?? new DateTime(),
|
||||
"exitcode" => $data["exitcode"] ?? $exitcode,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use Closure;
|
||||
use nur\sery\A;
|
||||
use nur\sery\cl;
|
||||
use nur\sery\ExitError;
|
||||
|
@ -475,15 +476,12 @@ class app2 {
|
|||
|
||||
#############################################################################
|
||||
|
||||
const EC_UNDEAD = 247;
|
||||
const EC_REAPABLE = 248;
|
||||
const EC_FORK_CHILD = 249;
|
||||
const EC_FORK_PARENT = 250;
|
||||
const EC_DISABLED = 251;
|
||||
const EC_LOCKED = 252;
|
||||
const EC_BAD_COMMAND = 253;
|
||||
const EC_UNEXPECTED = 254;
|
||||
const EC_SIGNAL = 255;
|
||||
const EC_FORK_CHILD = 250;
|
||||
const EC_FORK_PARENT = 251;
|
||||
const EC_DISABLED = 252;
|
||||
const EC_LOCKED = 253;
|
||||
const EC_BAD_COMMAND = 254;
|
||||
const EC_UNEXPECTED = 255;
|
||||
|
||||
#############################################################################
|
||||
|
||||
|
@ -492,7 +490,7 @@ class app2 {
|
|||
static function install_signal_handler(bool $allow=true): void {
|
||||
if (!$allow) return;
|
||||
$signalHandler = function(int $signo, $siginfo) {
|
||||
throw new ExitError(self::EC_SIGNAL);
|
||||
throw new ExitError(128 + $signo);
|
||||
};
|
||||
pcntl_signal(SIGHUP, $signalHandler);
|
||||
pcntl_signal(SIGINT, $signalHandler);
|
||||
|
|
|
@ -94,12 +94,13 @@ abstract class Application {
|
|||
break;
|
||||
case "infos":
|
||||
case "i":
|
||||
$desc = $runfile->getDesc();
|
||||
if ($runfile->isRunning()) {
|
||||
$action = $runfile->getActionDesc();
|
||||
if ($action !== null) $action = ": $action";
|
||||
echo "running$action\n";
|
||||
$actionDesc = $runfile->getActionDesc();
|
||||
if ($actionDesc !== null) $actionDesc = "\n$actionDesc";
|
||||
echo "$desc$actionDesc\n";
|
||||
} else {
|
||||
echo "not running\n";
|
||||
echo "$desc\n";
|
||||
$ec = 1;
|
||||
}
|
||||
break;
|
||||
|
@ -113,7 +114,7 @@ abstract class Application {
|
|||
static function run(?Application $app=null): void {
|
||||
$unlock = false;
|
||||
$stop = false;
|
||||
register_shutdown_function(function() use (&$unlock, &$stop) {
|
||||
$shutdown = function () use (&$unlock, &$stop) {
|
||||
if ($unlock) {
|
||||
app2::get()->getRunfile()->release();
|
||||
$unlock = false;
|
||||
|
@ -122,7 +123,8 @@ abstract class Application {
|
|||
app2::get()->getRunfile()->wfStop();
|
||||
$stop = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
register_shutdown_function($shutdown);
|
||||
app2::install_signal_handler(static::USE_SIGNAL_HANDLER);
|
||||
try {
|
||||
static::_initialize_app();
|
||||
|
|
|
@ -2,95 +2,38 @@
|
|||
namespace nur\sery\wip\app\cli;
|
||||
|
||||
use nur\sery\ExitError;
|
||||
use nur\sery\file\TmpfileWriter;
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\os\proc\Cmd;
|
||||
use nur\sery\os\sh;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\wip\app\app2;
|
||||
use nur\sery\wip\app\args;
|
||||
use nur\sery\wip\app\RunFile;
|
||||
|
||||
class bg_launcher {
|
||||
/**
|
||||
* transformer une liste d'argument de la forme
|
||||
* - ["myArg" => $value] devient ["--my-arg", "$value"]
|
||||
* - ["myOpt" => true] devient ["--my-opt"]
|
||||
* - ["myOpt" => false] est momis
|
||||
* - les valeurs séquentielles sont prises telles quelles
|
||||
*/
|
||||
static function verifix_args(array $args): array {
|
||||
$args = args::from_array($args);
|
||||
# corriger le chemin de l'application pour qu'il soit absolu et normalisé
|
||||
$args[0] = path::abspath($args[0]);
|
||||
return $args;
|
||||
}
|
||||
|
||||
static function launch(string $appClass, array $args): int {
|
||||
$app = app2::get();
|
||||
$vendorBindir = $app->getVendorbindir();
|
||||
$launch_php = "$vendorBindir/_launch.php";
|
||||
if (!file_exists($launch_php)) {
|
||||
$launch_php = __DIR__."/../../lib/_launch.php";
|
||||
}
|
||||
$tmpfile = new TmpfileWriter();
|
||||
$tmpfile->keep()->serialize($app->getParams());
|
||||
|
||||
$args = self::verifix_args($args);
|
||||
$cmd = new Cmd([
|
||||
$launch_php,
|
||||
"--internal-use", $tmpfile->getFile(),
|
||||
$appClass, "--", ...$args,
|
||||
]);
|
||||
$cmd->addRedir("both", "/tmp/nulib_app_launcher-launch.log");
|
||||
$cmd->passthru($exitcode);
|
||||
|
||||
# attendre un peu que la commande aie le temps de s'initialiser
|
||||
sleep(1);
|
||||
|
||||
$tmpfile->close();
|
||||
return $exitcode;
|
||||
}
|
||||
|
||||
static function _start(array $args, Runfile $runfile): bool {
|
||||
if ($runfile->warnIfLocked()) return false;
|
||||
static function _start(array $args, Runfile $runfile): void {
|
||||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
# parent, impossible de forker
|
||||
throw new ExitError(app2::EC_FORK_PARENT, "Unable to fork");
|
||||
} elseif ($pid) {
|
||||
# parent, fork ok
|
||||
return true;
|
||||
} else {
|
||||
## child, fork ok
|
||||
# Créer un groupe de process, pour pouvoir tuer tous les enfants en même temps
|
||||
$runfile->tm_startPg();
|
||||
} elseif (!$pid) {
|
||||
# child, fork ok
|
||||
$runfile->wfPrepare($pid);
|
||||
$logfile = $runfile->getLogfile() ?? "/tmp/NULIB_APP_app_start.log";
|
||||
$pid = posix_getpid();
|
||||
$exitcode = app2::EC_FORK_CHILD;
|
||||
try {
|
||||
# puis lancer la commande
|
||||
$cmd = new Cmd($args);
|
||||
$cmd->addSource("/g/init.env");
|
||||
$cmd->addRedir("both", $logfile, true);
|
||||
msg::debug("$pid: launching\n".$cmd->getCmd());
|
||||
$cmd->fork_exec($exitcode);
|
||||
msg::debug("$pid: exitcode=$exitcode");
|
||||
return true;
|
||||
$cmd->fork_exec($exitcode, false);
|
||||
sh::_waitpid(-$pid, $exitcode);
|
||||
} finally {
|
||||
$runfile->wfStopped($exitcode);
|
||||
$runfile->wfReaped($exitcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function _stop(Runfile $runfile): void {
|
||||
$data = $runfile->read();
|
||||
$pid = $data["pg_pid"];
|
||||
if ($pid === null) {
|
||||
msg::warning("$data[name]: groupe de process inconnu");
|
||||
return;
|
||||
}
|
||||
msg::action("kill $pid");
|
||||
if (!posix_kill(-$pid, SIGKILL)) {
|
||||
private static function kill(int $pid, int $signal): bool {
|
||||
if (!posix_kill($pid, $signal)) {
|
||||
switch (posix_get_last_error()) {
|
||||
case PCNTL_ESRCH:
|
||||
msg::afailure("process inexistant");
|
||||
|
@ -102,17 +45,53 @@ class bg_launcher {
|
|||
msg::afailure("signal invalide");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static function _stop(Runfile $runfile): bool {
|
||||
$data = $runfile->read();
|
||||
$pid = $runfile->_getCid($data);
|
||||
$stopped = false;
|
||||
msg::action("term $pid");
|
||||
$timeout = 10;
|
||||
while ($runfile->tm_isUndead($pid)) {
|
||||
sleep(1);
|
||||
if (--$timeout == 0) {
|
||||
msg::afailure("impossible d'arrêter la tâche");
|
||||
return;
|
||||
$delay = 300000;
|
||||
while (--$timeout >= 0) {
|
||||
if (!self::kill($pid, SIGTERM)) {
|
||||
msg::afailure();
|
||||
return false;
|
||||
}
|
||||
usleep($delay);
|
||||
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
||||
if (!$runfile->_isRunning($data)) {
|
||||
msg::asuccess();
|
||||
$stopped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$runfile->wfStopped(app2::EC_REAPABLE);
|
||||
msg::asuccess();
|
||||
if (!$stopped) {
|
||||
msg::action("kill $pid");
|
||||
$timeout = 3;
|
||||
$delay = 300000;
|
||||
while (--$timeout >= 0) {
|
||||
if (!self::kill($pid, SIGKILL)) {
|
||||
msg::afailure();
|
||||
return false;
|
||||
}
|
||||
usleep($delay);
|
||||
$delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
|
||||
if (!$runfile->_isRunning($data)) {
|
||||
msg::asuccess();
|
||||
$stopped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($stopped) {
|
||||
sh::_waitpid($pid, $exitcode);
|
||||
$runfile->wfReaped($exitcode);
|
||||
}
|
||||
return $stopped;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue