nur-sery/wip/app/cli/bg_launcher.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();
}
}