modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-06-13 12:24:15 +04:00
parent 43952bb4a9
commit c35ca7e5d4
8 changed files with 208 additions and 87 deletions

View File

@ -124,27 +124,27 @@ abstract class Application {
$unlock = false; $unlock = false;
$stop = false; $stop = false;
register_shutdown_function(function () use (&$unlock, &$stop) { register_shutdown_function(function () use (&$unlock, &$stop) {
$self = app::get(); $runfile = app::get()->getRunfile();
if ($unlock) $self->getLockfile()->release(); if ($unlock) $runfile->release();
if ($stop) $self->getRunfile()->stop(); if ($stop) $runfile->wfStop();
}); });
try { try {
static::_app_init(); static::_app_init();
if (static::USE_RUNFILE) { if (static::USE_RUNFILE) {
$self = app::get(); $runfile = app::get()->getRunfile();
global $argc, $argv; global $argc, $argv;
if ($argc === 2 && $argv[1] === "--Application-Runlock-release") { if ($argc === 2 && $argv[1] === "--Application-Runlock-release") {
$self->getLockfile()->release(); $runfile->release();
exit(0); exit(0);
} }
$useRunlock = static::USE_RUNLOCK; $useRunlock = static::USE_RUNLOCK;
if ($useRunlock && $self->getLockfile()->warnIfLocked()) { if ($useRunlock && $runfile->warnIfLocked()) {
exit(1); exit(1);
} }
$self->getRunfile()->start(); $runfile->wfStart();
$stop = true; $stop = true;
if ($useRunlock) { if ($useRunlock) {
$self->getLockfile()->lock(); $runfile->lock();
$unlock = true; $unlock = true;
} }
} }

View File

@ -63,4 +63,36 @@ class A {
static final function mpselect(?array &$dest, ?array $merge, ?array $pkeys): void { static final function mpselect(?array &$dest, ?array $merge, ?array $pkeys): void {
$dest = cl::mpselect($dest, $merge, $pkeys); $dest = cl::mpselect($dest, $merge, $pkeys);
} }
static final function append_nn(?array &$dest, $value, $key=null) {
if ($value !== null) {
if ($key === null) $dest[] = $value;
else $dest[$key] = $value;
}
return $value;
}
static final function append_nz(?array &$dest, $value, $key=null) {
if ($value !== null && $value !== false) {
if ($key === null) $dest[] = $value;
else $dest[$key] = $value;
}
return $value;
}
static final function prepend_nn(?array &$dest, $value) {
if ($value !== null) {
if ($dest === null) $dest = [];
array_unshift($dest, $value);
}
return $value;
}
static final function prepend_nz(?array &$dest, $value) {
if ($value !== null && $value !== false) {
if ($dest === null) $dest = [];
array_unshift($dest, $value);
}
return $value;
}
} }

View File

@ -4,6 +4,7 @@ namespace nur\sery\app;
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\php\time\DateTime; use nur\sery\php\time\DateTime;
use nur\sery\str; use nur\sery\str;
@ -43,9 +44,15 @@ class RunFile {
"pg_pid" => null, "pg_pid" => null,
"pid" => posix_getpid(), "pid" => posix_getpid(),
"serial" => 0, "serial" => 0,
# lock
"locked" => false,
"date_lock" => null,
"date_release" => null,
# run
"date_start" => $dateStart, "date_start" => $dateStart,
"date_stop" => null, "date_stop" => null,
"exitcode" => null, "exitcode" => null,
# action
"action" => null, "action" => null,
"action_date_start" => null, "action_date_start" => null,
"action_current_step" => null, "action_current_step" => null,
@ -60,6 +67,93 @@ class RunFile {
return $data; return $data;
} }
protected function willWrite(): array {
$file = $this->file;
$file->lockWrite();
$data = $file->unserialize(null, false, true);
if (!is_array($data)) {
$data = $this->initData();
$file->ftruncate();
$file->serialize($data, false, true);
}
$file->ftruncate();
return [$file, $data];
}
protected function serialize(SharedFile $file, array $data, ?array $merge=null): void {
$file->serialize(self::merge($data, $merge), true, true);
}
protected function update(callable $func): void {
[$file, $data] = $this->willWrite();
$merge = call_user_func($func, $data);
$this->serialize($file, $data, $merge);
}
function haveWorked(int $serial, ?int &$currentSerial=null): bool {
$data = $this->read();
$currentSerial = $data["serial"];
return $serial !== $currentSerial;
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# verrouillage par défaut
function isLocked(?array &$data=null): bool {
$data = $this->read();
return $data["locked"];
}
function warnIfLocked(?array $data=null): bool {
if ($data === null) $data = $this->read();
if ($data["locked"]) {
msg::warning("$data[name]: possède le verrou depuis $data[date_lock]");
return true;
}
return false;
}
function lock(): bool {
$this->update(function ($data) use (&$locked) {
if ($data["locked"]) {
$locked = false;
return null;
} else {
$locked = true;
return [
"locked" => true,
"date_lock" => new DateTime(),
"date_release" => null,
];
}
});
return $locked;
}
function release(): void {
$this->update(function ($data) {
return [
"locked" => false,
"date_release" => new DateTime(),
];
});
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# cycle de vie de l'application
/**
* indiquer que l'application démarre. l'état est entièrement réinitialisé,
* sauf le PID du leader qui est laissé en l'état
*/
function wfStart(): void {
$this->update(function (array $data) {
return cl::merge($this->initData(), [
"pg_pid" => $data["pg_pid"],
]);
});
}
/** tester si l'application a déjà été démarrée */ /** tester si l'application a déjà été démarrée */
function wasStarted(): bool { function wasStarted(): bool {
$data = $this->read(); $data = $this->read();
@ -97,6 +191,13 @@ class RunFile {
return true; return true;
} }
/** indiquer que l'application s'arrête */
function wfStop(): void {
$this->update(function (array $data) {
return ["date_stop" => new DateTime()];
});
}
/** tester si l'application est déjà été stoppée */ /** tester si l'application est déjà été stoppée */
function wasStopped(): bool { function wasStopped(): bool {
$data = $this->read(); $data = $this->read();
@ -109,46 +210,20 @@ class RunFile {
return $data["date_start"] !== null && $data["date_stop"] !== null; return $data["date_start"] !== null && $data["date_stop"] !== null;
} }
function haveWorked(int $serial, ?int &$currentSerial=null): bool { /** après l'arrêt de l'application, mettre à jour le code de retour */
$data = $this->read(); function wfStopped(int $exitcode): void {
$currentSerial = $data["serial"]; $this->update(function (array $data) use ($exitcode) {
return $serial !== $currentSerial; return [
} "pg_pid" => null,
"date_stop" => $data["date_stop"] ?? new DateTime(),
protected function willWrite(): array { "exitcode" => $exitcode,
$file = $this->file; ];
$file->lockWrite();
$data = $file->unserialize(null, false, true);
if (!is_array($data)) {
$data = $this->initData();
$file->ftruncate();
$file->serialize($data, false, true);
}
$file->ftruncate();
return [$file, $data];
}
protected function serialize(SharedFile $file, array $data, ?array $merge=null): void {
$file->serialize(self::merge($data, $merge), true, true);
}
protected function update(callable $func): void {
[$file, $data] = $this->willWrite();
$merge = call_user_func($func, $data);
$this->serialize($file, $data, $merge);
}
/** indiquer que l'application démarre */
function start(): void {
$this->update(function (array $data) {
# garder l'identifiant de process
$pgPid = $data["pg_pid"] ?? null;
return cl::merge($this->initData(), [
"pg_pid" => $pgPid,
]);
}); });
} }
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# gestion des actions
/** indiquer le début d'une action */ /** indiquer le début d'une action */
function action(?string $title, ?int $maxSteps=null): void { function action(?string $title, ?int $maxSteps=null): void {
$this->update(function (array $data) use ($title, $maxSteps) { $this->update(function (array $data) use ($title, $maxSteps) {
@ -171,23 +246,8 @@ class RunFile {
}); });
} }
/** indiquer que l'application s'arrête */ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function stop(): void { # Divers
$this->update(function (array $data) {
return ["date_stop" => new DateTime()];
});
}
/** après l'arrêt de l'application, mettre à jour le code de retour */
function stopped(int $exitcode): void {
$this->update(function (array $data) use ($exitcode) {
return [
"pg_pid" => null,
"date_stop" => $data["date_stop"] ?? new DateTime(),
"exitcode" => $exitcode,
];
});
}
function getLockFile(?string $name=null, ?string $title=null): LockFile { function getLockFile(?string $name=null, ?string $title=null): LockFile {
$ext = self::LOCK_EXT; $ext = self::LOCK_EXT;
@ -197,7 +257,10 @@ class RunFile {
return new LockFile($file, $name, $title); return new LockFile($file, $name, $title);
} }
/** démarrer un groupe de process */ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Gestionnaire de tâches (tm_*)
/** démarrer un groupe de process dont le process courant est le leader */
function tm_startPg(): void { function tm_startPg(): void {
$this->update(function (array $data) { $this->update(function (array $data) {
posix_setsid(); posix_setsid();

View File

@ -17,13 +17,15 @@ class launcher {
return $exitcode; return $exitcode;
} }
static function _start(array $args, Runfile $runfile): void { static function _start(array $args, Runfile $runfile): bool {
if ($runfile->isLocked()) return false;
$pid = pcntl_fork(); $pid = pcntl_fork();
if ($pid == -1) { if ($pid == -1) {
# parent, impossible de forker # parent, impossible de forker
throw new StateException("unable to fork"); throw new StateException("unable to fork");
} elseif ($pid) { } elseif ($pid) {
# parent, fork ok # parent, fork ok
return true;
} else { } else {
## child, fork ok ## child, fork ok
# Créer un groupe de process, pour pouvoir les tuer toutes en même temps # Créer un groupe de process, pour pouvoir les tuer toutes en même temps
@ -34,10 +36,12 @@ class launcher {
$cmd = new Cmd($args); $cmd = new Cmd($args);
#XXX fichier de log? #XXX fichier de log?
#XXX charger /g/init.env #XXX charger /g/init.env
$cmd->addSource("/g/init.env");
$cmd->addRedir("null"); $cmd->addRedir("null");
$cmd->fork_exec($exitcode); $cmd->fork_exec($exitcode);
return true;
} finally { } finally {
$runfile->stopped($exitcode); $runfile->wfStopped($exitcode);
} }
} }
} }
@ -72,7 +76,7 @@ class launcher {
return; return;
} }
} }
$runfile->stopped(-778); $runfile->wfStopped(-778);
msg::asuccess(); msg::asuccess();
} }
} }

View File

@ -1,16 +1,17 @@
<?php <?php
namespace nur\sery\os\proc; namespace nur\sery\os\proc;
use nur\A; use nur\sery\A;
use nur\base; use nur\sery\cv;
use nur\sery\os\sh; use nur\sery\os\sh;
use nur\shell;
abstract class AbstractCmd implements ICmd { abstract class AbstractCmd implements ICmd {
private bool $needsStdin; private bool $needsStdin;
private bool $needsTty; private bool $needsTty;
protected ?array $sources;
protected ?array $vars; protected ?array $vars;
protected array $cmds; protected array $cmds;
@ -18,6 +19,7 @@ abstract class AbstractCmd implements ICmd {
function __construct() { function __construct() {
$this->needsStdin = true; $this->needsStdin = true;
$this->needsTty = true; $this->needsTty = true;
$this->sources = null;
$this->vars = null; $this->vars = null;
$this->cmds = []; $this->cmds = [];
} }
@ -74,13 +76,27 @@ abstract class AbstractCmd implements ICmd {
$this->needsTty = $needsTty; $this->needsTty = $needsTty;
} }
function addSource(?string $source, bool $onlyIfExists=true): void {
if ($source === null) return;
if (!$onlyIfExists || file_exists($source)) {
$source = implode(" ", ["source", sh::quote($source)]);
$this->sources[] = $source;
}
}
function getSources(?string $sep=null): ?string {
if ($this->sources === null) return null;
if ($sep === null) $sep = "\n";
return implode($sep, $this->sources);
}
function addLiteralVars($vars, ?string $sep=null): void { function addLiteralVars($vars, ?string $sep=null): void {
if (base::z($vars)) return; if (cv::z($vars)) return;
if (is_array($vars)) { if (is_array($vars)) {
if ($sep === null) $sep = "\n"; if ($sep === null) $sep = "\n";
$vars = implode($sep, $vars); $vars = implode($sep, $vars);
} }
A::append($this->vars, strval($vars)); $this->vars[] = strval($vars);
} }
function addVars(?array $vars): void { function addVars(?array $vars): void {
@ -88,8 +104,8 @@ abstract class AbstractCmd implements ICmd {
foreach ($vars as $name => $value) { foreach ($vars as $name => $value) {
$var = []; $var = [];
if (!is_array($value)) $var[] = "export "; if (!is_array($value)) $var[] = "export ";
A::merge($var, [$name, "=", shell::quote($value)]); A::merge($var, [$name, "=", sh::quote($value)]);
A::append($this->vars, implode("", $var)); $this->vars[] = implode("", $var);
} }
} }
@ -106,7 +122,7 @@ abstract class AbstractCmd implements ICmd {
if ($cmd instanceof ICmd) { if ($cmd instanceof ICmd) {
$cmd->addPrefix($prefix); $cmd->addPrefix($prefix);
} elseif (is_array($prefix)) { } elseif (is_array($prefix)) {
$prefix = shell::join($prefix); $prefix = sh::join($prefix);
$cmd = "$prefix $cmd"; $cmd = "$prefix $cmd";
} else { } else {
$cmd = "$prefix $cmd"; $cmd = "$prefix $cmd";
@ -167,8 +183,8 @@ abstract class AbstractCmd implements ICmd {
} }
function system(string &$output=null, int &$retcode=null): bool { function system(string &$output=null, int &$retcode=null): bool {
$last_line = system($this->getCmd(), $retcode); $lastLine = system($this->getCmd(), $retcode);
if ($last_line !== false) $output = $last_line; if ($lastLine !== false) $output = $lastLine;
return $retcode == 0; return $retcode == 0;
} }

View File

@ -1,8 +1,8 @@
<?php <?php
namespace nur\sery\os\proc; namespace nur\sery\os\proc;
use nur\A; use nur\sery\A;
use nur\shell; use nur\sery\os\sh;
/** /**
* Class AbstractCmdList: une séquence de commandes séparées par ;, && ou || * Class AbstractCmdList: une séquence de commandes séparées par ;, && ou ||
@ -24,7 +24,7 @@ abstract class AbstractCmdList extends AbstractCmd {
function add($cmd, ?string $input=null, ?string $output=null): self { function add($cmd, ?string $input=null, ?string $output=null): self {
if ($cmd !== null) { if ($cmd !== null) {
if (!($cmd instanceof ICmd)) { if (!($cmd instanceof ICmd)) {
shell::fix_cmd($cmd, null, $input, $output); sh::verifix_cmd($cmd, null, $input, $output);
} }
$this->cmds[] = $cmd; $this->cmds[] = $cmd;
} }
@ -35,6 +35,7 @@ abstract class AbstractCmdList extends AbstractCmd {
if ($sep === null) $sep = "\n"; if ($sep === null) $sep = "\n";
$actualCmd = []; $actualCmd = [];
A::append_nn($actualCmd, $this->getSources($sep));
A::append_nn($actualCmd, $this->getVars($sep)); A::append_nn($actualCmd, $this->getVars($sep));
$parts = []; $parts = [];
@ -44,9 +45,8 @@ abstract class AbstractCmdList extends AbstractCmd {
} }
$parts[] = $cmd; $parts[] = $cmd;
} }
$psep = $this->sep; $psep = $this->sep ?? $sep;
if ($psep === null) $psep = $sep; $actualCmd[] = implode($psep, $parts);
A::append($actualCmd, implode($psep, $parts));
return implode($sep, $actualCmd); return implode($sep, $actualCmd);
} }

View File

@ -1,8 +1,8 @@
<?php <?php
namespace nur\sery\os\proc; namespace nur\sery\os\proc;
use nur\A; use nur\sery\A;
use nur\shell; use nur\sery\os\sh;
/** /**
* Class CmdPipe: une suite de commandes qui doivent s'exécuter avec les sorties * Class CmdPipe: une suite de commandes qui doivent s'exécuter avec les sorties
@ -32,7 +32,7 @@ class CmdPipe extends AbstractCmd {
function add($cmd): self { function add($cmd): self {
if ($cmd !== null) { if ($cmd !== null) {
if (!($cmd instanceof ICmd)) { if (!($cmd instanceof ICmd)) {
shell::fix_cmd($cmd); sh::verifix_cmd($cmd);
} }
$this->cmds[] = $cmd; $this->cmds[] = $cmd;
} }
@ -53,6 +53,7 @@ class CmdPipe extends AbstractCmd {
if ($sep === null) $sep = "\n"; if ($sep === null) $sep = "\n";
$actualCmd = []; $actualCmd = [];
A::append_nn($actualCmd, $this->getSources($sep));
A::append_nn($actualCmd, $this->getVars($sep)); A::append_nn($actualCmd, $this->getVars($sep));
$parts = []; $parts = [];
@ -73,7 +74,7 @@ class CmdPipe extends AbstractCmd {
if ($output !== null) $parts[] = ">".escapeshellarg($output); if ($output !== null) $parts[] = ">".escapeshellarg($output);
$cmd = implode(" ", $parts); $cmd = implode(" ", $parts);
} }
A::append($actualCmd, $cmd); $actualCmd[] = $cmd;
return implode($sep, $actualCmd); return implode($sep, $actualCmd);
} }

View File

@ -7,6 +7,7 @@ use nur\sery\app\LockFile;
use nur\sery\app\RunFile; use nur\sery\app\RunFile;
use nur\sery\os\path; use nur\sery\os\path;
use nur\sery\os\sh; use nur\sery\os\sh;
use nur\sery\str;
use nur\sery\ValueException; use nur\sery\ValueException;
class app { class app {
@ -67,7 +68,11 @@ class app {
$this->projdir = $projdir = path::abspath($params["projdir"] ?? "."); $this->projdir = $projdir = path::abspath($params["projdir"] ?? ".");
$this->appcode = $appcode = $params["appcode"] ?? "app"; $this->appcode = $appcode = $params["appcode"] ?? "app";
$this->apptype = $apptype = $params["apptype"] ?? "cli"; $this->apptype = $apptype = $params["apptype"] ?? "cli";
$this->name = $params["name"] ?? $appcode; # 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));
$this->name = $name;
$this->title = $params["title"] ?? null; $this->title = $params["title"] ?? null;
$appcode = strtoupper($appcode); $appcode = strtoupper($appcode);
# profile # profile