modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-09-26 17:31:36 +04:00
parent b195888602
commit 333ddca4f5
8 changed files with 240 additions and 207 deletions

View File

@ -193,9 +193,9 @@ abstract class AbstractCmd implements ICmd {
return $retcode == 0; 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); $cmd = $this->getCmd(null, true);
sh::_fork_exec($cmd, $retcode); sh::_fork_exec($cmd, $retcode, $wait);
return $retcode == 0; return $retcode == 0;
} }
} }

View File

@ -136,27 +136,34 @@ class sh {
return $retcode == 0; 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 * Lancer la commande $cmd dans un processus fils via un shell et attendre la
* fin de son exécution. * fin de son exécution.
* *
* $cmd doit déjà être formaté comme il convient * $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(); $pid = pcntl_fork();
if ($pid == -1) { if ($pid == -1) {
// parent, impossible de forker // parent, impossible de forker
throw new ExitError(app2::EC_FORK_PARENT, "unable to fork"); throw new ExitError(app2::EC_FORK_PARENT, "unable to fork");
} elseif ($pid) { } elseif ($pid) {
// parent, fork ok // parent, fork ok
pcntl_waitpid($pid, $status); if ($wait) return self::_waitpid($pid, $retcode);
if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status); $retcode = null;
else $retcode = app2::EC_FORK_CHILD; return true;
return $retcode == 0;
} }
// child, fork ok // child, fork ok
pcntl_exec("/bin/sh", ["-c", $cmd]); pcntl_exec("/bin/sh", ["-c", $cmd]);
return false; throw StateException::unexpected_state();
} }
/** /**

View File

@ -1,9 +1,12 @@
<?php <?php
namespace nur\sery\tools; 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\app2;
use nur\sery\wip\app\cli\Application; use nur\sery\wip\app\cli\Application;
use nur\sery\wip\app\cli\bg_launcher; use nur\sery\wip\app\cli\bg_launcher;
use nur\sery\wip\app\RunFile;
use nur\yaml; use nur\yaml;
class BgLauncherApp extends Application { class BgLauncherApp extends Application {
@ -31,6 +34,11 @@ class BgLauncherApp extends Application {
protected ?array $args = null; 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() { function main() {
$args = $this->args; $args = $this->args;
@ -52,18 +60,20 @@ class BgLauncherApp extends Application {
switch ($this->action) { switch ($this->action) {
case self::ACTION_START: case self::ACTION_START:
$appClass::_manage_runfile(count($args), $args, $runfile); $appClass::_manage_runfile(count($args), $args, $runfile);
if ($runfile->warnIfLocked()) return;
array_splice($args, 0, 0, [ array_splice($args, 0, 0, [
PHP_BINARY, PHP_BINARY,
NULIB_APP_app_launcher, path::abspath(NULIB_APP_app_launcher),
]); ]);
app2::params_putenv(); app2::params_putenv();
bg_launcher::_start($args, $runfile); bg_launcher::_start($args, $runfile);
break; break;
case self::ACTION_STOP: case self::ACTION_STOP:
bg_launcher::_stop($runfile); bg_launcher::_stop($runfile);
self::show_infos($runfile);
break; break;
case self::ACTION_INFOS: case self::ACTION_INFOS:
yaml::dump($runfile->read()); self::show_infos($runfile);
break; break;
} }
} }

View File

@ -18,10 +18,19 @@ class SteamTrainApp extends Application {
"description" => <<<EOT "description" => <<<EOT
Cette application peut être utilisée pour tester le lancement des tâches de fond Cette application peut être utilisée pour tester le lancement des tâches de fond
EOT, 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() { function main() {
$count = 100; $count = intval($this->count);
app2::action("Running train...", $count); app2::action("Running train...", $count);
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
msg::print("Tchou-tchou! x $i"); msg::print("Tchou-tchou! x $i");

View File

@ -1,11 +1,13 @@
<?php <?php
namespace nur\sery\wip\app; namespace nur\sery\wip\app;
use nur\sery\A;
use nur\sery\cl; use nur\sery\cl;
use nur\sery\file\SharedFile; use nur\sery\file\SharedFile;
use nur\sery\os\path; use nur\sery\os\path;
use nur\sery\output\msg; use nur\sery\output\msg;
use nur\sery\php\time\DateTime; use nur\sery\php\time\DateTime;
use nur\sery\php\time\Elapsed;
use nur\sery\str; use nur\sery\str;
/** /**
@ -41,18 +43,12 @@ class RunFile {
], $merge); ], $merge);
} }
protected function initData(bool $forStart=true): array { protected function initData(): array {
if ($forStart) {
$pid = posix_getpid();
$dateStart = new DateTime();
} else {
$pid = $dateStart = null;
}
return [ return [
"name" => $this->name, "name" => $this->name,
"id" => bin2hex(random_bytes(16)), "mode" => null,
"pg_pid" => null, "pgid" => null,
"pid" => $pid, "pid" => null,
"serial" => 0, "serial" => 0,
# lock # lock
"locked" => false, "locked" => false,
@ -60,10 +56,11 @@ class RunFile {
"date_release" => null, "date_release" => null,
# run # run
"logfile" => $this->logfile, "logfile" => $this->logfile,
"date_start" => $dateStart, "date_start" => null,
"date_stop" => null, "date_stop" => null,
"exitcode" => null, "exitcode" => null,
"is_done" => null, "is_reaped" => null,
"is_ack_done" => null,
# action # action
"action" => null, "action" => null,
"action_date_start" => null, "action_date_start" => null,
@ -75,7 +72,7 @@ class RunFile {
function read(): array { function read(): array {
$data = $this->file->unserialize(); $data = $this->file->unserialize();
if (!is_array($data)) $data = $this->initData(false); if (!is_array($data)) $data = $this->initData();
return $data; return $data;
} }
@ -84,7 +81,7 @@ class RunFile {
$file->lockWrite(); $file->lockWrite();
$data = $file->unserialize(null, false, true); $data = $file->unserialize(null, false, true);
if (!is_array($data)) { if (!is_array($data)) {
$data = $this->initData(false); $data = $this->initData();
$file->ftruncate(); $file->ftruncate();
$file->serialize($data, false, true); $file->serialize($data, false, true);
} }
@ -160,14 +157,40 @@ class RunFile {
# cycle de vie de l'application # cycle de vie de l'application
/** /**
* indiquer que l'application démarre. l'état est entièrement réinitialisé, * Préparer le démarrage de l'application. Cette méhode est appelée par un
* sauf le PID du leader qui est laissé en l'état * 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 { function wfStart(): void {
$this->update(function (array $data) { $this->update(function (array $data) {
return cl::merge($this->initData(), [ $pid = posix_getpid();
"pg_pid" => $data["pg_pid"], 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; return $data["date_start"] !== null && $data["date_stop"] === null;
} }
/** function _getCid(array $data=null): int {
* vérifier si l'application marquée comme démarrée tourne réellement if ($data["mode"] === "session") return -$data["pgid"];
*/ else return $data["pid"];
function isRunning(?array $data=null): bool { }
$data ??= $this->read();
if ($data["date_start"] === null) return false; function _isRunning(array $data=null): bool {
if ($data["date_stop"] !== null) return false;
if (!posix_kill($data["pid"], 0)) { if (!posix_kill($data["pid"], 0)) {
switch (posix_get_last_error()) { switch (posix_get_last_error()) {
case 1: #PCNTL_EPERM: case 1: #PCNTL_EPERM:
@ -208,10 +230,22 @@ class RunFile {
return true; 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 */ /** indiquer que l'application s'arrête */
function wfStop(): void { function wfStop(): void {
$this->update(function (array $data) { $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 */ /** 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) { $this->update(function (array $data) use ($exitcode) {
return [ return [
"pg_pid" => null, "mode" => null,
"pgid" => null,
"date_stop" => $data["date_stop"] ?? new DateTime(), "date_stop" => $data["date_stop"] ?? new DateTime(),
"exitcode" => $exitcode, "exitcode" => $exitcode,
"is_reaped" => true,
]; ];
}); });
} }
/**
* vérifier si on est dans le cas 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 * comme {@link self::isStopped()} mais ne renvoie true qu'une seule fois si
* $updateDone==true * $updateDone==true
@ -246,16 +309,42 @@ class RunFile {
$done = false; $done = false;
$this->update(function (array $ldata) use (&$done, &$data, $updateDone) { $this->update(function (array $ldata) use (&$done, &$data, $updateDone) {
$data = $ldata; $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; return false;
} }
$done = true; $done = true;
if ($updateDone) return ["is_done" => $done]; if ($updateDone) return ["is_ack_done" => $done];
else return null; else return null;
}); });
return $done; 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 # gestion des actions
@ -286,13 +375,11 @@ class RunFile {
function getActionDesc(?array $data=null): ?string { function getActionDesc(?array $data=null): ?string {
$data ??= $this->read(); $data ??= $this->read();
$action = $data["action"]; $action = $data["action"];
if ($action === null) { if ($action !== null) {
return "pid $data[pid] [$data[date_start]]";
}
$date ??= $data["action_date_step"]; $date ??= $data["action_date_step"];
$date ??= $data["action_date_start"]; $date ??= $data["action_date_start"];
if ($date !== null) $action = "[$date] $action"; if ($date !== null) $action = "$date $action";
$action = "Etape en cours: $action";
$current = $data["action_current_step"]; $current = $data["action_current_step"];
$max = $data["action_max_step"]; $max = $data["action_max_step"];
if ($current !== null && $max !== null) { if ($current !== null && $max !== null) {
@ -300,7 +387,8 @@ class RunFile {
} elseif ($current !== null) { } elseif ($current !== null) {
$action .= " ($current)"; $action .= " ($current)";
} }
return "pid $data[pid] $action"; }
return $action;
} }
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -313,64 +401,4 @@ class RunFile {
$name = str::join("/", [$this->name, $name]); $name = str::join("/", [$this->name, $name]);
return new LockFile($file, $name, $title); 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 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,
];
});
}
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace nur\sery\wip\app; namespace nur\sery\wip\app;
use Closure;
use nur\sery\A; use nur\sery\A;
use nur\sery\cl; use nur\sery\cl;
use nur\sery\ExitError; use nur\sery\ExitError;
@ -475,15 +476,12 @@ class app2 {
############################################################################# #############################################################################
const EC_UNDEAD = 247; const EC_FORK_CHILD = 250;
const EC_REAPABLE = 248; const EC_FORK_PARENT = 251;
const EC_FORK_CHILD = 249; const EC_DISABLED = 252;
const EC_FORK_PARENT = 250; const EC_LOCKED = 253;
const EC_DISABLED = 251; const EC_BAD_COMMAND = 254;
const EC_LOCKED = 252; const EC_UNEXPECTED = 255;
const EC_BAD_COMMAND = 253;
const EC_UNEXPECTED = 254;
const EC_SIGNAL = 255;
############################################################################# #############################################################################
@ -492,7 +490,7 @@ class app2 {
static function install_signal_handler(bool $allow=true): void { static function install_signal_handler(bool $allow=true): void {
if (!$allow) return; if (!$allow) return;
$signalHandler = function(int $signo, $siginfo) { $signalHandler = function(int $signo, $siginfo) {
throw new ExitError(self::EC_SIGNAL); throw new ExitError(128 + $signo);
}; };
pcntl_signal(SIGHUP, $signalHandler); pcntl_signal(SIGHUP, $signalHandler);
pcntl_signal(SIGINT, $signalHandler); pcntl_signal(SIGINT, $signalHandler);

View File

@ -94,12 +94,13 @@ abstract class Application {
break; break;
case "infos": case "infos":
case "i": case "i":
$desc = $runfile->getDesc();
if ($runfile->isRunning()) { if ($runfile->isRunning()) {
$action = $runfile->getActionDesc(); $actionDesc = $runfile->getActionDesc();
if ($action !== null) $action = ": $action"; if ($actionDesc !== null) $actionDesc = "\n$actionDesc";
echo "running$action\n"; echo "$desc$actionDesc\n";
} else { } else {
echo "not running\n"; echo "$desc\n";
$ec = 1; $ec = 1;
} }
break; break;
@ -113,7 +114,7 @@ abstract class Application {
static function run(?Application $app=null): void { static function run(?Application $app=null): void {
$unlock = false; $unlock = false;
$stop = false; $stop = false;
register_shutdown_function(function() use (&$unlock, &$stop) { $shutdown = function () use (&$unlock, &$stop) {
if ($unlock) { if ($unlock) {
app2::get()->getRunfile()->release(); app2::get()->getRunfile()->release();
$unlock = false; $unlock = false;
@ -122,7 +123,8 @@ abstract class Application {
app2::get()->getRunfile()->wfStop(); app2::get()->getRunfile()->wfStop();
$stop = false; $stop = false;
} }
}); };
register_shutdown_function($shutdown);
app2::install_signal_handler(static::USE_SIGNAL_HANDLER); app2::install_signal_handler(static::USE_SIGNAL_HANDLER);
try { try {
static::_initialize_app(); static::_initialize_app();

View File

@ -2,95 +2,38 @@
namespace nur\sery\wip\app\cli; namespace nur\sery\wip\app\cli;
use nur\sery\ExitError; use nur\sery\ExitError;
use nur\sery\file\TmpfileWriter;
use nur\sery\os\path;
use nur\sery\os\proc\Cmd; use nur\sery\os\proc\Cmd;
use nur\sery\os\sh;
use nur\sery\output\msg; use nur\sery\output\msg;
use nur\sery\wip\app\app2; use nur\sery\wip\app\app2;
use nur\sery\wip\app\args;
use nur\sery\wip\app\RunFile; use nur\sery\wip\app\RunFile;
class bg_launcher { class bg_launcher {
/** static function _start(array $args, Runfile $runfile): void {
* 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;
$pid = pcntl_fork(); $pid = pcntl_fork();
if ($pid == -1) { if ($pid == -1) {
# parent, impossible de forker # parent, impossible de forker
throw new ExitError(app2::EC_FORK_PARENT, "Unable to fork"); throw new ExitError(app2::EC_FORK_PARENT, "Unable to fork");
} elseif ($pid) { } elseif (!$pid) {
# parent, fork ok # child, fork ok
return true; $runfile->wfPrepare($pid);
} else {
## child, fork ok
# Créer un groupe de process, pour pouvoir tuer tous les enfants en même temps
$runfile->tm_startPg();
$logfile = $runfile->getLogfile() ?? "/tmp/NULIB_APP_app_start.log"; $logfile = $runfile->getLogfile() ?? "/tmp/NULIB_APP_app_start.log";
$pid = posix_getpid();
$exitcode = app2::EC_FORK_CHILD; $exitcode = app2::EC_FORK_CHILD;
try { try {
# puis lancer la commande # puis lancer la commande
$cmd = new Cmd($args); $cmd = new Cmd($args);
$cmd->addSource("/g/init.env"); $cmd->addSource("/g/init.env");
$cmd->addRedir("both", $logfile, true); $cmd->addRedir("both", $logfile, true);
msg::debug("$pid: launching\n".$cmd->getCmd()); $cmd->fork_exec($exitcode, false);
$cmd->fork_exec($exitcode); sh::_waitpid(-$pid, $exitcode);
msg::debug("$pid: exitcode=$exitcode");
return true;
} finally { } finally {
$runfile->wfStopped($exitcode); $runfile->wfReaped($exitcode);
} }
} }
} }
static function _stop(Runfile $runfile): void { private static function kill(int $pid, int $signal): bool {
$data = $runfile->read(); if (!posix_kill($pid, $signal)) {
$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)) {
switch (posix_get_last_error()) { switch (posix_get_last_error()) {
case PCNTL_ESRCH: case PCNTL_ESRCH:
msg::afailure("process inexistant"); msg::afailure("process inexistant");
@ -102,17 +45,53 @@ class bg_launcher {
msg::afailure("signal invalide"); msg::afailure("signal invalide");
break; 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; $timeout = 10;
while ($runfile->tm_isUndead($pid)) { $delay = 300000;
sleep(1); while (--$timeout >= 0) {
if (--$timeout == 0) { if (!self::kill($pid, SIGTERM)) {
msg::afailure("impossible d'arrêter la tâche"); msg::afailure();
return; return false;
} }
} usleep($delay);
$runfile->wfStopped(app2::EC_REAPABLE); $delay = 1000000; // attendre 1 seconde à partir de la deuxième fois
if (!$runfile->_isRunning($data)) {
msg::asuccess(); msg::asuccess();
$stopped = true;
break;
}
}
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;
} }
} }