119 lines
3.4 KiB
PHP
119 lines
3.4 KiB
PHP
<?php
|
|
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\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;
|
|
$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();
|
|
$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;
|
|
} finally {
|
|
$runfile->wfStopped($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)) {
|
|
switch (posix_get_last_error()) {
|
|
case PCNTL_ESRCH:
|
|
msg::afailure("process inexistant");
|
|
break;
|
|
case PCNTL_EPERM:
|
|
msg::afailure("process non accessible");
|
|
break;
|
|
case PCNTL_EINVAL:
|
|
msg::afailure("signal invalide");
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
$timeout = 10;
|
|
while ($runfile->tm_isUndead($pid)) {
|
|
sleep(1);
|
|
if (--$timeout == 0) {
|
|
msg::afailure("impossible d'arrêter la tâche");
|
|
return;
|
|
}
|
|
}
|
|
$runfile->wfStopped(app2::EC_REAPABLE);
|
|
msg::asuccess();
|
|
}
|
|
}
|