diff --git a/lib/_launch.php b/lib/_launch.php index d551c96..b59b5f3 100755 --- a/lib/_launch.php +++ b/lib/_launch.php @@ -1,13 +1,26 @@ #!/usr/bin/php getRunfile(); + $runfile = app::with($appClass, self::$internal_use_app_params)->getRunfile(); switch ($this->action) { case self::ACTION_START: launcher::_start($args, $runfile); @@ -58,4 +70,6 @@ Application::run(new class extends Application { break; } } -}); +} +_LaunchApp::internal_use_set_app_params($app_params); +_LaunchApp::run(); diff --git a/nur_src/cli/Application.php b/nur_src/cli/Application.php index da0db40..99e212c 100644 --- a/nur_src/cli/Application.php +++ b/nur_src/cli/Application.php @@ -19,9 +19,20 @@ use nur\sery\output\std\StdMessenger; * Class Application: application de base */ abstract class Application { + protected static ?array $internal_use_app_params = null; + + static function internal_use_set_app_params($params) { + self::$internal_use_app_params = $params; + } + /** @var string répertoire du projet (celui qui contient composer.json */ const PROJDIR = null; + /** + * @var array répertoires vendor exprimés relativement à PROJDIR + */ + const VENDOR = null; + /** * @var string code du projet, utilisé pour dériver le noms de certains des * paramètres extraits de l'environnement, e.g XXX_DATADIR si le projet a pour @@ -85,7 +96,7 @@ abstract class Application { if ($projdir == "/") break; } - app::init(static::class); + app::init(static::class, self::$internal_use_app_params); nmsg::set_messenger(new StdMessenger([ "min_level" => nmsg::DEBUG, ])); @@ -124,9 +135,8 @@ abstract class Application { $unlock = false; $stop = false; register_shutdown_function(function () use (&$unlock, &$stop) { - $runfile = app::get()->getRunfile(); - if ($unlock) $runfile->release(); - if ($stop) $runfile->wfStop(); + if ($unlock) app::get()->getRunfile()->release(); + if ($stop) app::get()->getRunfile()->wfStop(); }); try { static::_app_init(); diff --git a/src/app/RunFile.php b/src/app/RunFile.php index 33b036b..0e9fdc1 100644 --- a/src/app/RunFile.php +++ b/src/app/RunFile.php @@ -175,14 +175,14 @@ class RunFile { if ($data["date_stop"] !== null) return false; if (!posix_kill($data["pid"], 0)) { switch (posix_get_last_error()) { - case PCNTL_ESRCH: - # process inexistant - return false; - case PCNTL_EPERM: + 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 PCNTL_EINVAL: + case 3: #PCNTL_ESRCH: + # process inexistant + return false; + case 22: #PCNTL_EINVAL: # ne devrait pas se produire return false; } @@ -281,14 +281,14 @@ class RunFile { $pid ??= $data["pid"]; if (!posix_kill($pid, 0)) { switch (posix_get_last_error()) { - case PCNTL_ESRCH: - # process inexistant - return true; - case PCNTL_EPERM: + 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 PCNTL_EINVAL: + case 3: #PCNTL_ESRCH: + # process inexistant + return true; + case 22: #PCNTL_EINVAL: # ne devrait pas se produire return false; } diff --git a/src/app/launcher.php b/src/app/launcher.php index f1cea96..73066d5 100644 --- a/src/app/launcher.php +++ b/src/app/launcher.php @@ -1,21 +1,32 @@ getVendorbindir(); + $launch_php = "$vendorBindir/_launch.php"; + if (!file_exists($launch_php)) { $launch_php = __DIR__."/../../lib/_launch.php"; } - $cmd = new Cmd([$launch_php, $appClass, ...$args]); - //$cmd->addRedir("null"); + $tmpfile = new TmpfileWriter(); + $tmpfile->serialize($app->getParams()); + + $cmd = new Cmd([ + $launch_php, + "--internal-use", $tmpfile->getFile(), + $appClass, ...$args, + ]); + $cmd->addRedir("null"); $cmd->passthru($exitcode); + + $tmpfile->close(); return $exitcode; } @@ -37,7 +48,6 @@ class launcher { # puis lancer la commande $cmd = new Cmd($args); #XXX fichier de log? - #XXX charger /g/init.env $cmd->addSource("/g/init.env"); $cmd->addRedir("null"); $cmd->fork_exec($exitcode); diff --git a/src/os/proc/AbstractCmd.php b/src/os/proc/AbstractCmd.php index ee3ee17..d11f7b1 100644 --- a/src/os/proc/AbstractCmd.php +++ b/src/os/proc/AbstractCmd.php @@ -79,7 +79,7 @@ abstract class AbstractCmd implements ICmd { function addSource(?string $source, bool $onlyIfExists=true): void { if ($source === null) return; if (!$onlyIfExists || file_exists($source)) { - $source = implode(" ", ["source", sh::quote($source)]); + $source = implode(" ", [".", sh::quote($source)]); $this->sources[] = $source; } } @@ -200,7 +200,9 @@ abstract class AbstractCmd implements ICmd { * composition de plusieurs commandes */ protected function useExec(): bool { - return $this->vars === null && count($this->cmds) == 1; + return $this->sources === null + && $this->vars === null + && count($this->cmds) == 1; } function fork_exec(int &$retcode=null): bool { diff --git a/src/os/sh.php b/src/os/sh.php index 3508c36..5b46bfd 100644 --- a/src/os/sh.php +++ b/src/os/sh.php @@ -2,6 +2,7 @@ namespace nur\sery\os; use nur\sery\cl; +use nur\sery\StateException; use RuntimeException; class sh { @@ -144,19 +145,19 @@ class sh { $pid = pcntl_fork(); if ($pid == -1) { // parent, impossible de forker - throw new RuntimeException("unable to fork"); + throw new StateException("unable to fork"); } elseif ($pid) { // parent, fork ok pcntl_waitpid($pid, $status); - if (pcntl_wifexited($status)) { - $retcode = pcntl_wexitstatus($status); - } else { - $retcode = 127; - } + if (pcntl_wifexited($status)) $retcode = pcntl_wexitstatus($status); + else $retcode = 127; return $retcode == 0; } // child, fork ok - pcntl_exec("/bin/sh", ["-c", $cmd]); + $shell = "/bin/bash"; + if (!file_exists($shell)) $shell = "/bin/sh"; + $shell = "/bin/sh"; + pcntl_exec($shell, ["-c", $cmd]); return false; } diff --git a/src/wip/app/app.php b/src/wip/app/app.php index a3520b9..ab993f9 100644 --- a/src/wip/app/app.php +++ b/src/wip/app/app.php @@ -5,112 +5,172 @@ namespace nur\sery\wip\app; use nur\cli\Application; use nur\sery\app\LockFile; use nur\sery\app\RunFile; +use nur\sery\cl; use nur\sery\os\path; use nur\sery\os\sh; use nur\sery\str; use nur\sery\ValueException; class app { + /** + * @var array répertoires vendor exprimés relativement à PROJDIR + */ + const DEFAULT_VENDOR = [ + "bindir" => "vendor/bin", + "autoload" => "vendor/autoload.php", + ]; + private static function isa_Application($app): bool { if (!is_string($app)) return false; return $app === Application::class || is_subclass_of($app, Application::class); } + private static function verifix_name(string &$name): void { + # si $name est une classe, enlever le package et normaliser + $name = preg_replace('/.*\\\\/', "", $name); + $name = str::without_suffix("-app", str::camel2us($name, false, "-")); + } + /** @param Application|string */ - static function with($app): self { + static function with($app, ?array $internal_use_params=null): self { if ($app instanceof Application) { - $projdir = $app::PROJDIR; - $appcode = $app::APPCODE; - $name = $app::NAME; - $title = $app::TITLE; - $datadir = $app::DATADIR; - $etcdir = $app::ETCDIR; - $vardir = $app::VARDIR; - $logdir = $app::LOGDIR; + $params = [ + "projdir" => $app::PROJDIR, + "vendor" => $app::VENDOR, + "appcode" => $app::APPCODE, + "apptype" => "cli", + "name" => $app::NAME, + "title" => $app::TITLE, + "datadir" => $app::DATADIR, + "etcdir" => $app::ETCDIR, + "vardir" => $app::VARDIR, + "logdir" => $app::LOGDIR, + ]; } elseif (self::isa_Application($app)) { - $projdir = constant("$app::PROJDIR"); - $appcode = constant("$app::APPCODE"); - $name = constant("$app::NAME"); - $title = constant("$app::TITLE"); - $datadir = constant("$app::DATADIR"); - $etcdir = constant("$app::ETCDIR"); - $vardir = constant("$app::VARDIR"); - $logdir = constant("$app::LOGDIR"); + $params = [ + "projdir" => constant("$app::PROJDIR"), + "vendor" => constant("$app::VENDOR"), + "appcode" => constant("$app::APPCODE"), + "apptype" => "cli", + "name" => constant("$app::NAME"), + "title" => constant("$app::TITLE"), + "datadir" => constant("$app::DATADIR"), + "etcdir" => constant("$app::ETCDIR"), + "vardir" => constant("$app::VARDIR"), + "logdir" => constant("$app::LOGDIR"), + ]; } elseif (is_array($app)) { - return new static($app); + $params = $app; } else { throw ValueException::invalid_type($app, Application::class); } - return new static([ - "projdir" => $projdir, - "appcode" => $appcode, - "apptype" => "cli", - "name" => $name, - "title" => $title, - "datadir" => $datadir, - "etcdir" => $etcdir, - "vardir" => $vardir, - "logdir" => $logdir, - ]); + if ($internal_use_params !== null) { + $params = array_merge($internal_use_params, cl::selectm($params, [ + "name", + "title", + ], [ + "apptype" => "cli", + ])); + self::verifix_name($params["name"]); + } + return new static($params, $internal_use_params !== null); } protected static ?app $app = null; - static function init($app): void { - self::$app = static::with($app); + static function init($app, ?array $internal_use_params=null): void { + self::$app = static::with($app, $internal_use_params); } static function get(): self { return self::$app ??= new self(null); } - function __construct(?array $params) { - $this->projdir = $projdir = path::abspath($params["projdir"] ?? "."); - $this->appcode = $appcode = $params["appcode"] ?? "app"; - $this->apptype = $apptype = $params["apptype"] ?? "cli"; - $name = $params["name"] ?? null; - if ($name === null) { - $name = $appcode; + function __construct(?array $params, bool $internalUse_asis=false) { + if ($internalUse_asis) { + [ + "projdir" => $this->projdir, + "vendor" => $this->vendor, + "appcode" => $this->appcode, + "apptype" => $this->apptype, + "name" => $this->name, + "title" => $this->title, + "profile" => $this->profile, + "cwd" => $this->cwd, + "datadir" => $this->datadir, + "etcdir" => $this->etcdir, + "vardir" => $this->vardir, + "logdir" => $this->logdir, + ] = $params; } else { - # si $name est une classe, enlever le package et normaliser - $name = $params["name"] ?? $appcode; - $name = preg_replace('/.*\\\\/', "", $name); - $name = str::without_suffix("-app", str::camel2us($name, false, "-")); + $this->projdir = $projdir = path::abspath($params["projdir"] ?? "."); + $vendor = $params["vendor"] ?? self::DEFAULT_VENDOR; + $vendor["bindir"] = path::reljoin($projdir, $vendor["bindir"]); + $vendor["autoload"] = path::reljoin($projdir, $vendor["autoload"]); + $this->vendor = $vendor; + $this->appcode = $appcode = $params["appcode"] ?? "app"; + $this->apptype = $apptype = $params["apptype"] ?? "cli"; + $name = $params["name"] ?? null; + if ($name === null) { + $name = $appcode; + } else { + # si $name est une classe, enlever le package et normaliser + $name = preg_replace('/.*\\\\/', "", $name); + $name = str::without_suffix("-app", str::camel2us($name, false, "-")); + } + $this->name = $name; + $this->title = $params["title"] ?? null; + $appcode = strtoupper($appcode); + # profile + $profile = getenv("${appcode}_PROFILE"); + if ($profile === false) $profile = getenv("APP_PROFILE"); + if ($profile === false) $profile = $params["profile"] ?? null; + if ($profile === null) { + if (file_exists("$projdir/.default-profile-devel")) $profile = "devel"; + else $profile = "prod"; + } + $this->profile = $profile; + # cwd + $this->cwd = getcwd(); + # datadir + $datadir = getenv("${appcode}_DATADIR"); + if ($datadir === false) $datadir = $params["datadir"] ?? null; + if ($datadir === null) $datadir = "devel/$apptype"; + $this->datadir = $datadir = path::reljoin($projdir, $datadir); + # etcdir + $etcdir = getenv("${appcode}_ETCDIR"); + if ($etcdir === false) $etcdir = $params["etcdir"] ?? null; + if ($etcdir === null) $etcdir = "etc"; + $this->etcdir = $etcdir = path::reljoin($datadir, $etcdir); + # vardir + $vardir = getenv("${appcode}_VARDIR"); + if ($vardir === false) $vardir = $params["vardir"] ?? null; + if ($vardir === null) $vardir = "var"; + $this->vardir = $vardir = path::reljoin($datadir, $vardir); + # logdir + $logdir = getenv("${appcode}_LOGDIR"); + if ($logdir === false) $logdir = $params["logdir"] ?? null; + if ($logdir === null) $logdir = "log"; + $this->logdir = $logdir = path::reljoin($datadir, $logdir); } - $this->name = $name; - $this->title = $params["title"] ?? null; - $appcode = strtoupper($appcode); - # profile - $profile = getenv("${appcode}_PROFILE"); - if ($profile === false) $profile = getenv("APP_PROFILE"); - if ($profile === false) $profile = $params["profile"] ?? null; - if ($profile === null) { - if (file_exists("$projdir/.default-profile-devel")) $profile = "devel"; - else $profile = "prod"; - } - $this->profile = $profile; - # cwd - $this->cwd = getcwd(); - # datadir - $datadir = getenv("${appcode}_DATADIR"); - if ($datadir === false) $datadir = $params["datadir"] ?? null; - if ($datadir === null) $datadir = "devel/$apptype"; - $this->datadir = $datadir = path::reljoin($projdir, $datadir); - # etcdir - $etcdir = getenv("${appcode}_ETCDIR"); - if ($etcdir === false) $etcdir = $params["etcdir"] ?? null; - if ($etcdir === null) $etcdir = "etc"; - $this->etcdir = $etcdir = path::reljoin($datadir, $etcdir); - # vardir - $vardir = getenv("${appcode}_VARDIR"); - if ($vardir === false) $vardir = $params["vardir"] ?? null; - if ($vardir === null) $vardir = "var"; - $this->vardir = $vardir = path::reljoin($datadir, $vardir); - # logdir - $logdir = getenv("${appcode}_LOGDIR"); - if ($logdir === false) $logdir = $params["logdir"] ?? null; - if ($logdir === null) $logdir = "log"; - $this->logdir = $logdir = path::reljoin($datadir, $logdir); + } + + /** recréer le tableau des paramètres */ + function getParams(): array { + return [ + "projdir" => $this->projdir, + "vendor" => $this->vendor, + "appcode" => $this->appcode, + "apptype" => $this->apptype, + "name" => $this->name, + "title" => $this->title, + "profile" => $this->profile, + "cwd" => $this->cwd, + "datadir" => $this->datadir, + "etcdir" => $this->etcdir, + "vardir" => $this->vardir, + "logdir" => $this->logdir, + ]; } protected string $projdir; @@ -119,6 +179,16 @@ class app { return $this->projdir; } + protected array $vendor; + + function getVendorBindir(): string { + return $this->vendor["bindir"]; + } + + function getVendorAutoload(): string { + return $this->vendor["autoload"]; + } + protected string $appcode; function getAppcode(): string {