modifs.mineures sans commentaires
This commit is contained in:
parent
e9bcf77a4d
commit
4bb386167f
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="nur\sery\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="nur\sery\" />
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
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\yaml;
|
||||
|
||||
class BgLauncherApp extends Application {
|
||||
const PROJDIR = __DIR__.'/../..';
|
||||
|
||||
const ACTION_INFOS = 0, ACTION_START = 1, ACTION_STOP = 2;
|
||||
const ARGS = [
|
||||
"purpose" => "lancer un script en tâche de fond",
|
||||
"usage" => "ApplicationClass args...",
|
||||
|
||||
["-i", "--infos", "name" => "action", "value" => self::ACTION_INFOS,
|
||||
"help" => "Afficher des informations sur la tâche",
|
||||
],
|
||||
["-s", "--start", "name" => "action", "value" => self::ACTION_START,
|
||||
"help" => "Démarrer la tâche",
|
||||
],
|
||||
["-k", "--stop", "name" => "action", "value" => self::ACTION_STOP,
|
||||
"help" => "Arrêter la tâche",
|
||||
],
|
||||
];
|
||||
|
||||
protected int $action = self::ACTION_START;
|
||||
|
||||
protected ?array $args = null;
|
||||
|
||||
function main() {
|
||||
$appClass = $this->args[0] ?? null;
|
||||
if ($appClass === null) {
|
||||
self::die("Vous devez spécifier la classe de l'application");
|
||||
}
|
||||
$appClass = str_replace("/", "\\", $appClass);
|
||||
if (!class_exists($appClass)) {
|
||||
self::die("$appClass: classe non trouvée");
|
||||
}
|
||||
$args = array_slice($this->args, 1);
|
||||
|
||||
$useRunfile = constant("$appClass::USE_RUNFILE");
|
||||
if (!$useRunfile) {
|
||||
self::die("Cette application ne supporte le lancement en tâche de fond");
|
||||
}
|
||||
|
||||
$runfile = app2::with($appClass)->getRunfile();
|
||||
switch ($this->action) {
|
||||
case self::ACTION_START:
|
||||
bg_launcher::_start($args, $runfile);
|
||||
break;
|
||||
case self::ACTION_STOP:
|
||||
bg_launcher::_stop($runfile);
|
||||
break;
|
||||
case self::ACTION_INFOS:
|
||||
yaml::dump($runfile->read());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$params = app2::params_getenv();
|
||||
if ($params !== null) app2::init($params);
|
||||
BgLauncherApp::run();
|
|
@ -65,6 +65,7 @@
|
|||
},
|
||||
"bin": [
|
||||
"lib/_launch.php",
|
||||
"bin/bg-launcher.php",
|
||||
"nur_bin/compctl.php",
|
||||
"nur_bin/compdep.php",
|
||||
"nur_bin/cachectl.php",
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
<?php
|
||||
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use nur\sery\wip\tools\SteamTrainApp;
|
||||
use nur\sery\tools\SteamTrainApp;
|
||||
|
||||
SteamTrainApp::run();
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
namespace nur\sery\os;
|
||||
|
||||
use nur\sery\cl;
|
||||
use nur\sery\ExitError;
|
||||
use nur\sery\StateException;
|
||||
use nur\sery\wip\app\app2;
|
||||
|
||||
class sh {
|
||||
static final function _quote(string $value): string {
|
||||
|
@ -144,12 +146,12 @@ class sh {
|
|||
$pid = pcntl_fork();
|
||||
if ($pid == -1) {
|
||||
// parent, impossible de forker
|
||||
throw new StateException("unable to fork");
|
||||
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 = 127;
|
||||
else $retcode = app2::EC_FORK_CHILD;
|
||||
return $retcode == 0;
|
||||
}
|
||||
// child, fork ok
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\tools;
|
||||
namespace nur\sery\tools;
|
||||
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\wip\app\app2;
|
||||
use nur\sery\wip\app\cli\Application;
|
||||
|
||||
class SteamTrainApp extends Application {
|
||||
const PROJDIR = __DIR__.'/../..';
|
||||
const TITLE = "Train à vapeur";
|
||||
const USE_SIGNAL_HANDLER = true;
|
||||
const USE_LOGFILE = true;
|
||||
const USE_RUNFILE = true;
|
||||
const USE_RUNLOCK = true;
|
||||
const USE_SIGNAL_HANDLER = true;
|
||||
|
||||
const ARGS = [
|
||||
"purpose" => self::TITLE,
|
||||
"description" => <<<EOT
|
||||
Cette application peut être utilisée pour tester le lancement des tâches de fond
|
||||
EOT,
|
||||
];
|
||||
|
||||
function main() {
|
||||
$runfile = app2::get()->getRunfile();
|
||||
$runfile->action("Running train...", 100);
|
||||
for ($i = 1; $i <= 100; $i++) {
|
||||
$count = 100;
|
||||
app2::action("Running train...", $count);
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
msg::print("Tchou-tchou! x $i");
|
||||
$runfile->step();
|
||||
app2::step();
|
||||
sleep(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use nulib\tests\TestCase;
|
||||
|
||||
class argsTest extends TestCase {
|
||||
function testFrom_array() {
|
||||
self::assertSame([], args::from_array(null));
|
||||
self::assertSame([], args::from_array([]));
|
||||
self::assertSame([], args::from_array([false]));
|
||||
self::assertSame(["x"], args::from_array(["x", false]));
|
||||
|
||||
self::assertSame(["--opt"], args::from_array(["--opt"]));
|
||||
self::assertSame(["--opt", "value"], args::from_array(["--opt", "value"]));
|
||||
|
||||
self::assertSame([], args::from_array(["opt" => false]));
|
||||
self::assertSame(["--opt"], args::from_array(["opt" => true]));
|
||||
self::assertSame(["--opt", "value"], args::from_array(["opt" => "value"]));
|
||||
self::assertSame(["--opt", "42"], args::from_array(["opt" => 42]));
|
||||
self::assertSame(["--opt", "1", "2", "3", "--"], args::from_array(["opt" => [1, 2, 3]]));
|
||||
|
||||
self::assertSame(["x", "1", "2", "3", "y"], args::from_array(["x", [1, 2, 3], "y"]));
|
||||
}
|
||||
}
|
|
@ -269,6 +269,7 @@ class RunFile {
|
|||
"action_current_step" => 0,
|
||||
];
|
||||
});
|
||||
app2::_dispatch_signals();
|
||||
}
|
||||
|
||||
/** indiquer qu'une étape est franchie dans l'action en cours */
|
||||
|
|
|
@ -6,7 +6,6 @@ use nur\sery\cl;
|
|||
use nur\sery\ExitError;
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\os\sh;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\php\func;
|
||||
use nur\sery\str;
|
||||
use nur\sery\ValueException;
|
||||
|
@ -476,6 +475,10 @@ 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;
|
||||
|
@ -536,4 +539,14 @@ class app2 {
|
|||
throw new ExitError(self::EC_DISABLED, "Planifications désactivées. La tâche n'a pas été lancée");
|
||||
}
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
|
||||
static function action(?string $title, ?int $maxSteps=null): void {
|
||||
self::get()->getRunfile()->action($title, $maxSteps);
|
||||
}
|
||||
|
||||
static function step(int $nbSteps=1): void {
|
||||
self::get()->getRunfile()->step($nbSteps);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use nur\sery\A;
|
||||
use nur\sery\str;
|
||||
|
||||
class args {
|
||||
/**
|
||||
* transformer une liste d'argument de la forme
|
||||
* - ["myArg" => $value] devient ["--my-arg", "$value"]
|
||||
* - ["myOpt" => true] devient ["--my-opt"]
|
||||
* - ["myOpt" => false] est omis
|
||||
* - les autres valeurs sont prises telles quelles
|
||||
*/
|
||||
static function from_array(?array $array): array {
|
||||
$args = [];
|
||||
if ($array === null) return $args;
|
||||
$index = 0;
|
||||
foreach ($array as $arg => $value) {
|
||||
if ($value === false) continue;
|
||||
if ($arg === $index) {
|
||||
$index++;
|
||||
} else {
|
||||
$arg = str::us2camel($arg);
|
||||
$arg = str::camel2us($arg, false, "-");
|
||||
$arg = str_replace("_", "-", $arg);
|
||||
$args[] = "--$arg";
|
||||
if (is_array($value)) $value[] = "--";
|
||||
elseif ($value === true) $value = null;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
A::merge($args, array_map("strval", $value));
|
||||
} elseif ($value !== null) {
|
||||
$args[] = "$value";
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,119 @@
|
|||
<?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\StateException;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
<?php
|
||||
namespace nur\sery\wip\app;
|
||||
|
||||
use nur\sery\app\Runfile;
|
||||
use nur\sery\cl;
|
||||
use nur\sery\file\TmpfileWriter;
|
||||
use nur\sery\os\path;
|
||||
use nur\sery\os\proc\Cmd;
|
||||
use nur\sery\output\msg;
|
||||
use nur\sery\StateException;
|
||||
use nur\sery\str;
|
||||
use nur\sery\wip\app\app2;
|
||||
|
||||
class 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 {
|
||||
if (!cl::is_list($args)) {
|
||||
$fixedArgs = [];
|
||||
$index = 0;
|
||||
foreach ($args as $arg => $value) {
|
||||
if ($arg === $index) {
|
||||
$index++;
|
||||
$fixedArgs[] = $value;
|
||||
continue;
|
||||
} elseif ($value === false) {
|
||||
continue;
|
||||
}
|
||||
$arg = str::us2camel($arg);
|
||||
$arg = str::camel2us($arg, false, "-");
|
||||
$arg = str_replace("_", "-", $arg);
|
||||
$fixedArgs[] = "--$arg";
|
||||
if ($value !== true) $fixedArgs[] = "$value";
|
||||
}
|
||||
$args = $fixedArgs;
|
||||
}
|
||||
# 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 StateException("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_launcher-_start.log";
|
||||
$pid = posix_getpid();
|
||||
$exitcode = -776;
|
||||
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(-778);
|
||||
msg::asuccess();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue