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

View File

@ -63,4 +63,36 @@ class A {
static final function mpselect(?array &$dest, ?array $merge, ?array $pkeys): void {
$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\file\SharedFile;
use nur\sery\os\path;
use nur\sery\output\msg;
use nur\sery\php\time\DateTime;
use nur\sery\str;
@ -43,9 +44,15 @@ class RunFile {
"pg_pid" => null,
"pid" => posix_getpid(),
"serial" => 0,
# lock
"locked" => false,
"date_lock" => null,
"date_release" => null,
# run
"date_start" => $dateStart,
"date_stop" => null,
"exitcode" => null,
# action
"action" => null,
"action_date_start" => null,
"action_current_step" => null,
@ -60,6 +67,93 @@ class RunFile {
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 */
function wasStarted(): bool {
$data = $this->read();
@ -97,6 +191,13 @@ class RunFile {
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 */
function wasStopped(): bool {
$data = $this->read();
@ -109,46 +210,20 @@ class RunFile {
return $data["date_start"] !== null && $data["date_stop"] !== null;
}
function haveWorked(int $serial, ?int &$currentSerial=null): bool {
$data = $this->read();
$currentSerial = $data["serial"];
return $serial !== $currentSerial;
}
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);
}
/** 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,
]);
/** après l'arrêt de l'application, mettre à jour le code de retour */
function wfStopped(int $exitcode): void {
$this->update(function (array $data) use ($exitcode) {
return [
"pg_pid" => null,
"date_stop" => $data["date_stop"] ?? new DateTime(),
"exitcode" => $exitcode,
];
});
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# gestion des actions
/** indiquer le début d'une action */
function action(?string $title, ?int $maxSteps=null): void {
$this->update(function (array $data) use ($title, $maxSteps) {
@ -171,23 +246,8 @@ class RunFile {
});
}
/** indiquer que l'application s'arrête */
function stop(): void {
$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,
];
});
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Divers
function getLockFile(?string $name=null, ?string $title=null): LockFile {
$ext = self::LOCK_EXT;
@ -197,7 +257,10 @@ class RunFile {
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 {
$this->update(function (array $data) {
posix_setsid();

View File

@ -17,13 +17,15 @@ class launcher {
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();
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 les tuer toutes en même temps
@ -34,10 +36,12 @@ class launcher {
$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);
return true;
} finally {
$runfile->stopped($exitcode);
$runfile->wfStopped($exitcode);
}
}
}
@ -72,7 +76,7 @@ class launcher {
return;
}
}
$runfile->stopped(-778);
$runfile->wfStopped(-778);
msg::asuccess();
}
}

View File

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

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\os\proc;
use nur\A;
use nur\shell;
use nur\sery\A;
use nur\sery\os\sh;
/**
* 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 {
if ($cmd !== null) {
if (!($cmd instanceof ICmd)) {
shell::fix_cmd($cmd, null, $input, $output);
sh::verifix_cmd($cmd, null, $input, $output);
}
$this->cmds[] = $cmd;
}
@ -35,6 +35,7 @@ abstract class AbstractCmdList extends AbstractCmd {
if ($sep === null) $sep = "\n";
$actualCmd = [];
A::append_nn($actualCmd, $this->getSources($sep));
A::append_nn($actualCmd, $this->getVars($sep));
$parts = [];
@ -44,9 +45,8 @@ abstract class AbstractCmdList extends AbstractCmd {
}
$parts[] = $cmd;
}
$psep = $this->sep;
if ($psep === null) $psep = $sep;
A::append($actualCmd, implode($psep, $parts));
$psep = $this->sep ?? $sep;
$actualCmd[] = implode($psep, $parts);
return implode($sep, $actualCmd);
}

View File

@ -1,8 +1,8 @@
<?php
namespace nur\sery\os\proc;
use nur\A;
use nur\shell;
use nur\sery\A;
use nur\sery\os\sh;
/**
* 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 {
if ($cmd !== null) {
if (!($cmd instanceof ICmd)) {
shell::fix_cmd($cmd);
sh::verifix_cmd($cmd);
}
$this->cmds[] = $cmd;
}
@ -53,6 +53,7 @@ class CmdPipe extends AbstractCmd {
if ($sep === null) $sep = "\n";
$actualCmd = [];
A::append_nn($actualCmd, $this->getSources($sep));
A::append_nn($actualCmd, $this->getVars($sep));
$parts = [];
@ -73,7 +74,7 @@ class CmdPipe extends AbstractCmd {
if ($output !== null) $parts[] = ">".escapeshellarg($output);
$cmd = implode(" ", $parts);
}
A::append($actualCmd, $cmd);
$actualCmd[] = $cmd;
return implode($sep, $actualCmd);
}

View File

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