Compare commits

...

3 Commits

Author SHA1 Message Date
Jephté Clain 1cd546cdd8 maj deps php8.2 2024-11-29 08:50:54 +04:00
Jephté Clain 3958b8f11d modifs.mineures sans commentaires 2024-11-29 08:40:10 +04:00
Jephté Clain 71cceabcfb réintégrer nulib/app et nulib/tools 2024-11-29 08:37:57 +04:00
9 changed files with 411 additions and 4 deletions

View File

@ -4,5 +4,5 @@
RUNPHP=
# Si RUNPHP n'est pas défini, les variables suivantes peuvent être définies
DIST=d11
DIST=d12
#REGISTRY=pubdocker.univ-reunion.fr

View File

@ -15,12 +15,12 @@
}
},
"require": {
"symfony/yaml": "^5.0",
"symfony/yaml": "^7.1",
"ext-json": "*",
"php": "^7.4"
"php": "^8.2"
},
"require-dev": {
"nulib/tests": "7.4",
"nulib/tests": "8.2",
"ext-posix": "*",
"ext-pcntl": "*",
"ext-curl": "*"

View File

@ -0,0 +1,29 @@
<?php
# script à inclure pour implémenter .launcher.php
# les constantes suivantes doivent être définies AVANT de chager ce script:
# - NULIB_APP_app_params : paramètres du projet
use nulib\app;
use nulib\os\path;
if ($argc <= 1) die("invalid arguments");
app::init(NULIB_APP_app_params);
$app = $argv[1];
array_splice($argv, 0, 1); $argc--;
if (class_exists($app)) {
# la configuration est celle actuellement chargée
$app::run();
} elseif (is_executable($app)) {
# la configuration est passée par une variable d'environnement
app::params_putenv();
pcntl_exec($app, array_slice($argv, 1));
} else {
# la configuration est celle actuellement chargée
$name = preg_replace('/\.php$/', "", path::basename($app));
app::init([
"name" => $name,
]);
require $app;
}

View File

@ -0,0 +1,124 @@
<?php
namespace nulib\tools;
use nulib\app;
use nulib\app\cli\Application;
use nulib\app\RunFile;
use nulib\ExitError;
use nulib\ext\yaml;
use nulib\os\path;
use nulib\os\proc\Cmd;
use nulib\os\sh;
use nulib\output\msg;
class BgLauncherApp extends Application {
const ACTION_INFOS = 0, ACTION_START = 1, ACTION_STOP = 2;
const ARGS = [
"purpose" => "lancer un script en tâche de fond",
"usage" => "ApplicationClass args...",
"sections" => [
parent::VERBOSITY_SECTION,
],
["-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;
static function show_infos(RunFile $runfile, ?int $level=null): void {
msg::print($runfile->getDesc(), $level);
msg::print(yaml::with(["data" => $runfile->read()]), ($level ?? 0) - 1);
}
function main() {
$args = $this->args;
$appClass = $args[0] ?? null;
if ($appClass === null) {
self::die("Vous devez spécifier la classe de l'application");
}
$appClass = $args[0] = str_replace("/", "\\", $appClass);
if (!class_exists($appClass)) {
self::die("$appClass: classe non trouvée");
}
$useRunfile = constant("$appClass::USE_RUNFILE");
if (!$useRunfile) {
self::die("Cette application ne supporte le lancement en tâche de fond");
}
$runfile = app::with($appClass)->getRunfile();
switch ($this->action) {
case self::ACTION_START:
$argc = count($args);
$appClass::_manage_runfile($argc, $args, $runfile);
if ($runfile->warnIfLocked()) self::exit(app::EC_LOCKED);
array_splice($args, 0, 0, [
PHP_BINARY,
path::abspath(NULIB_APP_app_launcher),
]);
app::params_putenv();
self::_start($args, $runfile);
break;
case self::ACTION_STOP:
self::_stop($runfile);
self::show_infos($runfile, -1);
break;
case self::ACTION_INFOS:
self::show_infos($runfile);
break;
}
}
public static function _start(array $args, Runfile $runfile): void {
$pid = pcntl_fork();
if ($pid == -1) {
# parent, impossible de forker
throw new ExitError(app::EC_FORK_PARENT, "Unable to fork");
} elseif (!$pid) {
# child, fork ok
$runfile->wfPrepare($pid);
$outfile = $runfile->getOutfile() ?? "/tmp/NULIB_APP_app_console.out";
$exitcode = app::EC_FORK_CHILD;
try {
# rediriger STDIN, STDOUT et STDERR
fclose(fopen($outfile, "wb")); // vider le fichier
fclose(STDIN); $in = fopen("/dev/null", "rb");
fclose(STDOUT); $out = fopen($outfile, "ab");
fclose(STDERR); $err = fopen($outfile, "ab");
# puis lancer la commande
$cmd = new Cmd($args);
$cmd->addSource("/g/init.env");
$cmd->addRedir("both", $outfile, true);
$cmd->fork_exec($exitcode, false);
sh::_waitpid(-$pid, $exitcode);
} finally {
$runfile->wfReaped($exitcode);
}
}
}
public static function _stop(Runfile $runfile): bool {
$data = $runfile->read();
$pid = $runfile->_getCid($data);
msg::action("stop $pid");
if ($runfile->wfKill($reason)) {
msg::asuccess();
return true;
} else {
msg::afailure($reason);
return false;
}
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace nulib\tools;
use nulib\app;
use nulib\app\cli\Application;
use nulib\output\msg;
use nulib\php\time\DateTime;
use nulib\text\words;
class SteamTrainApp extends Application {
const PROJDIR = __DIR__.'/../..';
const TITLE = "Train à vapeur";
const USE_LOGFILE = true;
const USE_RUNFILE = true;
const USE_RUNLOCK = true;
const ARGS = [
"purpose" => self::TITLE,
"description" => <<<EOT
Cette application peut être utilisée pour tester le lancement des tâches de fond
EOT,
["-c", "--count", "args" => 1,
"help" => "spécifier le nombre d'étapes",
],
["-f", "--force-enabled", "value" => true,
"help" => "lancer la commande même si les tâches planifiées sont désactivées",
],
["-n", "--no-install-signal-handler", "value" => false,
"help" => "ne pas installer le gestionnaire de signaux",
],
];
protected $count = 100;
protected bool $forceEnabled = false;
protected bool $installSignalHandler = true;
function main() {
app::check_bgapplication_enabled($this->forceEnabled);
if ($this->installSignalHandler) app::install_signal_handler();
$count = intval($this->count);
msg::info("Starting train for ".words::q($count, "step#s"));
app::action("Running train...", $count);
for ($i = 1; $i <= $count; $i++) {
msg::print("Tchou-tchou! x $i");
app::step();
sleep(1);
}
msg::info("Stopping train at ".new DateTime());
}
}

39
php/support/copy-templates Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source /etc/nulib.sh || exit 1
declare -A DESTDIRS=(
[template-_bg_launcher.php]=sbin
[template-.launcher.php]=_cli
[template-_wrapper.sh]=_cli
)
declare -A MODES=(
[template-_bg_launcher.php]=+x
[template-.launcher.php]=
[template-_wrapper.sh]=+x
)
projdir=
args=(
"copier les templates dans le projet en cours"
#"usage"
-d:,--projdir: .
)
parse_args "$@"; set -- "${args[@]}"
if [ -n "$projdir" ]; then
cd "$projdir" || die
fi
[ -f composer.json ] || die "$(basename "$(dirname "$(pwd)")"): n'est pas un projet composer"
setx -a templates=ls_files "$MYDIR" "template-*"
for template in "${templates[@]}"; do
destdir="${DESTDIRS[$template]}"
[ -n "$destdir" ] || die "$template: la destination n'est pas configurée"
mode="${MODES[$template]}"
destname="${template#template-}"
tail -n+4 "$MYDIR/$template" >"$destdir/$destname"
[ -n "$mode" ] && chmod "$mode" "$destdir/$destname"
done

View File

@ -0,0 +1,12 @@
# TODO Faire une copie de ce script dans un répertoire de l'application web
# (dans le répertoire _cli/ par défaut) et modifier les paramètres si nécessaire
#-------------------------------------------------------------------------------
<?php
require __DIR__ . '/../vendor/autoload.php';
# Lancer une application en ligne de commande
const NULIB_APP_app_params = [
"projdir" => __DIR__ . '/..',
"appcode" => \app\config\bootstrap::APPCODE,
];
require __DIR__.'/../vendor/nulib/php/php/src/app/cli/include-launcher.php';

View File

@ -0,0 +1,18 @@
# TODO Faire une copie de ce script dans un répertoire de l'application web
# (dans le répertoire sbin/ par défaut) et modifier les paramètres si nécessaire
#-------------------------------------------------------------------------------
<?php
require __DIR__.'/../vendor/autoload.php';
# Lancer une application en tâche de fond
use nulib\app;
use nulib\tools\BgLauncherApp;
# chemin vers le lanceur PHP
const NULIB_APP_app_launcher = __DIR__.'/../_cli/.launcher.php';
app::init([
"projdir" => __DIR__.'/..',
"appcode" => \app\config\bootstrap::APPCODE,
]);
BgLauncherApp::run();

View File

@ -0,0 +1,132 @@
# TODO Faire une copie de ce script dans un répertoire de l'application web
# (dans le répertoire _cli/ par défaut) et modifier les paramétres si nécessaire
#-------------------------------------------------------------------------------
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# S'assurer que le script PHP est lancé avec l'utilisateur www-data
# Tous les chemins suivants sont relatifs au répertoire qui contient ce script
# Chemin relatif de la racine du projet
PROJPATH=..
# Chemin relatif vers le lanceur PHP
LAUNCHERPATH=.launcher.php
# Chemin relatif des scripts PHP wrappés
WRAPPEDPATH=
# Nom du service dans docker-compose.yml utilisé pour lancer les commandes
COMPOSE_SERVICE=web
###############################################################################
case "${RUNPHP_MODE:-auto}" in
auto)
RUNPHP_MODE=
[ -f /.dockerenv ] && RUNPHP_MODE=docker
[ -z "$RUNPHP_MODE" ] &&
[ -f /proc/self/mountinfo ] &&
grep -q ' /docker/' /proc/self/mountinfo &&
RUNPHP_MODE=docker
[ -z "$RUNPHP_MODE" ] &&
[ -f /proc/1/cgroup ] &&
grep -q ':/docker/' /proc/1/cgroup &&
RUNPHP_MODE=docker
[ -n "$RUNPHP_MODE" ] || RUNPHP_MODE=host
;;
docker) RUNPHP_MODE=docker;;
host) RUNPHP_MODE=host;;
direct) RUNPHP_MODE=direct;;
*) RUNPHP_MODE=host;;
esac
MYDIR="$(dirname -- "$0")"
MYNAME="$(basename -- "$0")"
if [ ! -L "$0" ]; then
echo "\
$0
Ce script doit être lancé en tant que lien symbolique avec un nom de la forme
'monscript.php' et lance le script PHP du même nom situé dans le même répertoire
avec l'utilisateur www-data"
if [ "$RUNPHP_MODE" == host -o "$RUNPHP_MODE" == direct ]; then
echo "\
----------------------------------------
Vérification des liens..."
cd "$MYDIR"
for i in *.php*; do
[ -f "$i" ] || continue
name="bin/${i%.*}.php"
dest="../_cli/_wrapper.sh"
link="../bin/${i%.*}.php"
if [ -L "$link" ]; then
echo "* $name OK"
elif [ -e "$link" ]; then
echo "* $name KO (not a link)"
else
echo "* $name NEW"
ln -s "$dest" "$link" || exit 1
fi
done
fi
exit 0
fi
MYTRUESELF="$(readlink -f "$0")"
MYTRUEDIR="$(dirname -- "$MYTRUESELF")"
PROJDIR="$(cd "$MYTRUEDIR${PROJPATH:+/$PROJPATH}"; pwd)"
if [ "$RUNPHP_MODE" == host ]; then
args=(
docker compose run
${RUNPHP_BUILD:+--build}
--rm
)
cwd="$(pwd)"
mounted=
if [ "$PROJDIR" == "$HOME" -o "${PROJDIR#$HOME/}" != "$PROJDIR" ]; then
# monter HOME
args+=(-v "$HOME:$HOME")
[ "${cwd#$HOME/}" != "$cwd" ] && mounted=1
else
# monter uniquement le répertoire du projet
args+=(-v "$PROJDIR:$PROJDIR")
[ "${cwd#$PROJDIR/}" != "$cwd" ] && mounted=1
fi
if [ -z "$mounted" ]; then
echo "Impossible de mapper le répertoire courant avec les montages du container"
exit 1
fi
args+=(
--workdir "$cwd"
"$COMPOSE_SERVICE"
exec "$MYNAME"
"$@"
)
cd "$PROJDIR"
exec "${args[@]}"
fi
launcher="$MYTRUEDIR/$LAUNCHERPATH"
class="$MYTRUEDIR${WRAPPEDPATH:+/$WRAPPEDPATH}/${MYNAME%.php}.phpc"
script="$MYTRUEDIR${WRAPPEDPATH:+/$WRAPPEDPATH}/${MYNAME%.php}.php"
[ -f /g/init.env ] && source /g/init.env
www_data="${DEVUSER_USERENT%%:*}"
[ -n "$www_data" ] || www_data=www-data
cmd=(php "$launcher")
[ -n "$MEMPROF_PROFILE" ] && cmd+=(-dextension=memprof.so)
if [ -f "$class" ]; then
cmd+=("$(<"$class")")
else
cmd+=("$script")
fi
cmd+=("$@")
if [ "$(id -u)" -eq 0 ]; then
su-exec "$www_data" "${cmd[@]}"
else
exec "${cmd[@]}"
fi